Code refactoring and minor bug fixes.

This commit is contained in:
Bernardo Damele 2010-05-27 16:45:09 +00:00
parent c431a74d9e
commit 9de1671b8f
3 changed files with 106 additions and 57 deletions

View File

@ -57,6 +57,7 @@ from lib.core.exception import sqlmapFilePathException
from lib.core.exception import sqlmapNoneDataException from lib.core.exception import sqlmapNoneDataException
from lib.core.exception import sqlmapMissingDependence from lib.core.exception import sqlmapMissingDependence
from lib.core.exception import sqlmapSyntaxException from lib.core.exception import sqlmapSyntaxException
from lib.core.optiondict import optDict
from lib.core.settings import DESCRIPTION from lib.core.settings import DESCRIPTION
from lib.core.settings import IS_WIN from lib.core.settings import IS_WIN
from lib.core.settings import PLATFORM from lib.core.settings import PLATFORM
@ -417,7 +418,7 @@ def fileToStr(fileName):
@rtype: C{str} @rtype: C{str}
""" """
filePointer = codecs.open(fileName, "r", conf.dataEncoding) filePointer = codecs.open(fileName, "rb", conf.dataEncoding)
fileText = filePointer.read() fileText = filePointer.read()
return fileText.replace(" ", "").replace("\t", "").replace("\r", "").replace("\n", " ") return fileText.replace(" ", "").replace("\t", "").replace("\r", "").replace("\n", " ")
@ -1106,7 +1107,8 @@ def profile(profileOutputFile=None, dotOutputFile=None, imageOutputFile=None):
import gtk import gtk
import pydot import pydot
except ImportError, e: except ImportError, e:
logger.error(e) errMsg = "profiling requires third-party libraries (%s)" % str(e)
logger.error(errMsg)
return return
if profileOutputFile is None: if profileOutputFile is None:
@ -1209,6 +1211,9 @@ def initCommonOutputs():
for line in cfile.xreadlines(): for line in cfile.xreadlines():
line = line.strip() line = line.strip()
if line.startswith('#'):
continue
if len(line) > 1: if len(line) > 1:
if line[0] == '[' and line[-1] == ']': if line[0] == '[' and line[-1] == ']':
key = line[1:-1] key = line[1:-1]
@ -1220,20 +1225,27 @@ def initCommonOutputs():
cfile.close() cfile.close()
def getGoodSamaritanParameters(part, prevValue, originalCharset): def goGoodSamaritan(part, prevValue, originalCharset):
""" """
Function for retrieving parameters needed for good samaritan (common outputs) feature. Function for retrieving parameters needed for common prediction (good
Returns singleValue if there is a complete single match (in part of common-outputs.txt set by parameter 'part') samaritan) feature.
regarding parameter prevValue. If there is no single value match, but multiple, predictedCharset is returned
containing more probable characters (retrieved from matched items in common-outputs.txt) together with the part is for instance Users, Databases, Tables and corresponds to the
rest of charset as otherCharset header (e.g. [Users]) in txt/common-outputs.txt.
prevValue: retrieved query output so far (e.g. 'i').
Returns singleValue if there is a complete single match (in part of
txt/common-outputs.txt under 'part') regarding parameter prevValue. If
there is no single value match, but multiple, commonCharset is
returned containing more probable characters (retrieved from matched
values in txt/common-outputs.txt) together with the rest of charset as
otherCharset.
""" """
if kb.commonOutputs is None: if kb.commonOutputs is None:
initCommonOutputs() initCommonOutputs()
if not part or not prevValue: #is not None and != ""
return None, None, originalCharset
predictionSet = set() predictionSet = set()
wildIndexes = [] wildIndexes = []
singleValue = None singleValue = None
@ -1249,29 +1261,34 @@ def getGoodSamaritanParameters(part, prevValue, originalCharset):
charIndex += 1 charIndex += 1
findIndex = prevValue.find('.', charIndex) findIndex = prevValue.find('.', charIndex)
# If the header we are looking for has common outputs defined
if part in kb.commonOutputs: if part in kb.commonOutputs:
for item in kb.commonOutputs[part]: for item in kb.commonOutputs[part]:
# Check if the common output (item) starts with prevValue
if re.search('\A%s' % prevValue, item): if re.search('\A%s' % prevValue, item):
singleValue = item singleValue = item
for index in wildIndexes: for index in wildIndexes:
char = item[index] char = item[index]
if char not in predictionSet: if char not in predictionSet:
predictionSet.add(char) predictionSet.add(char)
predictedCharset = [] commonCharset = []
otherCharset = [] otherCharset = []
# Split the original charset into common chars (commonCharset)
# and other chars (otherCharset)
for ordChar in originalCharset: for ordChar in originalCharset:
if chr(ordChar) not in predictionSet: if chr(ordChar) not in predictionSet:
otherCharset.append(ordChar) otherCharset.append(ordChar)
else: else:
predictedCharset.append(ordChar) commonCharset.append(ordChar)
predictedCharset.sort() commonCharset.sort()
if len(predictedCharset) > 1: if len(commonCharset) > 1:
return None, predictedCharset, otherCharset return None, commonCharset, otherCharset
else: else:
return singleValue, None, originalCharset return singleValue, None, originalCharset
else: else:
@ -1279,8 +1296,12 @@ def getGoodSamaritanParameters(part, prevValue, originalCharset):
def getCompiledRegex(regex, *args): def getCompiledRegex(regex, *args):
""" """
Returns compiled regular expression and stores it in cache for further usage Returns compiled regular expression and stores it in cache for further
usage
""" """
global __compiledRegularExpressions
if (regex, args) in __compiledRegularExpressions: if (regex, args) in __compiledRegularExpressions:
return __compiledRegularExpressions[(regex, args)] return __compiledRegularExpressions[(regex, args)]
else: else:
@ -1290,15 +1311,23 @@ def getCompiledRegex(regex, *args):
def getPartRun(): def getPartRun():
""" """
Goes through call stack and finds constructs matching conf.dmbsHandler.*. Returns it or it's alias used in common-outputs.txt Goes through call stack and finds constructs matching conf.dmbsHandler.*.
Returns it or its alias used in txt/common-outputs.txt
""" """
commonPartsDict = { "getTables":"Tables", "getColumns":"Columns", "getUsers":"Users", "getBanner":"Banners", "getDbs":"Databases" }
retVal = None retVal = None
commonPartsDict = optDict["Enumeration"]
stack = [item[4][0] if isinstance(item[4], list) else '' for item in inspect.stack()] stack = [item[4][0] if isinstance(item[4], list) else '' for item in inspect.stack()]
reobj = getCompiledRegex('conf\.dbmsHandler\.([^(]+)\(\)') reobj = getCompiledRegex('conf\.dbmsHandler\.([^(]+)\(\)')
# Goes backwards through the stack to find the conf.dbmsHandler method
# calling this function
for i in xrange(len(stack) - 1, 0, -1): for i in xrange(len(stack) - 1, 0, -1):
match = reobj.search(stack[i]) match = reobj.search(stack[i])
if match: if match:
# This is the calling conf.dbmsHandler method (e.g. 'getDbms')
retVal = match.groups()[0] retVal = match.groups()[0]
break break
return commonPartsDict[retVal] if retVal in commonPartsDict else retVal
return commonPartsDict[retVal][1] if retVal in commonPartsDict else retVal

