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 sqlmapMissingDependence
from lib.core.exception import sqlmapSyntaxException
from lib.core.optiondict import optDict
from lib.core.settings import DESCRIPTION
from lib.core.settings import IS_WIN
from lib.core.settings import PLATFORM
@ -417,7 +418,7 @@ def fileToStr(fileName):
@rtype: C{str}
"""
filePointer = codecs.open(fileName, "r", conf.dataEncoding)
filePointer = codecs.open(fileName, "rb", conf.dataEncoding)
fileText = filePointer.read()
return fileText.replace(" ", "").replace("\t", "").replace("\r", "").replace("\n", " ")
@ -1106,7 +1107,8 @@ def profile(profileOutputFile=None, dotOutputFile=None, imageOutputFile=None):
import gtk
import pydot
except ImportError, e:
logger.error(e)
errMsg = "profiling requires third-party libraries (%s)" % str(e)
logger.error(errMsg)
return
if profileOutputFile is None:
@ -1209,6 +1211,9 @@ def initCommonOutputs():
for line in cfile.xreadlines():
line = line.strip()
if line.startswith('#'):
continue
if len(line) > 1:
if line[0] == '[' and line[-1] == ']':
key = line[1:-1]
@ -1220,20 +1225,27 @@ def initCommonOutputs():
cfile.close()
def getGoodSamaritanParameters(part, prevValue, originalCharset):
def goGoodSamaritan(part, prevValue, originalCharset):
"""
Function for retrieving parameters needed for good samaritan (common outputs) feature.
Returns singleValue if there is a complete single match (in part of common-outputs.txt set by parameter 'part')
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
rest of charset as otherCharset
Function for retrieving parameters needed for common prediction (good
samaritan) feature.
part is for instance Users, Databases, Tables and corresponds to the
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:
initCommonOutputs()
if not part or not prevValue: #is not None and != ""
return None, None, originalCharset
predictionSet = set()
wildIndexes = []
singleValue = None
@ -1249,29 +1261,34 @@ def getGoodSamaritanParameters(part, prevValue, originalCharset):
charIndex += 1
findIndex = prevValue.find('.', charIndex)
# If the header we are looking for has common outputs defined
if part in kb.commonOutputs:
for item in kb.commonOutputs[part]:
# Check if the common output (item) starts with prevValue
if re.search('\A%s' % prevValue, item):
singleValue = item
for index in wildIndexes:
char = item[index]
if char not in predictionSet:
predictionSet.add(char)
predictedCharset = []
commonCharset = []
otherCharset = []
# Split the original charset into common chars (commonCharset)
# and other chars (otherCharset)
for ordChar in originalCharset:
if chr(ordChar) not in predictionSet:
otherCharset.append(ordChar)
else:
predictedCharset.append(ordChar)
commonCharset.append(ordChar)
predictedCharset.sort()
commonCharset.sort()
if len(predictedCharset) > 1:
return None, predictedCharset, otherCharset
if len(commonCharset) > 1:
return None, commonCharset, otherCharset
else:
return singleValue, None, originalCharset
else:
@ -1279,8 +1296,12 @@ def getGoodSamaritanParameters(part, prevValue, originalCharset):
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:
return __compiledRegularExpressions[(regex, args)]
else:
@ -1290,15 +1311,23 @@ def getCompiledRegex(regex, *args):
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
commonPartsDict = optDict["Enumeration"]
stack = [item[4][0] if isinstance(item[4], list) else '' for item in inspect.stack()]
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):
match = reobj.search(stack[i])
if match:
# This is the calling conf.dbmsHandler method (e.g. 'getDbms')
retVal = match.groups()[0]
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 = {
# 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": {
"direct": "string",
"url": "string",
@ -84,17 +87,17 @@ optDict = {
},
"Enumeration": {
"getBanner": "boolean",
"getCurrentUser": "boolean",
"getCurrentDb": "boolean",
"getBanner": ("boolean", "Banners"),
"getCurrentUser": ("boolean", "Users"),
"getCurrentDb": ("boolean", "Databases"),
"isDba": "boolean",
"getUsers": "boolean",
"getPasswordHashes": "boolean",
"getPrivileges": "boolean",
"getRoles": "boolean",
"getDbs": "boolean",
"getTables": "boolean",
"getColumns": "boolean",
"getUsers": ("boolean", "Users"),
"getPasswordHashes": ("boolean", "Hashes"),
"getPrivileges": ("boolean", "Privileges"),
"getRoles": ("boolean", "Roles"),
"getDbs": ("boolean", "Databases"),
"getTables": ("boolean", "Tables"),
"getColumns": ("boolean", "Columns"),
"dumpTable": "boolean",
"dumpAll": "boolean",
"search": "boolean",
@ -107,6 +110,8 @@ optDict = {
"limitStop": "integer",
"firstChar": "integer",
"lastChar": "integer",
"getNumOfTables": "integer",
"getNumOfDBs": "integer",
"query": "string",
"sqlShell": "boolean"
},
@ -144,6 +149,7 @@ optDict = {
},
"Miscellaneous": {
"xmlFile": "string",
"sessionFile": "string",
"flushSession": "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 dataToStdout
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 replaceNewlineTabs
from lib.core.common import safeStringFormat
@ -53,11 +53,12 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
"""
partialValue = ""
finalValue = ""
finalValue = ""
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:
firstChar = 0
@ -116,9 +117,8 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
else:
dataToStdout("[%s] [INFO] retrieved: " % time.strftime("%X"))
queriesCount = [0] # As list to deal with nested scoping rules
hintlock = threading.Lock()
queriesCount = [0] # As list to deal with nested scoping rules
hintlock = threading.Lock()
def tryHint(idx):
hintlock.acquire()
@ -131,9 +131,9 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
else:
posValue = ord(hintValue[idx-1])
queriesCount[0] += 1
forgedPayload = safeStringFormat(payload.replace('%3E', '%3D'), (expressionUnescaped, idx, posValue))
result = Request.queryPage(urlencode(forgedPayload))
queriesCount[0] += 1
result = Request.queryPage(urlencode(forgedPayload))
if result:
return hintValue[idx-1]
@ -155,6 +155,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
if len(charTbl) == 1:
forgedPayload = safeStringFormat(payload.replace('%3E', '%3D'), (expressionUnescaped, idx, charTbl[0]))
queriesCount[0] += 1
result = Request.queryPage(urlencode(forgedPayload))
if result:
return chr(charTbl[0]) if charTbl[0] < 128 else unichr(charTbl[0])
@ -165,9 +166,8 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
minValue = charTbl[0]
while len(charTbl) != 1:
queriesCount[0] += 1
position = (len(charTbl) >> 1)
posValue = charTbl[position]
position = (len(charTbl) >> 1)
posValue = charTbl[position]
if kb.dbms == "SQLite":
posValueOld = posValue
@ -181,7 +181,8 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
else:
forgedPayload = safeStringFormat(payload.replace('%3E', 'NOT BETWEEN 0 AND'), (expressionUnescaped, idx, posValue))
result = Request.queryPage(urlencode(forgedPayload))
queriesCount[0] += 1
result = Request.queryPage(urlencode(forgedPayload))
if kb.dbms == "SQLite":
posValue = posValueOld
@ -249,7 +250,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
if conf.threadContinue:
charStart = time.time()
val = getChar(curidx)
val = getChar(curidx)
if val is None:
raise sqlmapValueException, "failed to get character at index %d (expected %d total)" % (curidx, length)
@ -344,6 +345,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
raise
infoMsg = None
# If we have got one single character not correctly fetched it
# can mean that the connection to the target url was lost
if None in value:
@ -369,32 +371,44 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
index = firstChar
while True:
index += 1
index += 1
charStart = time.time()
#common prediction (a.k.a. good samaritan)
if conf.useCommonPrediction:
singleValue, predictedCharset, otherCharset = getGoodSamaritanParameters(kb.partRun, finalValue, asciiTbl)
# Common prediction feature (a.k.a. "good samaritan")
# NOTE: to be used only when multi-threading is not set for
# the moment
if conf.useCommonPrediction and len(finalValue) > 0 and kb.partRun is not 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 singleValue is None:
val = getChar(index, predictedCharset, False) if predictedCharset else None
else:
#one shot query containing equals singleValue
# If there is no singleValue (single match from
# txt/common-outputs.txt) use the returned common
# charset only to retrieve the query output
if singleValue is not None:
# One-shot query containing equals singleValue
query = agent.prefixQuery(" %s" % safeStringFormat('AND (%s) = %s', (expressionUnescaped, unescaper.unescape('\'%s\'' % singleValue))))
query = agent.postfixQuery(query)
queriesCount[0] += 1
result = Request.queryPage(urlencode(agent.payload(newValue=query)))
#did we have luck?
# Did we have luck?
if result:
dataToSessionFile(replaceNewlineTabs(singleValue[index-1:]))
if showEta:
etaProgressUpdate(time.time() - charStart, lastChar + 1)
etaProgressUpdate(time.time() - charStart, len(singleValue))
elif conf.verbose >= 1:
dataToStdout(singleValue[index-1:])
finalValue = singleValue
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:
val = getChar(index, otherCharset)
else: