Updated to sqlmap 0.7 release candidate 1

This commit is contained in:
Bernardo Damele 2009-04-22 11:48:07 +00:00
parent b997df740a
commit 8c0ac767f4
129 changed files with 8386 additions and 1388 deletions

View file

@ -5,8 +5,8 @@ $Id$
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2006-2009 Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
Copyright (c) 2007-2009 Bernardo Damele A. G. <bernardo.damele@gmail.com>
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free

View file

@ -5,8 +5,8 @@ $Id$
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2006-2009 Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
Copyright (c) 2007-2009 Bernardo Damele A. G. <bernardo.damele@gmail.com>
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free

View file

@ -5,8 +5,8 @@ $Id$
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2006-2009 Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
Copyright (c) 2007-2009 Bernardo Damele A. G. <bernardo.damele@gmail.com>
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
@ -29,14 +29,10 @@ import re
from lib.core.convert import md5hash
from lib.core.data import conf
from lib.core.data import logger
MATCH_RATIO = None
from lib.core.session import setMatchRatio
def comparison(page, headers=None, getSeqMatcher=False):
global MATCH_RATIO
regExpResults = None
# String to be excluded before calculating page hash
@ -78,13 +74,16 @@ def comparison(page, headers=None, getSeqMatcher=False):
# If the url is stable and we did not set yet the match ratio and the
# current injected value changes the url page content
if MATCH_RATIO == None:
if conf.md5hash != None and ratio < 1 and ratio > 0.6:
if conf.matchRatio == None:
if conf.md5hash != None and ratio > 0.6 and ratio < 1:
logger.debug("setting match ratio to %.3f" % ratio)
MATCH_RATIO = ratio
conf.matchRatio = ratio
elif conf.md5hash == None or ( conf.md5hash != None and ratio < 0.6 ):
logger.debug("setting match ratio to default value 0.900")
MATCH_RATIO = 0.900
conf.matchRatio = 0.900
if conf.matchRatio != None:
setMatchRatio()
# If it has been requested to return the ratio and not a comparison
# response
@ -100,7 +99,7 @@ def comparison(page, headers=None, getSeqMatcher=False):
# If the url is not stable it returns sequence matcher between the
# first untouched HTTP response page content and this content
elif ratio > MATCH_RATIO:
elif ratio > conf.matchRatio:
return True
else:
return False

View file

@ -5,8 +5,8 @@ $Id$
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2006-2009 Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
Copyright (c) 2007-2009 Bernardo Damele A. G. <bernardo.damele@gmail.com>
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
@ -38,7 +38,6 @@ from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.exception import sqlmapConnectionException
from lib.core.settings import RETRIES
from lib.request.basic import forgeHeaders
from lib.request.basic import parseResponse
from lib.request.comparison import comparison
@ -72,6 +71,7 @@ class Connect:
ua = kwargs.get('ua', None)
direct = kwargs.get('direct', False)
multipart = kwargs.get('multipart', False)
silent = kwargs.get('silent', False)
page = ""
cookieStr = ""
@ -128,7 +128,7 @@ class Connect:
conn = urllib2.urlopen(req)
# Reset the number of connection retries
conf.retries = 0
conf.retriesCount = 0
if not req.has_header("Accept-Encoding"):
requestHeaders += "\nAccept-Encoding: identity"
@ -199,8 +199,11 @@ class Connect:
return None, None
if conf.retries < RETRIES:
conf.retries += 1
if silent == True:
return None, None
elif conf.retriesCount < conf.retries:
conf.retriesCount += 1
warnMsg += ", sqlmap is going to retry the request"
logger.warn(warnMsg)
@ -226,7 +229,7 @@ class Connect:
@staticmethod
def queryPage(value=None, place=None, content=False, getSeqMatcher=False):
def queryPage(value=None, place=None, content=False, getSeqMatcher=False, silent=False):
"""
This method calls a function to get the target url page content
and returns its page MD5 hash or a boolean value in case of
@ -265,7 +268,7 @@ class Connect:
else:
ua = conf.parameters["User-Agent"]
page, headers = Connect.getPage(get=get, post=post, cookie=cookie, ua=ua)
page, headers = Connect.getPage(get=get, post=post, cookie=cookie, ua=ua, silent=silent)
if content:
return page, headers

View file

@ -5,8 +5,8 @@ $Id$
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2006-2009 Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
Copyright (c) 2007-2009 Bernardo Damele A. G. <bernardo.damele@gmail.com>
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
@ -39,7 +39,6 @@ from lib.core.data import kb
from lib.core.data import logger
from lib.core.data import queries
from lib.core.data import temp
from lib.core.settings import SECONDS
from lib.request.connect import Connect as Request
from lib.techniques.inband.union.use import unionUse
from lib.techniques.blind.inference import bisection
@ -47,7 +46,7 @@ from lib.utils.resume import queryOutputLength
from lib.utils.resume import resume
def __goInference(payload, expression):
def __goInference(payload, expression, charsetType=None):
start = time.time()
if ( conf.eta or conf.threads > 1 ) and kb.dbms:
@ -57,20 +56,20 @@ def __goInference(payload, expression):
dataToSessionFile("[%s][%s][%s][%s][" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], expression))
count, value = bisection(payload, expression, length=length)
count, value = bisection(payload, expression, length, charsetType)
duration = int(time.time() - start)
if conf.eta and length:
infoMsg = "retrieved: %s" % value
logger.info(infoMsg)
infoMsg = "performed %d queries in %d seconds" % (count, duration)
logger.info(infoMsg)
debugMsg = "performed %d queries in %d seconds" % (count, duration)
logger.debug(debugMsg)
return value
def __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, expected=None, num=None):
def __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, expected=None, num=None, resumeValue=True, charsetType=None):
outputs = []
origExpr = None
@ -89,7 +88,8 @@ def __goInferenceFields(expression, expressionFields, expressionFieldsList, payl
else:
expressionReplaced = expression.replace(expressionFields, field, 1)
output = resume(expressionReplaced, payload)
if resumeValue == True:
output = resume(expressionReplaced, payload)
if not output or ( expected == "int" and not output.isdigit() ):
if output:
@ -97,7 +97,7 @@ def __goInferenceFields(expression, expressionFields, expressionFieldsList, payl
warnMsg += "sqlmap is going to retrieve the value again"
logger.warn(warnMsg)
output = __goInference(payload, expressionReplaced)
output = __goInference(payload, expressionReplaced, charsetType)
if isinstance(num, int):
expression = origExpr
@ -107,7 +107,7 @@ def __goInferenceFields(expression, expressionFields, expressionFieldsList, payl
return outputs
def __goInferenceProxy(expression, fromUser=False, expected=None):
def __goInferenceProxy(expression, fromUser=False, expected=None, batch=False, resumeValue=True, unpack=True, charsetType=None):
"""
Retrieve the output of a SQL query characted by character taking
advantage of an blind SQL injection vulnerability on the affected
@ -125,13 +125,19 @@ def __goInferenceProxy(expression, fromUser=False, expected=None):
untilLimitChar = None
untilOrderChar = None
output = resume(expression, payload)
if resumeValue == True:
output = resume(expression, payload)
else:
output = None
if output and ( expected == None or ( expected == "int" and output.isdigit() ) ):
return output
if unpack == False:
return __goInference(payload, expression, charsetType)
if kb.dbmsDetected:
_, _, _, _, expressionFieldsList, expressionFields = agent.getFields(expression)
_, _, _, _, _, expressionFieldsList, expressionFields = agent.getFields(expression)
if len(expressionFieldsList) > 1:
infoMsg = "the SQL query provided has more than a field. "
@ -200,6 +206,8 @@ def __goInferenceProxy(expression, fromUser=False, expected=None):
if not stopLimit or stopLimit <= 1:
if kb.dbms == "Oracle" and expression.endswith("FROM DUAL"):
test = "n"
elif batch == True:
test = "y"
else:
message = "can the SQL query provided return "
message += "multiple entries? [Y/n] "
@ -214,54 +222,58 @@ def __goInferenceProxy(expression, fromUser=False, expected=None):
untilOrderChar = countedExpression.index(" ORDER BY ")
countedExpression = countedExpression[:untilOrderChar]
count = resume(countedExpression, payload)
if resumeValue == True:
count = resume(countedExpression, payload)
if not stopLimit:
if not count or not count.isdigit():
count = __goInference(payload, countedExpression)
count = __goInference(payload, countedExpression, charsetType)
if count and count.isdigit() and int(count) > 0:
count = int(count)
message = "the SQL query provided can return "
message += "up to %d entries. How many " % count
message += "entries do you want to retrieve?\n"
message += "[a] All (default)\n[#] Specific number\n"
message += "[q] Quit\nChoice: "
test = readInput(message, default="a")
if not test or test[0] in ("a", "A"):
if batch == True:
stopLimit = count
else:
message = "the SQL query provided can return "
message += "up to %d entries. How many " % count
message += "entries do you want to retrieve?\n"
message += "[a] All (default)\n[#] Specific number\n"
message += "[q] Quit"
test = readInput(message, default="a")
elif test[0] in ("q", "Q"):
return "Quit"
if not test or test[0] in ("a", "A"):
stopLimit = count
elif test.isdigit() and int(test) > 0 and int(test) <= count:
stopLimit = int(test)
elif test[0] in ("q", "Q"):
return "Quit"
infoMsg = "sqlmap is now going to retrieve the "
infoMsg += "first %d query output entries" % stopLimit
logger.info(infoMsg)
elif test.isdigit() and int(test) > 0 and int(test) <= count:
stopLimit = int(test)
elif test[0] in ("#", "s", "S"):
message = "How many? "
stopLimit = readInput(message, default="10")
infoMsg = "sqlmap is now going to retrieve the "
infoMsg += "first %d query output entries" % stopLimit
logger.info(infoMsg)
if not stopLimit.isdigit():
elif test[0] in ("#", "s", "S"):
message = "How many? "
stopLimit = readInput(message, default="10")
if not stopLimit.isdigit():
errMsg = "Invalid choice"
logger.error(errMsg)
return None
else:
stopLimit = int(stopLimit)
else:
errMsg = "Invalid choice"
logger.error(errMsg)
return None
else:
stopLimit = int(stopLimit)
else:
errMsg = "Invalid choice"
logger.error(errMsg)
return None
elif count and not count.isdigit():
warnMsg = "it was not possible to count the number "
warnMsg += "of entries for the SQL query provided. "
@ -286,7 +298,7 @@ def __goInferenceProxy(expression, fromUser=False, expected=None):
return None
for num in xrange(startLimit, stopLimit):
output = __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, expected, num)
output = __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, expected, num, resumeValue=resumeValue, charsetType=charsetType)
outputs.append(output)
return outputs
@ -294,17 +306,17 @@ def __goInferenceProxy(expression, fromUser=False, expected=None):
elif kb.dbms == "Oracle" and expression.startswith("SELECT ") and " FROM " not in expression:
expression = "%s FROM DUAL" % expression
outputs = __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, expected)
outputs = __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, expected, resumeValue=resumeValue, charsetType=charsetType)
returnValue = ", ".join([output for output in outputs])
else:
returnValue = __goInference(payload, expression)
returnValue = __goInference(payload, expression, charsetType)
return returnValue
def __goInband(expression, expected=None):
def __goInband(expression, expected=None, sort=True, resumeValue=True, unpack=True):
"""
Retrieve the output of a SQL query taking advantage of an inband SQL
injection vulnerability on the affected parameter.
@ -319,22 +331,22 @@ def __goInband(expression, expected=None):
and expression in kb.resumedQueries[conf.url].keys()
)
if condition:
if condition and resumeValue == True:
output = resume(expression, None)
if not output or ( expected == "int" and not output.isdigit() ):
partial = True
if not output:
output = unionUse(expression, resetCounter=True)
output = unionUse(expression, resetCounter=True, unpack=unpack)
if output:
data = parseUnionPage(output, expression, partial, condition)
data = parseUnionPage(output, expression, partial, condition, sort)
return data
def getValue(expression, blind=True, inband=True, fromUser=False, expected=None):
def getValue(expression, blind=True, inband=True, fromUser=False, expected=None, batch=False, unpack=True, sort=True, resumeValue=True, charsetType=None):
"""
Called each time sqlmap inject a SQL query on the SQL injection
affected parameter. It can call a function to retrieve the output
@ -346,11 +358,11 @@ def getValue(expression, blind=True, inband=True, fromUser=False, expected=None)
expression = expandAsteriskForColumns(expression)
value = None
if inband and conf.unionUse and kb.dbms:
if inband and kb.unionPosition:
if kb.dbms == "Oracle" and " ORDER BY " in expression:
expression = expression[:expression.index(" ORDER BY ")]
value = __goInband(expression, expected)
value = __goInband(expression, expected, sort, resumeValue, unpack)
if not value:
warnMsg = "for some reasons it was not possible to retrieve "
@ -358,25 +370,30 @@ def getValue(expression, blind=True, inband=True, fromUser=False, expected=None)
warnMsg += "technique, sqlmap is going blind"
logger.warn(warnMsg)
conf.paramNegative = False
oldParamFalseCond = conf.paramFalseCond
oldParamNegative = conf.paramNegative
conf.paramFalseCond = False
conf.paramNegative = False
if blind and not value:
value = __goInferenceProxy(expression, fromUser, expected)
value = __goInferenceProxy(expression, fromUser, expected, batch, resumeValue, unpack, charsetType)
conf.paramFalseCond = oldParamFalseCond
conf.paramNegative = oldParamNegative
return value
def goStacked(expression):
"""
TODO: write description
"""
def goStacked(expression, silent=False):
expression = cleanQuery(expression)
debugMsg = "query: %s" % expression
logger.debug(debugMsg)
comment = queries[kb.dbms].comment
query = agent.prefixQuery("; %s" % expression)
query = agent.postfixQuery("%s;%s" % (query, comment))
payload = agent.payload(newValue=query)
page, _ = Request.queryPage(payload, content=True)
page, _ = Request.queryPage(payload, content=True, silent=silent)
return payload, page

View file

@ -5,8 +5,8 @@ $Id$
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2006-2009 Bernardo Damele A. G. <bernardo.damele@gmail.com>
and Daniele Bellucci <daniele.bellucci@gmail.com>
Copyright (c) 2007-2009 Bernardo Damele A. G. <bernardo.damele@gmail.com>
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free