View File

@ -23,7 +23,10 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
""" """
optDict = { optDict = {
# Family: { "parameter_name": "parameter_datatype" }, # Format:
# Family: { "parameter name": "parameter datatype" },
# Or:
# Family: { "parameter name": ("parameter datatype", "category name used for common outputs feature") },
"Target": { "Target": {
"direct": "string", "direct": "string",
"url": "string", "url": "string",
@ -84,17 +87,17 @@ optDict = {
}, },
"Enumeration": { "Enumeration": {
"getBanner": "boolean", "getBanner": ("boolean", "Banners"),
"getCurrentUser": "boolean", "getCurrentUser": ("boolean", "Users"),
"getCurrentDb": "boolean", "getCurrentDb": ("boolean", "Databases"),
"isDba": "boolean", "isDba": "boolean",
"getUsers": "boolean", "getUsers": ("boolean", "Users"),
"getPasswordHashes": "boolean", "getPasswordHashes": ("boolean", "Hashes"),
"getPrivileges": "boolean", "getPrivileges": ("boolean", "Privileges"),
"getRoles": "boolean", "getRoles": ("boolean", "Roles"),
"getDbs": "boolean", "getDbs": ("boolean", "Databases"),
"getTables": "boolean", "getTables": ("boolean", "Tables"),
"getColumns": "boolean", "getColumns": ("boolean", "Columns"),
"dumpTable": "boolean", "dumpTable": "boolean",
"dumpAll": "boolean", "dumpAll": "boolean",
"search": "boolean", "search": "boolean",
@ -107,6 +110,8 @@ optDict = {
"limitStop": "integer", "limitStop": "integer",
"firstChar": "integer", "firstChar": "integer",
"lastChar": "integer", "lastChar": "integer",
"getNumOfTables": "integer",
"getNumOfDBs": "integer",
"query": "string", "query": "string",
"sqlShell": "boolean" "sqlShell": "boolean"
}, },
@ -144,6 +149,7 @@ optDict = {
}, },
"Miscellaneous": { "Miscellaneous": {
"xmlFile": "string",
"sessionFile": "string", "sessionFile": "string",
"flushSession": "boolean", "flushSession": "boolean",
"eta": "boolean", "eta": "boolean",

View File

@ -30,7 +30,7 @@ from lib.core.agent import agent
from lib.core.common import dataToSessionFile from lib.core.common import dataToSessionFile
from lib.core.common import dataToStdout from lib.core.common import dataToStdout
from lib.core.common import getCharset from lib.core.common import getCharset
from lib.core.common import getGoodSamaritanParameters from lib.core.common import goGoodSamaritan
from lib.core.common import getPartRun from lib.core.common import getPartRun
from lib.core.common import replaceNewlineTabs from lib.core.common import replaceNewlineTabs
from lib.core.common import safeStringFormat from lib.core.common import safeStringFormat
@ -54,10 +54,11 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
partialValue = "" partialValue = ""
finalValue = "" finalValue = ""
asciiTbl = getCharset(charsetType) asciiTbl = getCharset(charsetType)
kb.partRun = getPartRun() if conf.useCommonPrediction else None #set kb.partRun in case common-prediction used # Set kb.partRun in case "common prediction" feature (a.k.a. "good
# samaritan") is used
kb.partRun = getPartRun() if conf.useCommonPrediction else None
if "LENGTH(" in expression or "LEN(" in expression: if "LENGTH(" in expression or "LEN(" in expression:
firstChar = 0 firstChar = 0
@ -117,7 +118,6 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
dataToStdout("[%s] [INFO] retrieved: " % time.strftime("%X")) dataToStdout("[%s] [INFO] retrieved: " % time.strftime("%X"))
queriesCount = [0] # As list to deal with nested scoping rules queriesCount = [0] # As list to deal with nested scoping rules
hintlock = threading.Lock() hintlock = threading.Lock()
def tryHint(idx): def tryHint(idx):
@ -131,8 +131,8 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
else: else:
posValue = ord(hintValue[idx-1]) posValue = ord(hintValue[idx-1])
queriesCount[0] += 1
forgedPayload = safeStringFormat(payload.replace('%3E', '%3D'), (expressionUnescaped, idx, posValue)) forgedPayload = safeStringFormat(payload.replace('%3E', '%3D'), (expressionUnescaped, idx, posValue))
queriesCount[0] += 1
result = Request.queryPage(urlencode(forgedPayload)) result = Request.queryPage(urlencode(forgedPayload))
if result: if result:
@ -155,6 +155,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
if len(charTbl) == 1: if len(charTbl) == 1:
forgedPayload = safeStringFormat(payload.replace('%3E', '%3D'), (expressionUnescaped, idx, charTbl[0])) forgedPayload = safeStringFormat(payload.replace('%3E', '%3D'), (expressionUnescaped, idx, charTbl[0]))
queriesCount[0] += 1
result = Request.queryPage(urlencode(forgedPayload)) result = Request.queryPage(urlencode(forgedPayload))
if result: if result:
return chr(charTbl[0]) if charTbl[0] < 128 else unichr(charTbl[0]) return chr(charTbl[0]) if charTbl[0] < 128 else unichr(charTbl[0])
@ -165,7 +166,6 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
minValue = charTbl[0] minValue = charTbl[0]
while len(charTbl) != 1: while len(charTbl) != 1:
queriesCount[0] += 1
position = (len(charTbl) >> 1) position = (len(charTbl) >> 1)
posValue = charTbl[position] posValue = charTbl[position]
@ -181,6 +181,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
else: else:
forgedPayload = safeStringFormat(payload.replace('%3E', 'NOT BETWEEN 0 AND'), (expressionUnescaped, idx, posValue)) forgedPayload = safeStringFormat(payload.replace('%3E', 'NOT BETWEEN 0 AND'), (expressionUnescaped, idx, posValue))
queriesCount[0] += 1
result = Request.queryPage(urlencode(forgedPayload)) result = Request.queryPage(urlencode(forgedPayload))
if kb.dbms == "SQLite": if kb.dbms == "SQLite":
@ -344,6 +345,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
raise raise
infoMsg = None infoMsg = None
# If we have got one single character not correctly fetched it # If we have got one single character not correctly fetched it
# can mean that the connection to the target url was lost # can mean that the connection to the target url was lost
if None in value: if None in value:
@ -372,29 +374,41 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
index += 1 index += 1
charStart = time.time() charStart = time.time()
#common prediction (a.k.a. good samaritan) # Common prediction feature (a.k.a. "good samaritan")
if conf.useCommonPrediction: # NOTE: to be used only when multi-threading is not set for
singleValue, predictedCharset, otherCharset = getGoodSamaritanParameters(kb.partRun, finalValue, asciiTbl) # the moment
if conf.useCommonPrediction and len(finalValue) > 0 and kb.partRun is not None:
val = None val = None
singleValue, commonCharset, otherCharset = goGoodSamaritan(kb.partRun, finalValue, asciiTbl)
#if there is no singleValue (single match from common-outputs.txt) use the returned predictedCharset # If there is no singleValue (single match from
if singleValue is None: # txt/common-outputs.txt) use the returned common
val = getChar(index, predictedCharset, False) if predictedCharset else None # charset only to retrieve the query output
else: if singleValue is not None:
#one shot query containing equals singleValue # One-shot query containing equals singleValue
query = agent.prefixQuery(" %s" % safeStringFormat('AND (%s) = %s', (expressionUnescaped, unescaper.unescape('\'%s\'' % singleValue)))) query = agent.prefixQuery(" %s" % safeStringFormat('AND (%s) = %s', (expressionUnescaped, unescaper.unescape('\'%s\'' % singleValue))))
query = agent.postfixQuery(query) query = agent.postfixQuery(query)
queriesCount[0] += 1
result = Request.queryPage(urlencode(agent.payload(newValue=query))) result = Request.queryPage(urlencode(agent.payload(newValue=query)))
#did we have luck?
# Did we have luck?
if result: if result:
dataToSessionFile(replaceNewlineTabs(singleValue[index-1:])) dataToSessionFile(replaceNewlineTabs(singleValue[index-1:]))
if showEta: if showEta:
etaProgressUpdate(time.time() - charStart, lastChar + 1) etaProgressUpdate(time.time() - charStart, len(singleValue))
elif conf.verbose >= 1: elif conf.verbose >= 1:
dataToStdout(singleValue[index-1:]) dataToStdout(singleValue[index-1:])
finalValue = singleValue finalValue = singleValue
break break
#if we had no luck with singleValue and predictedCharset use the returned otherCharset elif commonCharset:
# TODO: this part does not seem to work yet
val = getChar(index, commonCharset, False)
# If we had no luck with singleValue and common charset,
# use the returned other charset
if not val: if not val:
val = getChar(index, otherCharset) val = getChar(index, otherCharset)
else: else: