First big commit to move UNION query tests to detection phase - there are some improvements and tuning to do yet though.

Major refactoring to Agent.payload() method.
Minor bug fixes, some code refactoring and a lot of core adjustments here and there.
Added more checks for injection in GROUP BY and ORDER BY.
This commit is contained in:
Bernardo Damele
2011-01-11 22:18:47 +00:00
parent 06230e4d92
commit 300128042c
10 changed files with 254 additions and 200 deletions

View File

@@ -53,7 +53,7 @@ class Agent:
return query
def payload(self, place=None, parameter=None, value=None, newValue=None, negative=False):
def payload(self, place=None, parameter=None, value=None, newValue=None, where=None):
"""
This method replaces the affected parameter with the SQL
injection statement to request
@@ -62,59 +62,38 @@ class Agent:
if conf.direct:
return self.payloadDirect(newValue)
falseValue = ""
negValue = ""
retValue = ""
retValue = ""
if negative or kb.unionNegative:
negValue = "-"
if where is None and isTechniqueAvailable(kb.technique):
where = kb.injection.data[kb.technique].where
# After identifing the injectable parameter
if kb.injection.place == PLACE.UA and kb.injection.parameter:
retValue = kb.injection.parameter.replace(kb.injection.parameter,
self.addPayloadDelimiters("%s%s" % (negValue, kb.injection.parameter + falseValue + newValue)))
elif kb.injection.place and kb.injection.parameter:
paramString = conf.parameters[kb.injection.place]
paramDict = conf.paramDict[kb.injection.place]
origValue = paramDict[kb.injection.parameter]
if kb.injection.place is not None:
place = kb.injection.place
if isTechniqueAvailable(kb.technique):
where = kb.injection.data[kb.technique].where
if kb.injection.parameter is not None:
parameter = kb.injection.parameter
if place == PLACE.UA:
retValue = parameter.replace(parameter, self.addPayloadDelimiters(parameter + newValue))
else:
paramString = conf.parameters[place]
paramDict = conf.paramDict[place]
origValue = paramDict[parameter]
if value is None:
if where == 1:
value = origValue
elif where == 2:
value = "-%s" % randomInt()
elif where == 3:
value = ""
else:
value = origValue
else:
value = origValue
newValue = "%s%s" % (value, newValue)
newValue = self.cleanupPayload(newValue, origValue)
if "POSTxml" in conf.paramDict and kb.injection.place == PLACE.POST:
root = ET.XML(paramString)
iterator = root.getiterator(kb.injection.parameter)
for child in iterator:
child.text = self.addPayloadDelimiters(negValue + value + falseValue + newValue)
retValue = ET.tostring(root)
elif kb.injection.place == PLACE.URI:
retValue = paramString.replace("*",
self.addPayloadDelimiters("%s%s" % (negValue, falseValue + newValue)))
else:
retValue = paramString.replace("%s=%s" % (kb.injection.parameter, origValue),
"%s=%s" % (kb.injection.parameter, self.addPayloadDelimiters(negValue + value + falseValue + newValue)))
# Before identifing the injectable parameter
elif parameter == PLACE.UA:
retValue = value.replace(value, self.addPayloadDelimiters(newValue))
elif place == PLACE.URI:
retValue = value.replace("*", self.addPayloadDelimiters("%s" % newValue.replace(value, str())))
else:
paramString = conf.parameters[place]
if "POSTxml" in conf.paramDict and place == PLACE.POST:
root = ET.XML(paramString)
iterator = root.getiterator(parameter)
@@ -123,10 +102,13 @@ class Agent:
child.text = self.addPayloadDelimiters(newValue)
retValue = ET.tostring(root)
elif place == PLACE.URI:
retValue = paramString.replace("*", self.addPayloadDelimiters(newValue))
else:
retValue = paramString.replace("%s=%s" % (parameter, value),
retValue = paramString.replace("%s=%s" % (parameter, origValue),
"%s=%s" % (parameter, self.addPayloadDelimiters(newValue)))
# print "retValue:", retValue
return retValue
def fullPayload(self, query):
@@ -139,7 +121,7 @@ class Agent:
return payload
def prefixQuery(self, string):
def prefixQuery(self, string, prefix=None, where=None, clause=None):
"""
This method defines how the input string has to be escaped
to perform the injection depending on the injection type
@@ -156,9 +138,10 @@ class Agent:
# payload, do not put a space after the prefix
if kb.technique == PAYLOAD.TECHNIQUE.STACKED:
query = kb.injection.prefix
elif kb.injection.clause == [2, 3] or kb.injection.clause == [ 3 ]:
if kb.technique != PAYLOAD.TECHNIQUE.UNION:
query = kb.injection.prefix
elif where == 3 or clause == [2, 3] or clause == [ 2 ] or clause == [ 3 ]:
query = prefix
elif kb.injection.clause == [2, 3] or kb.injection.clause == [ 2 ] or kb.injection.clause == [ 3 ]:
query = kb.injection.prefix
elif kb.technique and kb.technique in kb.injection.data:
where = kb.injection.data[kb.technique].where
@@ -166,14 +149,17 @@ class Agent:
query = kb.injection.prefix
if query is None:
query = "%s " % kb.injection.prefix
if kb.injection.prefix is None and prefix is not None:
query = "%s " % prefix
else:
query = "%s " % kb.injection.prefix
query = "%s%s" % (query, string)
query = self.cleanupPayload(query)
return query
def suffixQuery(self, string, comment=None):
def suffixQuery(self, string, comment=None, suffix=None):
"""
This method appends the DBMS comment to the
SQL injection request
@@ -185,12 +171,16 @@ class Agent:
if comment is not None:
string += comment
string += " %s" % kb.injection.suffix
if kb.injection.suffix is None and suffix is not None:
string += " %s" % suffix
else:
string += " %s" % kb.injection.suffix
string = self.cleanupPayload(string)
return string.rstrip()
def cleanupPayload(self, payload, origvalue=None):
def cleanupPayload(self, payload, origvalue=None, unionVector=None):
if payload is None:
return
@@ -207,11 +197,9 @@ class Agent:
payload = payload.replace("[DELIMITER_STOP]", kb.misc.stop)
payload = payload.replace("[SPACE_REPLACE]", kb.misc.space)
payload = payload.replace("[SLEEPTIME]", str(conf.timeSec))
payload = payload.replace("[UNION]", str(unionVector))
if origvalue is not None:
if not origvalue.isdigit():
origvalue = "'%s'" % origvalue
payload = payload.replace("[ORIGVALUE]", origvalue)
if "[INFERENCE]" in payload:
@@ -228,14 +216,15 @@ class Agent:
payload = payload.replace("[INFERENCE]", inferenceQuery)
elif kb.misc.testedDbms is not None:
elif hasattr(kb.misc, "testedDbms") and kb.misc.testedDbms is not None:
inferenceQuery = queries[kb.misc.testedDbms].inference.query
payload = payload.replace("[INFERENCE]", inferenceQuery)
else:
errMsg = "invalid usage of inference payload without "
errMsg += "knowledge of underlying DBMS"
raise sqlmapNoneDataException, errMsg
# NOTE: Leave this commented for the time being
#else:
# errMsg = "invalid usage of inference payload without "
# errMsg += "knowledge of underlying DBMS"
# raise sqlmapNoneDataException, errMsg
return payload
@@ -483,7 +472,7 @@ class Agent:
return concatenatedQuery
def forgeInbandQuery(self, query, exprPosition=None, nullChar=None, count=None, comment=None, multipleUnions=None):
def forgeInbandQuery(self, query, exprPosition=None, nullChar=None, count=None, comment=None, prefix=None, suffix=None, multipleUnions=None):
"""
Take in input an query (pseudo query) string and return its
processed UNION ALL SELECT query.
@@ -526,7 +515,7 @@ class Agent:
if query.startswith("SELECT "):
query = query[len("SELECT "):]
inbandQuery = self.prefixQuery("UNION ALL SELECT ")
inbandQuery = self.prefixQuery("UNION ALL SELECT ", prefix=prefix)
if query.startswith("TOP"):
topNum = re.search("\ATOP\s+([\d]+)\s+", query, re.I).group(1)
@@ -584,8 +573,7 @@ class Agent:
if kb.dbms == DBMS.ORACLE:
inbandQuery += " FROM DUAL"
inbandQuery = self.suffixQuery(inbandQuery, comment)
inbandQuery = self.suffixQuery(inbandQuery, comment, suffix)
return inbandQuery

View File

@@ -1935,6 +1935,7 @@ def initTechnique(technique=None):
"""
Prepares proper page template and match ratio for technique specified
"""
try:
data = getTechniqueData(technique)
@@ -1945,7 +1946,8 @@ def initTechnique(technique=None):
warnMsg = "there is no injection data available for technique "
warnMsg += "'%s'" % enumValueToNameLookup(PAYLOAD.TECHNIQUE, technique)
logger.warn(warnMsg)
except sqlmapDataException, ex:
except sqlmapDataException, _:
errMsg = "missing data in old session file(s). "
errMsg += "please use '--flush-session' to deal "
errMsg += "with this error"
@@ -2063,3 +2065,35 @@ def openFile(filename, mode='r'):
('w' in mode or 'a' in mode or '+' in mode) else "read")
errMsg += "and that it's not locked by another process."
raise sqlmapFilePathException, errMsg
def configUnion():
if isinstance(conf.uCols, basestring):
debugMsg = "setting the UNION query SQL injection range of columns"
logger.debug(debugMsg)
if "-" not in conf.uCols or len(conf.uCols.split("-")) != 2:
raise sqlmapSyntaxException, "--union-cols must be a range with hyphon (e.g. 1-10)"
conf.uCols = conf.uCols.replace(" ", "")
conf.uColsStart, conf.uColsStop = conf.uCols.split("-")
if not conf.uColsStart.isdigit() or not conf.uColsStop.isdigit():
raise sqlmapSyntaxException, "--union-cols must be a range of integers"
conf.uColsStart = int(conf.uColsStart)
conf.uColsStop = int(conf.uColsStop)
if conf.uColsStart > conf.uColsStop:
errMsg = "--union-cols range has to be from lower to "
errMsg += "higher number of columns"
raise sqlmapSyntaxException, errMsg
if isinstance(conf.uChar, basestring) and conf.uChar != "NULL":
debugMsg = "setting the UNION query SQL injection character to '%s'" % conf.uChar
logger.debug(debugMsg)
if not conf.uChar.isdigit() and ( not conf.uChar.startswith("'") or not conf.uChar.endswith("'") ):
debugMsg = "forcing the UNION query SQL injection character to '%s'" % conf.uChar
logger.debug(debugMsg)
conf.uChar = "'%s'" % conf.uChar

View File

@@ -204,70 +204,18 @@ def setUnion(comment=None, count=None, position=None, negative=False, char=None,
"""
if comment:
condition = (
not kb.resumedQueries or ( kb.resumedQueries.has_key(conf.url) and
not kb.resumedQueries[conf.url].has_key("Union comment") )
)
if condition:
dataToSessionFile("[%s][%s][%s][Union comment][%s]\n" % (conf.url, kb.injection.place, safeFormatString(conf.parameters[kb.injection.place]), safeFormatString(comment)))
kb.unionComment = comment
if count:
condition = (
not kb.resumedQueries or ( kb.resumedQueries.has_key(conf.url) and
not kb.resumedQueries[conf.url].has_key("Union count") )
)
if condition:
dataToSessionFile("[%s][%s][%s][Union count][%d]\n" % (conf.url, kb.injection.place, safeFormatString(conf.parameters[kb.injection.place]), count))
kb.unionCount = count
if position is not None:
condition = (
not kb.resumedQueries or ( kb.resumedQueries.has_key(conf.url) and
not kb.resumedQueries[conf.url].has_key("Union position") )
)
if condition:
dataToSessionFile("[%s][%s][%s][Union position][%s]\n" % (conf.url, kb.injection.place, safeFormatString(conf.parameters[kb.injection.place]), position))
kb.unionPosition = position
if negative:
condition = (
not kb.resumedQueries or ( kb.resumedQueries.has_key(conf.url) and
( not kb.resumedQueries[conf.url].has_key("Union negative")
) )
)
if condition:
dataToSessionFile("[%s][%s][%s][Union negative][Yes]\n" % (conf.url, kb.injection.place, safeFormatString(conf.parameters[kb.injection.place])))
kb.unionNegative = True
if char:
condition = (
not kb.resumedQueries or ( kb.resumedQueries.has_key(conf.url) and
( not kb.resumedQueries[conf.url].has_key("Union char")
) )
)
if condition:
dataToSessionFile("[%s][%s][%s][Union char][%s]\n" % (conf.url, kb.injection.place, safeFormatString(conf.parameters[kb.injection.place]), char))
if payload:
condition = (
not kb.resumedQueries or ( kb.resumedQueries.has_key(conf.url) and
( not kb.resumedQueries[conf.url].has_key("Union payload")
) )
)
if condition:
dataToSessionFile("[%s][%s][%s][Union payload][%s]\n" % (conf.url, kb.injection.place, safeFormatString(conf.parameters[kb.injection.place]), payload))
kb.unionTest = payload
def setRemoteTempPath():