Merge remote-tracking branch 'sqlmapproject/master'

This commit is contained in:
cxh852456 2015-12-29 21:21:50 +08:00
commit b3ca725c13
14 changed files with 203 additions and 40 deletions

View File

@ -86,6 +86,7 @@ from lib.core.exception import SqlmapSilentQuitException
from lib.core.exception import SqlmapSyntaxException
from lib.core.exception import SqlmapSystemException
from lib.core.exception import SqlmapUserQuitException
from lib.core.exception import SqlmapValueException
from lib.core.log import LOGGER_HANDLER
from lib.core.optiondict import optDict
from lib.core.settings import BANNER
@ -1638,7 +1639,9 @@ def safeStringFormat(format_, params):
match = re.search(r"(\A|[^A-Za-z0-9])(%s)([^A-Za-z0-9]|\Z)", retVal)
if match:
if count >= len(params):
raise Exception("wrong number of parameters during string formatting")
warnMsg = "wrong number of parameters during string formatting. "
warnMsg += "Please report by e-mail content \"%r | %r | %r\" to 'dev@sqlmap.org'" % (format_, params, retVal)
raise SqlmapValueException(warnMsg)
else:
retVal = re.sub(r"(\A|[^A-Za-z0-9])(%s)([^A-Za-z0-9]|\Z)", r"\g<1>%s\g<3>" % params[count], retVal, 1)
count += 1
@ -1738,7 +1741,7 @@ def posixToNtSlashes(filepath):
'C:\\\\Windows'
"""
return filepath.replace('/', '\\')
return filepath.replace('/', '\\') if filepath else filepath
def ntToPosixSlashes(filepath):
"""
@ -1749,7 +1752,7 @@ def ntToPosixSlashes(filepath):
'C:/Windows'
"""
return filepath.replace('\\', '/')
return filepath.replace('\\', '/') if filepath else filepath
def isHexEncodedString(subject):
"""

View File

@ -177,6 +177,7 @@ class HTTP_HEADER:
TRANSFER_ENCODING = "Transfer-Encoding"
URI = "URI"
VIA = "Via"
X_POWERED_BY = "X-Powered-By"
class EXPECTED:
BOOL = "bool"

View File

@ -20,13 +20,13 @@ def cleanupVals(text, tag):
text = text.split(',')
if isinstance(text, basestring):
text = int(text) if text.isdigit() else str(text)
text = int(text) if text.isdigit() else text
elif isinstance(text, list):
count = 0
for _ in text:
text[count] = int(_) if _.isdigit() else str(_)
text[count] = int(_) if _.isdigit() else _
count += 1
if len(text) == 1 and tag not in ("clause", "where"):

View File

@ -103,7 +103,7 @@ def forgeHeaders(items=None):
kb.mergeCookies = not _ or _[0] in ("y", "Y")
if kb.mergeCookies and kb.injection.place != PLACE.COOKIE:
_ = lambda x: re.sub(r"(?i)\b%s=[^%s]+" % (re.escape(cookie.name), conf.cookieDel or DEFAULT_COOKIE_DELIMITER), "%s=%s" % (cookie.name, getUnicode(cookie.value)), x)
_ = lambda x: re.sub(r"(?i)\b%s=[^%s]+" % (re.escape(cookie.name), conf.cookieDel or DEFAULT_COOKIE_DELIMITER), ("%s=%s" % (cookie.name, getUnicode(cookie.value))).replace('\\', r'\\'), x)
headers[HTTP_HEADER.COOKIE] = _(headers[HTTP_HEADER.COOKIE])
if PLACE.COOKIE in conf.parameters:
@ -156,6 +156,8 @@ def checkCharEncoding(encoding, warn=True):
if delimiter in encoding:
encoding = encoding[:encoding.find(delimiter)].strip()
encoding = encoding.replace("&quot", "")
# popular typos/errors
if "8858" in encoding:
encoding = encoding.replace("8858", "8859") # iso-8858 -> iso-8859
@ -189,6 +191,8 @@ def checkCharEncoding(encoding, warn=True):
encoding = "ascii"
elif encoding.find("utf8") > 0:
encoding = "utf8"
elif encoding.find("utf-8") > 0:
encoding = "utf-8"
# Reference: http://philip.html5.org/data/charsets-2.html
if encoding in translate:

View File

@ -456,6 +456,9 @@ class Connect(object):
for cookie in conf.cj:
if cookie.value is None:
cookie.value = ""
else:
for char in (r"\r", r"\n"):
cookie.value = re.sub(r"(%s)([^ \t])" % char, r"\g<1>\t\g<2>", cookie.value)
conn = urllib2.urlopen(req)

View File

@ -5,6 +5,7 @@ Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/)
See the file 'doc/COPYING' for copying permission
"""
import distutils.version
import httplib
import socket
import urllib2
@ -13,6 +14,7 @@ from lib.core.common import getSafeExString
from lib.core.data import kb
from lib.core.data import logger
from lib.core.exception import SqlmapConnectionException
from lib.core.settings import PYVERSION
ssl = None
try:
@ -84,7 +86,10 @@ class HTTPSConnection(httplib.HTTPSConnection):
logger.debug("SSL connection error occurred ('%s')" % getSafeExString(ex))
if not success:
raise SqlmapConnectionException("can't establish SSL connection")
errMsg = "can't establish SSL connection"
if distutils.version.LooseVersion(PYVERSION) < distutils.version.LooseVersion("2.7.10"):
errMsg += " (please retry with Python >= 2.7.10)"
raise SqlmapConnectionException(errMsg)
class HTTPSHandler(urllib2.HTTPSHandler):
def https_open(self, req):

View File

@ -7,16 +7,20 @@ See the file 'doc/COPYING' for copying permission
from lib.core.common import Backend
from lib.core.common import randomStr
from lib.core.common import readInput
from lib.core.common import safeSQLIdentificatorNaming
from lib.core.common import unsafeSQLIdentificatorNaming
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.data import paths
from lib.core.data import queries
from lib.core.exception import SqlmapMissingMandatoryOptionException
from lib.core.exception import SqlmapNoneDataException
from lib.core.exception import SqlmapUserQuitException
from lib.core.settings import CURRENT_DB
from lib.utils.pivotdumptable import pivotDumpTable
from lib.techniques.brute.use import columnExists
from plugins.generic.enumeration import Enumeration as GenericEnumeration
class Enumeration(GenericEnumeration):
@ -91,7 +95,7 @@ class Enumeration(GenericEnumeration):
return kb.data.cachedTables
def getColumns(self, onlyColNames=False):
def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMode=False):
self.forceDbmsEnum()
if conf.db is None or conf.db == CURRENT_DB:
@ -111,6 +115,17 @@ class Enumeration(GenericEnumeration):
conf.db = safeSQLIdentificatorNaming(conf.db)
if conf.col:
colList = conf.col.split(",")
else:
colList = []
if conf.excludeCol:
colList = [_ for _ in colList if _ not in conf.excludeCol.split(',')]
for col in colList:
colList[colList.index(col)] = safeSQLIdentificatorNaming(col)
if conf.tbl:
tblList = conf.tbl.split(",")
else:
@ -129,6 +144,43 @@ class Enumeration(GenericEnumeration):
for tbl in tblList:
tblList[tblList.index(tbl)] = safeSQLIdentificatorNaming(tbl, True)
if bruteForce:
resumeAvailable = False
for tbl in tblList:
for db, table, colName, colType in kb.brute.columns:
if db == conf.db and table == tbl:
resumeAvailable = True
break
if resumeAvailable and not conf.freshQueries or colList:
columns = {}
for column in colList:
columns[column] = None
for tbl in tblList:
for db, table, colName, colType in kb.brute.columns:
if db == conf.db and table == tbl:
columns[colName] = colType
if conf.db in kb.data.cachedColumns:
kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)] = columns
else:
kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = {safeSQLIdentificatorNaming(tbl, True): columns}
return kb.data.cachedColumns
message = "do you want to use common column existence check? [y/N/q] "
test = readInput(message, default="Y" if "Y" in message else "N")
if test[0] in ("n", "N"):
return
elif test[0] in ("q", "Q"):
raise SqlmapUserQuitException
else:
return columnExists(paths.COMMON_COLUMNS)
rootQuery = queries[Backend.getIdentifiedDbms()].columns
for tbl in tblList:
@ -141,6 +193,12 @@ class Enumeration(GenericEnumeration):
return {conf.db: kb.data.cachedColumns[conf.db]}
if dumpMode and colList:
table = {}
table[safeSQLIdentificatorNaming(tbl)] = dict((_, None) for _ in colList)
kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = table
continue
infoMsg = "fetching columns "
infoMsg += "for table '%s' " % unsafeSQLIdentificatorNaming(tbl)
infoMsg += "on database '%s'" % unsafeSQLIdentificatorNaming(conf.db)

View File

@ -60,7 +60,7 @@ class Takeover(GenericTakeover):
else:
self.__plugindir = "%s/lib/mysql/plugin" % self.__basedir
self.__plugindir = ntToPosixSlashes(normalizePath(self.__plugindir))
self.__plugindir = ntToPosixSlashes(normalizePath(self.__plugindir)) or '.'
self.udfRemoteFile = "%s/%s.%s" % (self.__plugindir, self.udfSharedLibName, self.udfSharedLibExt)
@ -74,7 +74,7 @@ class Takeover(GenericTakeover):
# NOTE: specifying the relative path as './udf.dll'
# saves in @@datadir on both MySQL 4.1 and MySQL 5.0
self.__datadir = "."
self.__datadir = '.'
self.__datadir = ntToPosixSlashes(normalizePath(self.__datadir))
# The DLL can be in either C:\WINDOWS, C:\WINDOWS\system,

View File

@ -9,19 +9,23 @@ from lib.core.common import Backend
from lib.core.common import filterPairValues
from lib.core.common import isTechniqueAvailable
from lib.core.common import randomStr
from lib.core.common import readInput
from lib.core.common import safeSQLIdentificatorNaming
from lib.core.common import unArrayizeValue
from lib.core.common import unsafeSQLIdentificatorNaming
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.data import paths
from lib.core.data import queries
from lib.core.dicts import SYBASE_TYPES
from lib.core.enums import PAYLOAD
from lib.core.exception import SqlmapMissingMandatoryOptionException
from lib.core.exception import SqlmapNoneDataException
from lib.core.exception import SqlmapUserQuitException
from lib.core.settings import CURRENT_DB
from lib.utils.pivotdumptable import pivotDumpTable
from lib.techniques.brute.use import columnExists
from plugins.generic.enumeration import Enumeration as GenericEnumeration
class Enumeration(GenericEnumeration):
@ -159,7 +163,7 @@ class Enumeration(GenericEnumeration):
return kb.data.cachedTables
def getColumns(self, onlyColNames=False):
def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMode=False):
self.forceDbmsEnum()
if conf.db is None or conf.db == CURRENT_DB:
@ -208,6 +212,43 @@ class Enumeration(GenericEnumeration):
for tbl in tblList:
tblList[tblList.index(tbl)] = safeSQLIdentificatorNaming(tbl)
if bruteForce:
resumeAvailable = False
for tbl in tblList:
for db, table, colName, colType in kb.brute.columns:
if db == conf.db and table == tbl:
resumeAvailable = True
break
if resumeAvailable and not conf.freshQueries or colList:
columns = {}
for column in colList:
columns[column] = None
for tbl in tblList:
for db, table, colName, colType in kb.brute.columns:
if db == conf.db and table == tbl:
columns[colName] = colType
if conf.db in kb.data.cachedColumns:
kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)] = columns
else:
kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = {safeSQLIdentificatorNaming(tbl, True): columns}
return kb.data.cachedColumns
message = "do you want to use common column existence check? [y/N/q] "
test = readInput(message, default="Y" if "Y" in message else "N")
if test[0] in ("n", "N"):
return
elif test[0] in ("q", "Q"):
raise SqlmapUserQuitException
else:
return columnExists(paths.COMMON_COLUMNS)
rootQuery = queries[Backend.getIdentifiedDbms()].columns
if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct:
@ -225,7 +266,7 @@ class Enumeration(GenericEnumeration):
return {conf.db: kb.data.cachedColumns[conf.db]}
if colList:
if dumpMode and colList:
table = {}
table[safeSQLIdentificatorNaming(tbl)] = dict((_, None) for _ in colList)
kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = table

View File

@ -238,7 +238,7 @@ class Databases:
return kb.data.cachedTables
message = "do you want to use common table existence check? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS,) else "[y/N/q]")
message = "do you want to use common table existence check? %s " % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS,) else "[y/N/q]")
test = readInput(message, default="Y" if "Y" in message else "N")
if test[0] in ("n", "N"):

View File

@ -111,7 +111,10 @@ def main():
except SqlmapUserQuitException:
errMsg = "user quit"
logger.error(errMsg)
try:
logger.error(errMsg)
except KeyboardInterrupt:
pass
except (SqlmapSilentQuitException, bdb.BdbQuit):
pass
@ -121,18 +124,30 @@ def main():
except SqlmapBaseException as ex:
errMsg = getSafeExString(ex)
logger.critical(errMsg)
try:
logger.critical(errMsg)
except KeyboardInterrupt:
pass
raise SystemExit
except KeyboardInterrupt:
print
errMsg = "user aborted"
logger.error(errMsg)
try:
logger.error(errMsg)
except KeyboardInterrupt:
pass
except EOFError:
print
errMsg = "exit"
logger.error(errMsg)
try:
logger.error(errMsg)
except KeyboardInterrupt:
pass
except SystemExit:
pass
@ -142,32 +157,35 @@ def main():
errMsg = unhandledExceptionMessage()
excMsg = traceback.format_exc()
if any(_ in excMsg for _ in ("No space left", "Disk quota exceeded")):
errMsg = "no space left on output device"
logger.error(errMsg)
raise SystemExit
try:
if any(_ in excMsg for _ in ("No space left", "Disk quota exceeded")):
errMsg = "no space left on output device"
logger.error(errMsg)
raise SystemExit
elif "bad marshal data (unknown type code)" in excMsg:
match = re.search(r"\s*(.+)\s+ValueError", excMsg)
errMsg = "one of your .pyc files are corrupted%s" % (" ('%s')" % match.group(1) if match else "")
errMsg += ". Please delete .pyc files on your system to fix the problem"
logger.error(errMsg)
raise SystemExit
elif "bad marshal data (unknown type code)" in excMsg:
match = re.search(r"\s*(.+)\s+ValueError", excMsg)
errMsg = "one of your .pyc files are corrupted%s" % (" ('%s')" % match.group(1) if match else "")
errMsg += ". Please delete .pyc files on your system to fix the problem"
logger.error(errMsg)
raise SystemExit
for match in re.finditer(r'File "(.+?)", line', excMsg):
file_ = match.group(1)
file_ = os.path.relpath(file_, os.path.dirname(__file__))
file_ = file_.replace("\\", '/')
file_ = re.sub(r"\.\./", '/', file_).lstrip('/')
excMsg = excMsg.replace(match.group(1), file_)
for match in re.finditer(r'File "(.+?)", line', excMsg):
file_ = match.group(1)
file_ = os.path.relpath(file_, os.path.dirname(__file__))
file_ = file_.replace("\\", '/')
file_ = re.sub(r"\.\./", '/', file_).lstrip('/')
excMsg = excMsg.replace(match.group(1), file_)
errMsg = maskSensitiveData(errMsg)
excMsg = maskSensitiveData(excMsg)
errMsg = maskSensitiveData(errMsg)
excMsg = maskSensitiveData(excMsg)
logger.critical(errMsg)
kb.stickyLevel = logging.CRITICAL
dataToStdout(excMsg)
createGithubIssue(errMsg, excMsg)
logger.critical(errMsg)
kb.stickyLevel = logging.CRITICAL
dataToStdout(excMsg)
createGithubIssue(errMsg, excMsg)
except KeyboardInterrupt:
pass
finally:
if conf.get("showTime"):

26
waf/safe3.py Normal file
View File

@ -0,0 +1,26 @@
#!/usr/bin/env python
"""
Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/)
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
__product__ = "Safe3 Web Application Firewall"
def detect(get_page):
retval = False
for vector in WAF_ATTACK_VECTORS:
page, headers, code = get_page(get=vector)
retval = re.search(r"Safe3WAF", headers.get(HTTP_HEADER.X_POWERED_BY, ""), re.I) is not None
retval |= re.search(r"Safe3 Web Firewall", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None
if retval:
break
return retval

View File

@ -7,6 +7,7 @@ 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
__product__ = "Safedog Web Application Firewall (Safedog)"
@ -16,7 +17,9 @@ def detect(get_page):
for vector in WAF_ATTACK_VECTORS:
page, headers, code = get_page(get=vector)
retval = re.search(r"WAF/2.0", headers.get("X-Powered-By", ""), re.I) is not None
retval = re.search(r"WAF/2\.0", headers.get(HTTP_HEADER.X_POWERED_BY, ""), re.I) is not None
retval |= re.search(r"Safedog", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None
retval |= re.search(r"safedog", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None
if retval:
break

View File

@ -17,6 +17,7 @@
<error regexp="Warning.*\Wpg_.*"/>
<error regexp="valid PostgreSQL result"/>
<error regexp="Npgsql\."/>
<error regexp="PG::SyntaxError:"/>
<error regexp="org\.postgresql\.util\.PSQLException"/>
<error regexp="ERROR:\s\ssyntax error at or near "/>
</dbms>