mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2025-07-27 08:30:10 +03:00
merge to latest
This commit is contained in:
commit
4fd39167aa
|
@ -1,7 +1,7 @@
|
||||||
Due to the anti-virus positive detection of shell scripts stored inside this folder, we needed to somehow circumvent this. As from the plain sqlmap users perspective nothing has to be done prior to their usage by sqlmap, but if you want to have access to their original source code use the decrypt functionality of the ../extra/cloak/cloak.py utility.
|
Due to the anti-virus positive detection of shell scripts stored inside this folder, we needed to somehow circumvent this. As from the plain sqlmap users perspective nothing has to be done prior to their usage by sqlmap, but if you want to have access to their original source code use the decrypt functionality of the ../../extra/cloak/cloak.py utility.
|
||||||
|
|
||||||
To prepare the original scripts to the cloaked form use this command:
|
To prepare the original scripts to the cloaked form use this command:
|
||||||
find backdoors/backdoor.* stagers/stager.* -type f -exec python ../extra/cloak/cloak.py -i '{}' \;
|
find backdoors/backdoor.* stagers/stager.* -type f -exec python ../../extra/cloak/cloak.py -i '{}' \;
|
||||||
|
|
||||||
To get back them into the original form use this:
|
To get back them into the original form use this:
|
||||||
find backdoors/backdoor.*_ stagers/stager.*_ -type f -exec python ../extra/cloak/cloak.py -d -i '{}' \;
|
find backdoors/backdoor.*_ stagers/stager.*_ -type f -exec python ../../extra/cloak/cloak.py -d -i '{}' \;
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -83,7 +83,7 @@
|
||||||
<error regexp="CLI Driver.*?DB2"/>
|
<error regexp="CLI Driver.*?DB2"/>
|
||||||
<error regexp="DB2 SQL error"/>
|
<error regexp="DB2 SQL error"/>
|
||||||
<error regexp="\bdb2_\w+\("/>
|
<error regexp="\bdb2_\w+\("/>
|
||||||
<error regexp="SQLSTATE.+SQLCODE"/>
|
<error regexp="SQLCODE[=:\d, -]+SQLSTATE"/>
|
||||||
<error regexp="com\.ibm\.db2\.jcc"/>
|
<error regexp="com\.ibm\.db2\.jcc"/>
|
||||||
<error regexp="Zend_Db_(Adapter|Statement)_Db2_Exception"/>
|
<error regexp="Zend_Db_(Adapter|Statement)_Db2_Exception"/>
|
||||||
<error regexp="Pdo[./_\\]Ibm"/>
|
<error regexp="Pdo[./_\\]Ibm"/>
|
||||||
|
|
|
@ -301,8 +301,8 @@
|
||||||
<blind query="SELECT COLUMN_NAME FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='%s' AND OWNER='%s'" query2="SELECT DATA_TYPE FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='%s' AND COLUMN_NAME='%s' AND OWNER='%s'" count="SELECT COUNT(COLUMN_NAME) FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='%s' AND OWNER='%s'" condition="COLUMN_NAME"/>
|
<blind query="SELECT COLUMN_NAME FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='%s' AND OWNER='%s'" query2="SELECT DATA_TYPE FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='%s' AND COLUMN_NAME='%s' AND OWNER='%s'" count="SELECT COUNT(COLUMN_NAME) FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='%s' AND OWNER='%s'" condition="COLUMN_NAME"/>
|
||||||
</columns>
|
</columns>
|
||||||
<dump_table>
|
<dump_table>
|
||||||
<inband query="SELECT %s FROM %s"/>
|
<inband query="SELECT %s FROM %s ORDER BY ROWNUM"/>
|
||||||
<blind query="SELECT %s FROM (SELECT qq.*,ROWNUM AS LIMIT FROM %s qq) WHERE LIMIT=%d" count="SELECT COUNT(*) FROM %s"/>
|
<blind query="SELECT %s FROM (SELECT qq.*,ROWNUM AS LIMIT FROM %s qq ORDER BY ROWNUM) WHERE LIMIT=%d" count="SELECT COUNT(*) FROM %s"/>
|
||||||
</dump_table>
|
</dump_table>
|
||||||
<!-- NOTE: in Oracle schema names are the counterpart to database names on other DBMSes -->
|
<!-- NOTE: in Oracle schema names are the counterpart to database names on other DBMSes -->
|
||||||
<search_db>
|
<search_db>
|
||||||
|
|
|
@ -277,7 +277,7 @@ be bound by the terms and conditions of this License Agreement.
|
||||||
* The `bottle` web framework library located under `thirdparty/bottle/`.
|
* The `bottle` web framework library located under `thirdparty/bottle/`.
|
||||||
Copyright (C) 2012, Marcel Hellkamp.
|
Copyright (C) 2012, Marcel Hellkamp.
|
||||||
* The `identYwaf` library located under `thirdparty/identywaf/`.
|
* The `identYwaf` library located under `thirdparty/identywaf/`.
|
||||||
Copyright (C) 2019, Miroslav Stampar.
|
Copyright (C) 2019-2020, Miroslav Stampar.
|
||||||
* The `ordereddict` library located under `thirdparty/odict/`.
|
* The `ordereddict` library located under `thirdparty/odict/`.
|
||||||
Copyright (C) 2009, Raymond Hettinger.
|
Copyright (C) 2009, Raymond Hettinger.
|
||||||
* The `six` Python 2 and 3 compatibility library located under `thirdparty/six/`.
|
* The `six` Python 2 and 3 compatibility library located under `thirdparty/six/`.
|
||||||
|
|
|
@ -32,7 +32,7 @@ Pour afficher une liste complète des options et des commutateurs (switches), ta
|
||||||
|
|
||||||
python sqlmap.py -hh
|
python sqlmap.py -hh
|
||||||
|
|
||||||
Vous pouvez regarder un vidéo [ici](https://asciinema.org/a/46601) pour plus d'exemples.
|
Vous pouvez regarder une vidéo [ici](https://asciinema.org/a/46601) pour plus d'exemples.
|
||||||
Pour obtenir un aperçu des ressources de __sqlmap__, une liste des fonctionnalités prises en charge, la description de toutes les options, ainsi que des exemples, nous vous recommandons de consulter [le wiki](https://github.com/sqlmapproject/sqlmap/wiki/Usage).
|
Pour obtenir un aperçu des ressources de __sqlmap__, une liste des fonctionnalités prises en charge, la description de toutes les options, ainsi que des exemples, nous vous recommandons de consulter [le wiki](https://github.com/sqlmapproject/sqlmap/wiki/Usage).
|
||||||
|
|
||||||
Liens
|
Liens
|
||||||
|
|
|
@ -43,7 +43,7 @@ Tautan
|
||||||
* Situs: http://sqlmap.org
|
* Situs: http://sqlmap.org
|
||||||
* Unduh: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) atau [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master)
|
* Unduh: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) atau [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master)
|
||||||
* RSS feed dari commits: https://github.com/sqlmapproject/sqlmap/commits/master.atom
|
* RSS feed dari commits: https://github.com/sqlmapproject/sqlmap/commits/master.atom
|
||||||
* Issue tracker: https://github.com/sqlmapproject/sqlmap/issues
|
* Pelacak Masalah: https://github.com/sqlmapproject/sqlmap/issues
|
||||||
* Wiki Manual Penggunaan: https://github.com/sqlmapproject/sqlmap/wiki
|
* Wiki Manual Penggunaan: https://github.com/sqlmapproject/sqlmap/wiki
|
||||||
* Pertanyaan yang Sering Ditanyakan (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ
|
* Pertanyaan yang Sering Ditanyakan (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ
|
||||||
* Twitter: [@sqlmap](https://twitter.com/sqlmap)
|
* Twitter: [@sqlmap](https://twitter.com/sqlmap)
|
||||||
|
|
|
@ -21,7 +21,7 @@ if sys.version_info >= (3, 0):
|
||||||
xrange = range
|
xrange = range
|
||||||
ord = lambda _: _
|
ord = lambda _: _
|
||||||
|
|
||||||
KEY = b"Beeth7hoyooleeF0"
|
KEY = b"MOZFqVjlk1CY436G"
|
||||||
|
|
||||||
def xor(message, key):
|
def xor(message, key):
|
||||||
return b"".join(struct.pack('B', ord(message[i]) ^ ord(key[i % len(key)])) for i in range(len(message)))
|
return b"".join(struct.pack('B', ord(message[i]) ^ ord(key[i % len(key)])) for i in range(len(message)))
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
export SQLMAP_DREI=1
|
export SQLMAP_DREI=1
|
||||||
#for i in $(find . -iname "*.py" | grep -v __init__); do python3 -c 'import '`echo $i | cut -d '.' -f 2 | cut -d '/' -f 2- | sed 's/\//./g'`''; done
|
#for i in $(find . -iname "*.py" | grep -v __init__); do python3 -c 'import '`echo $i | cut -d '.' -f 2 | cut -d '/' -f 2- | sed 's/\//./g'`''; done
|
||||||
for i in $(find . -iname "*.py" | grep -v __init__); do PYTHONWARNINGS=all python3.7 -m compileall $i | sed 's/Compiling/Checking/g'; done
|
for i in $(find . -iname "*.py" | grep -v __init__); do PYTHONWARNINGS=all python3 -m compileall $i | sed 's/Compiling/Checking/g'; done
|
||||||
unset SQLMAP_DREI
|
unset SQLMAP_DREI
|
||||||
source `dirname "$0"`"/junk.sh"
|
source `dirname "$0"`"/junk.sh"
|
||||||
|
|
||||||
|
|
16
extra/shutils/recloak.sh
Executable file
16
extra/shutils/recloak.sh
Executable file
|
@ -0,0 +1,16 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# NOTE: this script is for dev usage after AV something something
|
||||||
|
|
||||||
|
DIR=$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)
|
||||||
|
|
||||||
|
cd $DIR/../..
|
||||||
|
for file in $(find -regex ".*\.[a-z]*_" -type f | grep -v wordlist); do python extra/cloak/cloak.py -d -i $file; done
|
||||||
|
|
||||||
|
cd $DIR/../cloak
|
||||||
|
sed -i 's/KEY = .*/KEY = b"'`python -c 'import random; import string; print("".join(random.sample(string.ascii_letters + string.digits, 16)))'`'"/g' cloak.py
|
||||||
|
|
||||||
|
cd $DIR/../..
|
||||||
|
for file in $(find -regex ".*\.[a-z]*_" -type f | grep -v wordlist); do python extra/cloak/cloak.py -i `echo $file | sed 's/_$//g'`; done
|
||||||
|
|
||||||
|
git clean -f > /dev/null
|
|
@ -9,6 +9,7 @@ See the file 'LICENSE' for copying permission
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import base64
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
@ -146,7 +147,10 @@ class ReqHandler(BaseHTTPRequestHandler):
|
||||||
if "query" in self.params:
|
if "query" in self.params:
|
||||||
_cursor.execute(self.params["query"])
|
_cursor.execute(self.params["query"])
|
||||||
elif "id" in self.params:
|
elif "id" in self.params:
|
||||||
_cursor.execute("SELECT * FROM users WHERE id=%s LIMIT 0, 1" % self.params["id"])
|
if "base64" in self.params:
|
||||||
|
_cursor.execute("SELECT * FROM users WHERE id=%s LIMIT 0, 1" % base64.b64decode("%s===" % self.params["id"], altchars=self.params.get("altchars")).decode())
|
||||||
|
else:
|
||||||
|
_cursor.execute("SELECT * FROM users WHERE id=%s LIMIT 0, 1" % self.params["id"])
|
||||||
results = _cursor.fetchall()
|
results = _cursor.fetchall()
|
||||||
|
|
||||||
output += "<b>SQL results:</b><br>\n"
|
output += "<b>SQL results:</b><br>\n"
|
||||||
|
|
|
@ -1581,7 +1581,7 @@ def checkConnection(suppressOutput=False):
|
||||||
kb.originalPage = kb.pageTemplate = threadData.lastPage
|
kb.originalPage = kb.pageTemplate = threadData.lastPage
|
||||||
kb.originalCode = threadData.lastCode
|
kb.originalCode = threadData.lastCode
|
||||||
|
|
||||||
if conf.cj and not conf.cookie and not conf.dropSetCookie:
|
if conf.cj and not conf.cookie and not any(_[0] == HTTP_HEADER.COOKIE for _ in conf.httpHeaders) and not conf.dropSetCookie:
|
||||||
candidate = DEFAULT_COOKIE_DELIMITER.join("%s=%s" % (_.name, _.value) for _ in conf.cj)
|
candidate = DEFAULT_COOKIE_DELIMITER.join("%s=%s" % (_.name, _.value) for _ in conf.cj)
|
||||||
|
|
||||||
message = "you have not declared cookie(s), while "
|
message = "you have not declared cookie(s), while "
|
||||||
|
|
|
@ -42,6 +42,7 @@ from lib.core.enums import PAYLOAD
|
||||||
from lib.core.enums import PLACE
|
from lib.core.enums import PLACE
|
||||||
from lib.core.enums import POST_HINT
|
from lib.core.enums import POST_HINT
|
||||||
from lib.core.exception import SqlmapNoneDataException
|
from lib.core.exception import SqlmapNoneDataException
|
||||||
|
from lib.core.settings import BOUNDED_BASE64_MARKER
|
||||||
from lib.core.settings import BOUNDARY_BACKSLASH_MARKER
|
from lib.core.settings import BOUNDARY_BACKSLASH_MARKER
|
||||||
from lib.core.settings import BOUNDED_INJECTION_MARKER
|
from lib.core.settings import BOUNDED_INJECTION_MARKER
|
||||||
from lib.core.settings import DEFAULT_COOKIE_DELIMITER
|
from lib.core.settings import DEFAULT_COOKIE_DELIMITER
|
||||||
|
@ -183,7 +184,7 @@ class Agent(object):
|
||||||
newValue = self.adjustLateValues(newValue)
|
newValue = self.adjustLateValues(newValue)
|
||||||
|
|
||||||
# TODO: support for POST_HINT
|
# TODO: support for POST_HINT
|
||||||
newValue = encodeBase64(newValue, binary=False, encoding=conf.encoding or UNICODE_ENCODING, safe=conf.base64Safe)
|
newValue = "%s%s%s" % (BOUNDED_BASE64_MARKER, newValue, BOUNDED_BASE64_MARKER)
|
||||||
|
|
||||||
if parameter in kb.base64Originals:
|
if parameter in kb.base64Originals:
|
||||||
origValue = kb.base64Originals[parameter]
|
origValue = kb.base64Originals[parameter]
|
||||||
|
@ -397,6 +398,10 @@ class Agent(object):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if payload:
|
if payload:
|
||||||
|
for match in re.finditer(r"%s(.*?)%s" % (BOUNDED_BASE64_MARKER, BOUNDED_BASE64_MARKER), payload):
|
||||||
|
_ = encodeBase64(match.group(1), binary=False, encoding=conf.encoding or UNICODE_ENCODING, safe=conf.base64Safe)
|
||||||
|
payload = payload.replace(match.group(0), _)
|
||||||
|
|
||||||
payload = payload.replace(SLEEP_TIME_MARKER, str(conf.timeSec))
|
payload = payload.replace(SLEEP_TIME_MARKER, str(conf.timeSec))
|
||||||
payload = payload.replace(SINGLE_QUOTE_MARKER, "'")
|
payload = payload.replace(SINGLE_QUOTE_MARKER, "'")
|
||||||
|
|
||||||
|
@ -1202,12 +1207,15 @@ class Agent(object):
|
||||||
|
|
||||||
def whereQuery(self, query):
|
def whereQuery(self, query):
|
||||||
if conf.dumpWhere and query:
|
if conf.dumpWhere and query:
|
||||||
match = re.search(r" (LIMIT|ORDER).+", query, re.I)
|
if Backend.isDbms(DBMS.ORACLE) and re.search(r"qq ORDER BY \w+\)", query, re.I) is not None:
|
||||||
if match:
|
prefix, suffix = re.sub(r"(?i)(qq)( ORDER BY \w+\))", r"\g<1> WHERE %s\g<2>" % conf.dumpWhere, query), ""
|
||||||
suffix = match.group(0)
|
|
||||||
prefix = query[:-len(suffix)]
|
|
||||||
else:
|
else:
|
||||||
prefix, suffix = query, ""
|
match = re.search(r" (LIMIT|ORDER).+", query, re.I)
|
||||||
|
if match:
|
||||||
|
suffix = match.group(0)
|
||||||
|
prefix = query[:-len(suffix)]
|
||||||
|
else:
|
||||||
|
prefix, suffix = query, ""
|
||||||
|
|
||||||
if conf.tbl and "%s)" % conf.tbl.upper() in prefix.upper():
|
if conf.tbl and "%s)" % conf.tbl.upper() in prefix.upper():
|
||||||
prefix = re.sub(r"(?i)%s\)" % re.escape(conf.tbl), "%s WHERE %s)" % (conf.tbl, conf.dumpWhere), prefix)
|
prefix = re.sub(r"(?i)%s\)" % re.escape(conf.tbl), "%s WHERE %s)" % (conf.tbl, conf.dumpWhere), prefix)
|
||||||
|
|
|
@ -632,6 +632,7 @@ def paramToDict(place, parameters=None):
|
||||||
if parameter in (conf.base64Parameter or []):
|
if parameter in (conf.base64Parameter or []):
|
||||||
try:
|
try:
|
||||||
kb.base64Originals[parameter] = oldValue = value
|
kb.base64Originals[parameter] = oldValue = value
|
||||||
|
value = urldecode(value, convall=True)
|
||||||
value = decodeBase64(value, binary=False, encoding=conf.encoding or UNICODE_ENCODING)
|
value = decodeBase64(value, binary=False, encoding=conf.encoding or UNICODE_ENCODING)
|
||||||
parameters = re.sub(r"\b%s(\b|\Z)" % re.escape(oldValue), value, parameters)
|
parameters = re.sub(r"\b%s(\b|\Z)" % re.escape(oldValue), value, parameters)
|
||||||
except:
|
except:
|
||||||
|
@ -1051,6 +1052,16 @@ def dataToDumpFile(dumpFile, data):
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def dataToOutFile(filename, data):
|
def dataToOutFile(filename, data):
|
||||||
|
"""
|
||||||
|
Saves data to filename
|
||||||
|
|
||||||
|
>>> pushValue(conf.get("filePath"))
|
||||||
|
>>> conf.filePath = tempfile.gettempdir()
|
||||||
|
>>> "_etc_passwd" in dataToOutFile("/etc/passwd", b":::*")
|
||||||
|
True
|
||||||
|
>>> conf.filePath = popValue()
|
||||||
|
"""
|
||||||
|
|
||||||
retVal = None
|
retVal = None
|
||||||
|
|
||||||
if data:
|
if data:
|
||||||
|
@ -1714,6 +1725,11 @@ def escapeJsonValue(value):
|
||||||
Escapes JSON value (used in payloads)
|
Escapes JSON value (used in payloads)
|
||||||
|
|
||||||
# Reference: https://stackoverflow.com/a/16652683
|
# Reference: https://stackoverflow.com/a/16652683
|
||||||
|
|
||||||
|
>>> "\\n" in escapeJsonValue("foo\\nbar")
|
||||||
|
False
|
||||||
|
>>> "\\\\t" in escapeJsonValue("foo\\tbar")
|
||||||
|
True
|
||||||
"""
|
"""
|
||||||
|
|
||||||
retVal = ""
|
retVal = ""
|
||||||
|
@ -1888,6 +1904,12 @@ def getLocalIP():
|
||||||
def getRemoteIP():
|
def getRemoteIP():
|
||||||
"""
|
"""
|
||||||
Get remote/target IP address
|
Get remote/target IP address
|
||||||
|
|
||||||
|
>>> pushValue(conf.hostname)
|
||||||
|
>>> conf.hostname = "localhost"
|
||||||
|
>>> getRemoteIP() == "127.0.0.1"
|
||||||
|
True
|
||||||
|
>>> conf.hostname = popValue()
|
||||||
"""
|
"""
|
||||||
|
|
||||||
retVal = None
|
retVal = None
|
||||||
|
@ -2014,6 +2036,9 @@ def normalizePath(filepath):
|
||||||
def safeFilepathEncode(filepath):
|
def safeFilepathEncode(filepath):
|
||||||
"""
|
"""
|
||||||
Returns filepath in (ASCII) format acceptable for OS handling (e.g. reading)
|
Returns filepath in (ASCII) format acceptable for OS handling (e.g. reading)
|
||||||
|
|
||||||
|
>>> 'sqlmap' in safeFilepathEncode(paths.SQLMAP_HOME_PATH)
|
||||||
|
True
|
||||||
"""
|
"""
|
||||||
|
|
||||||
retVal = filepath
|
retVal = filepath
|
||||||
|
@ -2046,6 +2071,8 @@ def safeStringFormat(format_, params):
|
||||||
|
|
||||||
>>> safeStringFormat('SELECT foo FROM %s LIMIT %d', ('bar', '1'))
|
>>> safeStringFormat('SELECT foo FROM %s LIMIT %d', ('bar', '1'))
|
||||||
'SELECT foo FROM bar LIMIT 1'
|
'SELECT foo FROM bar LIMIT 1'
|
||||||
|
>>> safeStringFormat("SELECT foo FROM %s WHERE name LIKE '%susan%' LIMIT %d", ('bar', '1'))
|
||||||
|
"SELECT foo FROM bar WHERE name LIKE '%susan%' LIMIT 1"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if format_.count(PAYLOAD_DELIMITER) == 2:
|
if format_.count(PAYLOAD_DELIMITER) == 2:
|
||||||
|
@ -2089,7 +2116,10 @@ def safeStringFormat(format_, params):
|
||||||
warnMsg += "Please report by e-mail content \"%r | %r | %r\" to '%s'" % (format_, params, retVal, DEV_EMAIL_ADDRESS)
|
warnMsg += "Please report by e-mail content \"%r | %r | %r\" to '%s'" % (format_, params, retVal, DEV_EMAIL_ADDRESS)
|
||||||
raise SqlmapValueException(warnMsg)
|
raise SqlmapValueException(warnMsg)
|
||||||
else:
|
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)
|
try:
|
||||||
|
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)
|
||||||
|
except re.error:
|
||||||
|
retVal = retVal.replace(match.group(0), match.group(0) % params[count], 1)
|
||||||
count += 1
|
count += 1
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
@ -2220,6 +2250,15 @@ def isHexEncodedString(subject):
|
||||||
def isMultiThreadMode():
|
def isMultiThreadMode():
|
||||||
"""
|
"""
|
||||||
Checks if running in multi-thread(ing) mode
|
Checks if running in multi-thread(ing) mode
|
||||||
|
|
||||||
|
>>> isMultiThreadMode()
|
||||||
|
False
|
||||||
|
>>> _ = lambda: time.sleep(0.1)
|
||||||
|
>>> thread = threading.Thread(target=_)
|
||||||
|
>>> thread.daemon = True
|
||||||
|
>>> thread.start()
|
||||||
|
>>> isMultiThreadMode()
|
||||||
|
True
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return threading.activeCount() > 1
|
return threading.activeCount() > 1
|
||||||
|
@ -2228,6 +2267,9 @@ def isMultiThreadMode():
|
||||||
def getConsoleWidth(default=80):
|
def getConsoleWidth(default=80):
|
||||||
"""
|
"""
|
||||||
Returns console width
|
Returns console width
|
||||||
|
|
||||||
|
>>> any((getConsoleWidth(), True))
|
||||||
|
True
|
||||||
"""
|
"""
|
||||||
|
|
||||||
width = None
|
width = None
|
||||||
|
@ -2434,6 +2476,9 @@ def initCommonOutputs():
|
||||||
def getFileItems(filename, commentPrefix='#', unicoded=True, lowercase=False, unique=False):
|
def getFileItems(filename, commentPrefix='#', unicoded=True, lowercase=False, unique=False):
|
||||||
"""
|
"""
|
||||||
Returns newline delimited items contained inside file
|
Returns newline delimited items contained inside file
|
||||||
|
|
||||||
|
>>> "SELECT" in getFileItems(paths.SQL_KEYWORDS)
|
||||||
|
True
|
||||||
"""
|
"""
|
||||||
|
|
||||||
retVal = list() if not unique else OrderedDict()
|
retVal = list() if not unique else OrderedDict()
|
||||||
|
@ -2540,8 +2585,8 @@ def goGoodSamaritan(prevValue, originalCharset):
|
||||||
|
|
||||||
def getPartRun(alias=True):
|
def getPartRun(alias=True):
|
||||||
"""
|
"""
|
||||||
Goes through call stack and finds constructs matching conf.dbmsHandler.*.
|
Goes through call stack and finds constructs matching
|
||||||
Returns it or its alias used in 'txt/common-outputs.txt'
|
conf.dbmsHandler.*. Returns it or its alias used in 'txt/common-outputs.txt'
|
||||||
"""
|
"""
|
||||||
|
|
||||||
retVal = None
|
retVal = None
|
||||||
|
@ -4734,7 +4779,7 @@ def serializeObject(object_):
|
||||||
"""
|
"""
|
||||||
Serializes given object
|
Serializes given object
|
||||||
|
|
||||||
>>> type(serializeObject([1, 2, 3, ('a', 'b')])) == six.binary_type
|
>>> type(serializeObject([1, 2, 3, ('a', 'b')])) == str
|
||||||
True
|
True
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -4964,6 +5009,14 @@ def decloakToTemp(filename):
|
||||||
>>> openFile(_, "rb", encoding=None).read().startswith(b'<%')
|
>>> openFile(_, "rb", encoding=None).read().startswith(b'<%')
|
||||||
True
|
True
|
||||||
>>> os.remove(_)
|
>>> os.remove(_)
|
||||||
|
>>> _ = decloakToTemp(os.path.join(paths.SQLMAP_SHELL_PATH, "backdoors", "backdoor.asp_"))
|
||||||
|
>>> openFile(_, "rb", encoding=None).read().startswith(b'<%')
|
||||||
|
True
|
||||||
|
>>> os.remove(_)
|
||||||
|
>>> _ = decloakToTemp(os.path.join(paths.SQLMAP_UDF_PATH, "postgresql", "linux", "64", "11", "lib_postgresqludf_sys.so_"))
|
||||||
|
>>> b'sys_eval' in openFile(_, "rb", encoding=None).read()
|
||||||
|
True
|
||||||
|
>>> os.remove(_)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
content = decloak(filename)
|
content = decloak(filename)
|
||||||
|
@ -4997,6 +5050,12 @@ def getRequestHeader(request, name):
|
||||||
Solving an issue with an urllib2 Request header case sensitivity
|
Solving an issue with an urllib2 Request header case sensitivity
|
||||||
|
|
||||||
# Reference: http://bugs.python.org/issue2275
|
# Reference: http://bugs.python.org/issue2275
|
||||||
|
|
||||||
|
>>> _ = lambda _: _
|
||||||
|
>>> _.headers = {"FOO": "BAR"}
|
||||||
|
>>> _.header_items = lambda: _.headers.items()
|
||||||
|
>>> getText(getRequestHeader(_, "foo"))
|
||||||
|
'BAR'
|
||||||
"""
|
"""
|
||||||
|
|
||||||
retVal = None
|
retVal = None
|
||||||
|
@ -5094,6 +5153,13 @@ def pollProcess(process, suppress_errors=False):
|
||||||
def parseRequestFile(reqFile, checkParams=True):
|
def parseRequestFile(reqFile, checkParams=True):
|
||||||
"""
|
"""
|
||||||
Parses WebScarab and Burp logs and adds results to the target URL list
|
Parses WebScarab and Burp logs and adds results to the target URL list
|
||||||
|
|
||||||
|
>>> handle, reqFile = tempfile.mkstemp(suffix=".req")
|
||||||
|
>>> content = b"POST / HTTP/1.0\\nUser-agent: foobar\\nHost: www.example.com\\n\\nid=1\\n"
|
||||||
|
>>> _ = os.write(handle, content)
|
||||||
|
>>> os.close(handle)
|
||||||
|
>>> next(parseRequestFile(reqFile)) == ('http://www.example.com:80/', 'POST', 'id=1', None, (('User-agent', 'foobar'), ('Host', 'www.example.com')))
|
||||||
|
True
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _parseWebScarabLog(content):
|
def _parseWebScarabLog(content):
|
||||||
|
@ -5236,7 +5302,7 @@ def parseRequestFile(reqFile, checkParams=True):
|
||||||
params = True
|
params = True
|
||||||
|
|
||||||
# Avoid proxy and connection type related headers
|
# Avoid proxy and connection type related headers
|
||||||
elif key not in (HTTP_HEADER.PROXY_CONNECTION, HTTP_HEADER.CONNECTION):
|
elif key not in (HTTP_HEADER.PROXY_CONNECTION, HTTP_HEADER.CONNECTION, HTTP_HEADER.IF_MODIFIED_SINCE, HTTP_HEADER.IF_NONE_MATCH):
|
||||||
headers.append((getUnicode(key), getUnicode(value)))
|
headers.append((getUnicode(key), getUnicode(value)))
|
||||||
|
|
||||||
if kb.customInjectionMark in re.sub(PROBLEMATIC_CUSTOM_INJECTION_PATTERNS, "", value or ""):
|
if kb.customInjectionMark in re.sub(PROBLEMATIC_CUSTOM_INJECTION_PATTERNS, "", value or ""):
|
||||||
|
|
|
@ -48,16 +48,16 @@ def base64pickle(value):
|
||||||
retVal = None
|
retVal = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
retVal = encodeBase64(pickle.dumps(value, PICKLE_PROTOCOL))
|
retVal = encodeBase64(pickle.dumps(value, PICKLE_PROTOCOL), binary=False)
|
||||||
except:
|
except:
|
||||||
warnMsg = "problem occurred while serializing "
|
warnMsg = "problem occurred while serializing "
|
||||||
warnMsg += "instance of a type '%s'" % type(value)
|
warnMsg += "instance of a type '%s'" % type(value)
|
||||||
singleTimeWarnMessage(warnMsg)
|
singleTimeWarnMessage(warnMsg)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
retVal = encodeBase64(pickle.dumps(value))
|
retVal = encodeBase64(pickle.dumps(value), binary=False)
|
||||||
except:
|
except:
|
||||||
retVal = encodeBase64(pickle.dumps(str(value), PICKLE_PROTOCOL))
|
retVal = encodeBase64(pickle.dumps(str(value), PICKLE_PROTOCOL), binary=False)
|
||||||
|
|
||||||
return retVal
|
return retVal
|
||||||
|
|
||||||
|
|
|
@ -239,6 +239,7 @@ class HTTP_HEADER(object):
|
||||||
EXPIRES = "Expires"
|
EXPIRES = "Expires"
|
||||||
HOST = "Host"
|
HOST = "Host"
|
||||||
IF_MODIFIED_SINCE = "If-Modified-Since"
|
IF_MODIFIED_SINCE = "If-Modified-Since"
|
||||||
|
IF_NONE_MATCH = "If-None-Match"
|
||||||
LAST_MODIFIED = "Last-Modified"
|
LAST_MODIFIED = "Last-Modified"
|
||||||
LOCATION = "Location"
|
LOCATION = "Location"
|
||||||
PRAGMA = "Pragma"
|
PRAGMA = "Pragma"
|
||||||
|
|
|
@ -825,7 +825,7 @@ def _setTamperingFunctions():
|
||||||
|
|
||||||
def _setPreprocessFunctions():
|
def _setPreprocessFunctions():
|
||||||
"""
|
"""
|
||||||
Loads preprocess functions from given script(s)
|
Loads preprocess function(s) from given script(s)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if conf.preprocess:
|
if conf.preprocess:
|
||||||
|
@ -870,17 +870,95 @@ def _setPreprocessFunctions():
|
||||||
raise SqlmapSyntaxException("cannot import preprocess module '%s' (%s)" % (getUnicode(filename[:-3]), getSafeExString(ex)))
|
raise SqlmapSyntaxException("cannot import preprocess module '%s' (%s)" % (getUnicode(filename[:-3]), getSafeExString(ex)))
|
||||||
|
|
||||||
for name, function in inspect.getmembers(module, inspect.isfunction):
|
for name, function in inspect.getmembers(module, inspect.isfunction):
|
||||||
if name == "preprocess" and inspect.getargspec(function).args and all(_ in inspect.getargspec(function).args for _ in ("page", "headers", "code")):
|
try:
|
||||||
|
if name == "preprocess" and inspect.getargspec(function).args and all(_ in inspect.getargspec(function).args for _ in ("req",)):
|
||||||
|
found = True
|
||||||
|
|
||||||
|
kb.preprocessFunctions.append(function)
|
||||||
|
function.__name__ = module.__name__
|
||||||
|
|
||||||
|
break
|
||||||
|
except ValueError: # Note: https://github.com/sqlmapproject/sqlmap/issues/4357
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not found:
|
||||||
|
errMsg = "missing function 'preprocess(req)' "
|
||||||
|
errMsg += "in preprocess script '%s'" % script
|
||||||
|
raise SqlmapGenericException(errMsg)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
function(_urllib.request.Request("http://localhost"))
|
||||||
|
except:
|
||||||
|
handle, filename = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.PREPROCESS, suffix=".py")
|
||||||
|
os.close(handle)
|
||||||
|
|
||||||
|
openFile(filename, "w+b").write("#!/usr/bin/env\n\ndef preprocess(req):\n pass\n")
|
||||||
|
openFile(os.path.join(os.path.dirname(filename), "__init__.py"), "w+b").write("pass")
|
||||||
|
|
||||||
|
errMsg = "function 'preprocess(req)' "
|
||||||
|
errMsg += "in preprocess script '%s' " % script
|
||||||
|
errMsg += "appears to be invalid "
|
||||||
|
errMsg += "(Note: find template script at '%s')" % filename
|
||||||
|
raise SqlmapGenericException(errMsg)
|
||||||
|
|
||||||
|
def _setPostprocessFunctions():
|
||||||
|
"""
|
||||||
|
Loads postprocess function(s) from given script(s)
|
||||||
|
"""
|
||||||
|
|
||||||
|
if conf.postprocess:
|
||||||
|
for script in re.split(PARAMETER_SPLITTING_REGEX, conf.postprocess):
|
||||||
|
found = False
|
||||||
|
function = None
|
||||||
|
|
||||||
|
script = safeFilepathEncode(script.strip())
|
||||||
|
|
||||||
|
try:
|
||||||
|
if not script:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not os.path.exists(script):
|
||||||
|
errMsg = "postprocess script '%s' does not exist" % script
|
||||||
|
raise SqlmapFilePathException(errMsg)
|
||||||
|
|
||||||
|
elif not script.endswith(".py"):
|
||||||
|
errMsg = "postprocess script '%s' should have an extension '.py'" % script
|
||||||
|
raise SqlmapSyntaxException(errMsg)
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
errMsg = "invalid character provided in option '--postprocess'"
|
||||||
|
raise SqlmapSyntaxException(errMsg)
|
||||||
|
|
||||||
|
dirname, filename = os.path.split(script)
|
||||||
|
dirname = os.path.abspath(dirname)
|
||||||
|
|
||||||
|
infoMsg = "loading postprocess module '%s'" % filename[:-3]
|
||||||
|
logger.info(infoMsg)
|
||||||
|
|
||||||
|
if not os.path.exists(os.path.join(dirname, "__init__.py")):
|
||||||
|
errMsg = "make sure that there is an empty file '__init__.py' "
|
||||||
|
errMsg += "inside of postprocess scripts directory '%s'" % dirname
|
||||||
|
raise SqlmapGenericException(errMsg)
|
||||||
|
|
||||||
|
if dirname not in sys.path:
|
||||||
|
sys.path.insert(0, dirname)
|
||||||
|
|
||||||
|
try:
|
||||||
|
module = __import__(safeFilepathEncode(filename[:-3]))
|
||||||
|
except Exception as ex:
|
||||||
|
raise SqlmapSyntaxException("cannot import postprocess module '%s' (%s)" % (getUnicode(filename[:-3]), getSafeExString(ex)))
|
||||||
|
|
||||||
|
for name, function in inspect.getmembers(module, inspect.isfunction):
|
||||||
|
if name == "postprocess" and inspect.getargspec(function).args and all(_ in inspect.getargspec(function).args for _ in ("page", "headers", "code")):
|
||||||
found = True
|
found = True
|
||||||
|
|
||||||
kb.preprocessFunctions.append(function)
|
kb.postprocessFunctions.append(function)
|
||||||
function.__name__ = module.__name__
|
function.__name__ = module.__name__
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
if not found:
|
if not found:
|
||||||
errMsg = "missing function 'preprocess(page, headers=None, code=None)' "
|
errMsg = "missing function 'postprocess(page, headers=None, code=None)' "
|
||||||
errMsg += "in preprocess script '%s'" % script
|
errMsg += "in postprocess script '%s'" % script
|
||||||
raise SqlmapGenericException(errMsg)
|
raise SqlmapGenericException(errMsg)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
|
@ -889,11 +967,11 @@ def _setPreprocessFunctions():
|
||||||
handle, filename = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.PREPROCESS, suffix=".py")
|
handle, filename = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.PREPROCESS, suffix=".py")
|
||||||
os.close(handle)
|
os.close(handle)
|
||||||
|
|
||||||
open(filename, "w+b").write("#!/usr/bin/env\n\ndef preprocess(page, headers=None, code=None):\n return page, headers, code\n")
|
openFile(filename, "w+b").write("#!/usr/bin/env\n\ndef postprocess(page, headers=None, code=None):\n return page, headers, code\n")
|
||||||
open(os.path.join(os.path.dirname(filename), "__init__.py"), "w+b").write("pass")
|
openFile(os.path.join(os.path.dirname(filename), "__init__.py"), "w+b").write("pass")
|
||||||
|
|
||||||
errMsg = "function 'preprocess(page, headers=None, code=None)' "
|
errMsg = "function 'postprocess(page, headers=None, code=None)' "
|
||||||
errMsg += "in preprocess script '%s' " % script
|
errMsg += "in postprocess script '%s' " % script
|
||||||
errMsg += "should return a tuple '(page, headers, code)' "
|
errMsg += "should return a tuple '(page, headers, code)' "
|
||||||
errMsg += "(Note: find template script at '%s')" % filename
|
errMsg += "(Note: find template script at '%s')" % filename
|
||||||
raise SqlmapGenericException(errMsg)
|
raise SqlmapGenericException(errMsg)
|
||||||
|
@ -1450,8 +1528,8 @@ def _createHomeDirectories():
|
||||||
if conf.get("purge"):
|
if conf.get("purge"):
|
||||||
return
|
return
|
||||||
|
|
||||||
for context in "output", "history":
|
for context in ("output", "history"):
|
||||||
directory = paths["SQLMAP_%s_PATH" % context.upper()]
|
directory = paths["SQLMAP_%s_PATH" % getUnicode(context).upper()] # NOTE: https://github.com/sqlmapproject/sqlmap/issues/4363
|
||||||
try:
|
try:
|
||||||
if not os.path.isdir(directory):
|
if not os.path.isdir(directory):
|
||||||
os.makedirs(directory)
|
os.makedirs(directory)
|
||||||
|
@ -1762,6 +1840,8 @@ def _cleanupOptions():
|
||||||
if not regex:
|
if not regex:
|
||||||
conf.exclude = re.sub(r"\s*,\s*", ',', conf.exclude)
|
conf.exclude = re.sub(r"\s*,\s*", ',', conf.exclude)
|
||||||
conf.exclude = r"\A%s\Z" % '|'.join(re.escape(_) for _ in conf.exclude.split(','))
|
conf.exclude = r"\A%s\Z" % '|'.join(re.escape(_) for _ in conf.exclude.split(','))
|
||||||
|
else:
|
||||||
|
conf.exclude = re.sub(r"(\w+)\$", r"\g<1>\$", conf.exclude)
|
||||||
|
|
||||||
if conf.binaryFields:
|
if conf.binaryFields:
|
||||||
conf.binaryFields = conf.binaryFields.replace(" ", "")
|
conf.binaryFields = conf.binaryFields.replace(" ", "")
|
||||||
|
@ -2009,10 +2089,11 @@ def _setKnowledgeBaseAttributes(flushAll=True):
|
||||||
kb.skipSeqMatcher = False
|
kb.skipSeqMatcher = False
|
||||||
kb.smokeMode = False
|
kb.smokeMode = False
|
||||||
kb.reduceTests = None
|
kb.reduceTests = None
|
||||||
kb.tlsSNI = {}
|
kb.sslSuccess = False
|
||||||
kb.stickyDBMS = False
|
kb.stickyDBMS = False
|
||||||
kb.storeHashesChoice = None
|
kb.storeHashesChoice = None
|
||||||
kb.suppressResumeInfo = False
|
kb.suppressResumeInfo = False
|
||||||
|
kb.tableExistsChoice = None
|
||||||
kb.tableFrom = None
|
kb.tableFrom = None
|
||||||
kb.technique = None
|
kb.technique = None
|
||||||
kb.tempDir = None
|
kb.tempDir = None
|
||||||
|
@ -2022,7 +2103,7 @@ def _setKnowledgeBaseAttributes(flushAll=True):
|
||||||
kb.testType = None
|
kb.testType = None
|
||||||
kb.threadContinue = True
|
kb.threadContinue = True
|
||||||
kb.threadException = False
|
kb.threadException = False
|
||||||
kb.tableExistsChoice = None
|
kb.tlsSNI = {}
|
||||||
kb.uChar = NULL
|
kb.uChar = NULL
|
||||||
kb.udfFail = False
|
kb.udfFail = False
|
||||||
kb.unionDuplicates = False
|
kb.unionDuplicates = False
|
||||||
|
@ -2037,6 +2118,7 @@ def _setKnowledgeBaseAttributes(flushAll=True):
|
||||||
kb.keywords = set(getFileItems(paths.SQL_KEYWORDS))
|
kb.keywords = set(getFileItems(paths.SQL_KEYWORDS))
|
||||||
kb.normalizeCrawlingChoice = None
|
kb.normalizeCrawlingChoice = None
|
||||||
kb.passwordMgr = None
|
kb.passwordMgr = None
|
||||||
|
kb.postprocessFunctions = []
|
||||||
kb.preprocessFunctions = []
|
kb.preprocessFunctions = []
|
||||||
kb.skipVulnHost = None
|
kb.skipVulnHost = None
|
||||||
kb.storeCrawlingChoice = None
|
kb.storeCrawlingChoice = None
|
||||||
|
@ -2683,6 +2765,7 @@ def init():
|
||||||
_listTamperingFunctions()
|
_listTamperingFunctions()
|
||||||
_setTamperingFunctions()
|
_setTamperingFunctions()
|
||||||
_setPreprocessFunctions()
|
_setPreprocessFunctions()
|
||||||
|
_setPostprocessFunctions()
|
||||||
_setTrafficOutputFP()
|
_setTrafficOutputFP()
|
||||||
_setupHTTPCollector()
|
_setupHTTPCollector()
|
||||||
_setHttpChunked()
|
_setHttpChunked()
|
||||||
|
|
|
@ -222,6 +222,7 @@ optDict = {
|
||||||
"hexConvert": "boolean",
|
"hexConvert": "boolean",
|
||||||
"outputDir": "string",
|
"outputDir": "string",
|
||||||
"parseErrors": "boolean",
|
"parseErrors": "boolean",
|
||||||
|
"postprocess": "string",
|
||||||
"preprocess": "string",
|
"preprocess": "string",
|
||||||
"repair": "boolean",
|
"repair": "boolean",
|
||||||
"saveConfig": "string",
|
"saveConfig": "string",
|
||||||
|
|
|
@ -6,6 +6,7 @@ See the file 'LICENSE' for copying permission
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import codecs
|
import codecs
|
||||||
|
import os
|
||||||
import random
|
import random
|
||||||
|
|
||||||
import lib.controller.checks
|
import lib.controller.checks
|
||||||
|
@ -76,6 +77,15 @@ def dirtyPatches():
|
||||||
# to prevent too much "guessing" in case of binary data retrieval
|
# to prevent too much "guessing" in case of binary data retrieval
|
||||||
thirdparty.chardet.universaldetector.MINIMUM_THRESHOLD = 0.90
|
thirdparty.chardet.universaldetector.MINIMUM_THRESHOLD = 0.90
|
||||||
|
|
||||||
|
# https://github.com/sqlmapproject/sqlmap/issues/4314
|
||||||
|
try:
|
||||||
|
os.urandom(1)
|
||||||
|
except NotImplemented:
|
||||||
|
if six.PY3:
|
||||||
|
os.urandom = lambda size: bytes(random.randint(0, 255) for _ in range(size))
|
||||||
|
else:
|
||||||
|
os.urandom = lambda size: "".join(chr(random.randint(0, 255)) for _ in xrange(size))
|
||||||
|
|
||||||
def resolveCrossReferences():
|
def resolveCrossReferences():
|
||||||
"""
|
"""
|
||||||
Place for cross-reference resolution
|
Place for cross-reference resolution
|
||||||
|
|
|
@ -18,7 +18,7 @@ from lib.core.enums import OS
|
||||||
from thirdparty.six import unichr as _unichr
|
from thirdparty.six import unichr as _unichr
|
||||||
|
|
||||||
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
|
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
|
||||||
VERSION = "1.4.8.9"
|
VERSION = "1.4.10.3"
|
||||||
TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable"
|
TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable"
|
||||||
TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34}
|
TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34}
|
||||||
VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE)
|
VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE)
|
||||||
|
@ -66,6 +66,7 @@ PARTIAL_HEX_VALUE_MARKER = "__PARTIAL_HEX_VALUE__"
|
||||||
URI_QUESTION_MARKER = "__QUESTION_MARK__"
|
URI_QUESTION_MARKER = "__QUESTION_MARK__"
|
||||||
ASTERISK_MARKER = "__ASTERISK_MARK__"
|
ASTERISK_MARKER = "__ASTERISK_MARK__"
|
||||||
REPLACEMENT_MARKER = "__REPLACEMENT_MARK__"
|
REPLACEMENT_MARKER = "__REPLACEMENT_MARK__"
|
||||||
|
BOUNDED_BASE64_MARKER = "__BOUNDED_BASE64_MARK__"
|
||||||
BOUNDED_INJECTION_MARKER = "__BOUNDED_INJECTION_MARK__"
|
BOUNDED_INJECTION_MARKER = "__BOUNDED_INJECTION_MARK__"
|
||||||
SAFE_VARIABLE_MARKER = "__SAFE__"
|
SAFE_VARIABLE_MARKER = "__SAFE__"
|
||||||
SAFE_HEX_MARKER = "__SAFE_HEX__"
|
SAFE_HEX_MARKER = "__SAFE_HEX__"
|
||||||
|
|
|
@ -111,7 +111,7 @@ def _setRequestParams():
|
||||||
def process(match, repl):
|
def process(match, repl):
|
||||||
retVal = match.group(0)
|
retVal = match.group(0)
|
||||||
|
|
||||||
if not (conf.testParameter and match.group("name") not in [removePostHintPrefix(_) for _ in conf.testParameter]) and match.group("name") == match.group("name").strip('\\'):
|
if not (conf.testParameter and match.group("name") not in (removePostHintPrefix(_) for _ in conf.testParameter)) and match.group("name") == match.group("name").strip('\\'):
|
||||||
retVal = repl
|
retVal = repl
|
||||||
while True:
|
while True:
|
||||||
_ = re.search(r"\\g<([^>]+)>", retVal)
|
_ = re.search(r"\\g<([^>]+)>", retVal)
|
||||||
|
|
|
@ -44,10 +44,13 @@ def vulnTest():
|
||||||
(u"-c <config> --flush-session --roles --statements --hostname --privileges --sql-query=\"SELECT '\u0161u\u0107uraj'\" --technique=U", (u": '\u0161u\u0107uraj'", "on SQLite it is not possible")),
|
(u"-c <config> --flush-session --roles --statements --hostname --privileges --sql-query=\"SELECT '\u0161u\u0107uraj'\" --technique=U", (u": '\u0161u\u0107uraj'", "on SQLite it is not possible")),
|
||||||
(u"-u <url> --flush-session --sql-query=\"SELECT '\u0161u\u0107uraj'\" --technique=B --no-escape --string=luther --unstable", (u": '\u0161u\u0107uraj'",)),
|
(u"-u <url> --flush-session --sql-query=\"SELECT '\u0161u\u0107uraj'\" --technique=B --no-escape --string=luther --unstable", (u": '\u0161u\u0107uraj'",)),
|
||||||
("--dummy", ("all tested parameters do not appear to be injectable", "does not seem to be injectable", "there is not at least one", "~might be injectable")),
|
("--dummy", ("all tested parameters do not appear to be injectable", "does not seem to be injectable", "there is not at least one", "~might be injectable")),
|
||||||
|
("-u '<url>&id2=1' -p id2 -v 5 --flush-session --level=5 --test-filter='AND boolean-based blind - WHERE or HAVING clause (MySQL comment)'", ("~1AND",)),
|
||||||
("--list-tampers", ("between", "MySQL", "xforwardedfor")),
|
("--list-tampers", ("between", "MySQL", "xforwardedfor")),
|
||||||
("-r <request> --flush-session -v 5 --test-skip='heavy' --save=<tmp>", ("CloudFlare", "possible DBMS: 'SQLite'", "User-agent: foobar", "~Type: time-based blind")),
|
("-r <request> --flush-session -v 5 --test-skip='heavy' --save=<tmp>", ("CloudFlare", "possible DBMS: 'SQLite'", "User-agent: foobar", "~Type: time-based blind")),
|
||||||
("-l <log> --flush-session --keep-alive --skip-waf -v 5 --technique=U --union-from=users --banner --parse-errors", ("banner: '3.", "ORDER BY term out of range", "~xp_cmdshell", "Connection: keep-alive")),
|
("-l <log> --flush-session --keep-alive --skip-waf -v 5 --technique=U --union-from=users --banner --parse-errors", ("banner: '3.", "ORDER BY term out of range", "~xp_cmdshell", "Connection: keep-alive")),
|
||||||
("-l <log> --offline --banner -v 5", ("banner: '3.", "~[TRAFFIC OUT]")),
|
("-l <log> --offline --banner -v 5", ("banner: '3.", "~[TRAFFIC OUT]")),
|
||||||
|
("-u <base64> -p id --base64=id --data='base64=true' --flush-session --banner --technique=B", ("banner: '3.",)),
|
||||||
|
("-u <base64> -p id --base64=id --data='base64=true' --flush-session --tables --technique=U", (" users ",)),
|
||||||
("-u <url> --flush-session --banner --technique=B --not-string 'no results'", ("banner: '3.",)),
|
("-u <url> --flush-session --banner --technique=B --not-string 'no results'", ("banner: '3.",)),
|
||||||
("-u <url> --flush-session --banner --technique=B --first=1 --last=2", ("banner: '3.'",)),
|
("-u <url> --flush-session --banner --technique=B --first=1 --last=2", ("banner: '3.'",)),
|
||||||
("-u <url> --flush-session --encoding=ascii --forms --crawl=2 --threads=2 --banner", ("total of 2 targets", "might be injectable", "Type: UNION query", "banner: '3.")),
|
("-u <url> --flush-session --encoding=ascii --forms --crawl=2 --threads=2 --banner", ("total of 2 targets", "might be injectable", "Type: UNION query", "banner: '3.")),
|
||||||
|
@ -125,7 +128,10 @@ def vulnTest():
|
||||||
status = '%d/%d (%d%%) ' % (count, len(TESTS), round(100.0 * count / len(TESTS)))
|
status = '%d/%d (%d%%) ' % (count, len(TESTS), round(100.0 * count / len(TESTS)))
|
||||||
dataToStdout("\r[%s] [INFO] complete: %s" % (time.strftime("%X"), status))
|
dataToStdout("\r[%s] [INFO] complete: %s" % (time.strftime("%X"), status))
|
||||||
|
|
||||||
cmd = "%s %s %s --batch --non-interactive" % (sys.executable, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "sqlmap.py")), options.replace("<url>", url).replace("<direct>", direct).replace("<request>", request).replace("<log>", log).replace("<config>", config))
|
for tag, value in (("<url>", url), ("<direct>", direct), ("<request>", request), ("<log>", log), ("<config>", config), ("<base64>", url.replace("id=1", "id=MZ=%3d"))):
|
||||||
|
options = options.replace(tag, value)
|
||||||
|
|
||||||
|
cmd = "%s %s %s --batch --non-interactive" % (sys.executable, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "sqlmap.py")), options)
|
||||||
|
|
||||||
if "<tmp>" in cmd:
|
if "<tmp>" in cmd:
|
||||||
handle, tmp = tempfile.mkstemp()
|
handle, tmp = tempfile.mkstemp()
|
||||||
|
|
|
@ -623,7 +623,7 @@ def cmdLineParser(argv=None):
|
||||||
help="Parameter(s) containing Base64 encoded data")
|
help="Parameter(s) containing Base64 encoded data")
|
||||||
|
|
||||||
general.add_argument("--base64-safe", dest="base64Safe", action="store_true",
|
general.add_argument("--base64-safe", dest="base64Safe", action="store_true",
|
||||||
help="Use URL and filename safe Base64 alphabet")
|
help="Use URL and filename safe Base64 alphabet (RFC 4648)")
|
||||||
|
|
||||||
general.add_argument("--batch", dest="batch", action="store_true",
|
general.add_argument("--batch", dest="batch", action="store_true",
|
||||||
help="Never ask for user input, use the default behavior")
|
help="Never ask for user input, use the default behavior")
|
||||||
|
@ -683,7 +683,10 @@ def cmdLineParser(argv=None):
|
||||||
help="Parse and display DBMS error messages from responses")
|
help="Parse and display DBMS error messages from responses")
|
||||||
|
|
||||||
general.add_argument("--preprocess", dest="preprocess",
|
general.add_argument("--preprocess", dest="preprocess",
|
||||||
help="Use given script(s) for preprocessing of response data")
|
help="Use given script(s) for preprocessing (request)")
|
||||||
|
|
||||||
|
general.add_argument("--postprocess", dest="postprocess",
|
||||||
|
help="Use given script(s) for postprocessing (response)")
|
||||||
|
|
||||||
general.add_argument("--repair", dest="repair", action="store_true",
|
general.add_argument("--repair", dest="repair", action="store_true",
|
||||||
help="Redump entries having unknown character marker (%s)" % INFERENCE_UNKNOWN_CHAR)
|
help="Redump entries having unknown character marker (%s)" % INFERENCE_UNKNOWN_CHAR)
|
||||||
|
@ -863,7 +866,7 @@ def cmdLineParser(argv=None):
|
||||||
_ = []
|
_ = []
|
||||||
advancedHelp = True
|
advancedHelp = True
|
||||||
extraHeaders = []
|
extraHeaders = []
|
||||||
tamperIndex = None
|
auxIndexes = {}
|
||||||
|
|
||||||
# Reference: https://stackoverflow.com/a/4012683 (Note: previously used "...sys.getfilesystemencoding() or UNICODE_ENCODING")
|
# Reference: https://stackoverflow.com/a/4012683 (Note: previously used "...sys.getfilesystemencoding() or UNICODE_ENCODING")
|
||||||
for arg in argv:
|
for arg in argv:
|
||||||
|
@ -952,14 +955,20 @@ def cmdLineParser(argv=None):
|
||||||
argv[i] = ""
|
argv[i] = ""
|
||||||
elif argv[i] in DEPRECATED_OPTIONS:
|
elif argv[i] in DEPRECATED_OPTIONS:
|
||||||
argv[i] = ""
|
argv[i] = ""
|
||||||
elif argv[i].startswith("--tamper"):
|
elif any(argv[i].startswith(_) for _ in ("--tamper", "--ignore-code", "--skip")):
|
||||||
if tamperIndex is None:
|
key = re.search(r"\-?\-(\w+)\b", argv[i]).group(1)
|
||||||
tamperIndex = i if '=' in argv[i] else (i + 1 if i + 1 < len(argv) and not argv[i + 1].startswith('-') else None)
|
index = auxIndexes.get(key, None)
|
||||||
|
if index is None:
|
||||||
|
index = i if '=' in argv[i] else (i + 1 if i + 1 < len(argv) and not argv[i + 1].startswith('-') else None)
|
||||||
|
auxIndexes[key] = index
|
||||||
else:
|
else:
|
||||||
argv[tamperIndex] = "%s,%s" % (argv[tamperIndex], argv[i].split('=')[1] if '=' in argv[i] else (argv[i + 1] if i + 1 < len(argv) and not argv[i + 1].startswith('-') else ""))
|
delimiter = ','
|
||||||
|
argv[index] = "%s%s%s" % (argv[index], delimiter, argv[i].split('=')[1] if '=' in argv[i] else (argv[i + 1] if i + 1 < len(argv) and not argv[i + 1].startswith('-') else ""))
|
||||||
argv[i] = ""
|
argv[i] = ""
|
||||||
elif argv[i] in ("-H", "--header"):
|
elif argv[i] in ("-H", "--header") or any(argv[i].startswith("%s=" % _) for _ in ("-H", "--header")):
|
||||||
if i + 1 < len(argv):
|
if '=' in argv[i]:
|
||||||
|
extraHeaders.append(argv[i].split('=', 1)[1])
|
||||||
|
elif i + 1 < len(argv):
|
||||||
extraHeaders.append(argv[i + 1])
|
extraHeaders.append(argv[i + 1])
|
||||||
elif argv[i] == "--deps":
|
elif argv[i] == "--deps":
|
||||||
argv[i] = "--dependencies"
|
argv[i] = "--dependencies"
|
||||||
|
@ -997,7 +1006,7 @@ def cmdLineParser(argv=None):
|
||||||
for verbosity in (_ for _ in argv if re.search(r"\A\-v+\Z", _)):
|
for verbosity in (_ for _ in argv if re.search(r"\A\-v+\Z", _)):
|
||||||
try:
|
try:
|
||||||
if argv.index(verbosity) == len(argv) - 1 or not argv[argv.index(verbosity) + 1].isdigit():
|
if argv.index(verbosity) == len(argv) - 1 or not argv[argv.index(verbosity) + 1].isdigit():
|
||||||
conf.verbose = verbosity.count('v') + 1
|
conf.verbose = verbosity.count('v')
|
||||||
del argv[argv.index(verbosity)]
|
del argv[argv.index(verbosity)]
|
||||||
except (IndexError, ValueError):
|
except (IndexError, ValueError):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -353,7 +353,7 @@ def decodePage(page, contentEncoding, contentType, percentDecode=True):
|
||||||
|
|
||||||
if (kb.pageEncoding or "").lower() == "utf-8-sig":
|
if (kb.pageEncoding or "").lower() == "utf-8-sig":
|
||||||
kb.pageEncoding = "utf-8"
|
kb.pageEncoding = "utf-8"
|
||||||
if page and page.startswith("\xef\xbb\xbf"): # Reference: https://docs.python.org/2/library/codecs.html (Note: noticed problems when "utf-8-sig" is left to Python for handling)
|
if page and page.startswith(b"\xef\xbb\xbf"): # Reference: https://docs.python.org/2/library/codecs.html (Note: noticed problems when "utf-8-sig" is left to Python for handling)
|
||||||
page = page[3:]
|
page = page[3:]
|
||||||
|
|
||||||
page = getUnicode(page, kb.pageEncoding)
|
page = getUnicode(page, kb.pageEncoding)
|
||||||
|
@ -394,7 +394,7 @@ def processResponse(page, responseHeaders, code=None, status=None):
|
||||||
if msg:
|
if msg:
|
||||||
logger.warning("parsed DBMS error message: '%s'" % msg.rstrip('.'))
|
logger.warning("parsed DBMS error message: '%s'" % msg.rstrip('.'))
|
||||||
|
|
||||||
if kb.processResponseCounter < IDENTYWAF_PARSE_LIMIT:
|
if not conf.skipWaf and kb.processResponseCounter < IDENTYWAF_PARSE_LIMIT:
|
||||||
rawResponse = "%s %s %s\n%s\n%s" % (_http_client.HTTPConnection._http_vsn_str, code or "", status or "", getUnicode("".join(responseHeaders.headers if responseHeaders else [])), page)
|
rawResponse = "%s %s %s\n%s\n%s" % (_http_client.HTTPConnection._http_vsn_str, code or "", status or "", getUnicode("".join(responseHeaders.headers if responseHeaders else [])), page)
|
||||||
|
|
||||||
identYwaf.non_blind.clear()
|
identYwaf.non_blind.clear()
|
||||||
|
|
|
@ -501,6 +501,16 @@ class Connect(object):
|
||||||
else:
|
else:
|
||||||
return None, None, None
|
return None, None, None
|
||||||
|
|
||||||
|
for function in kb.preprocessFunctions:
|
||||||
|
try:
|
||||||
|
function(req)
|
||||||
|
except Exception as ex:
|
||||||
|
errMsg = "error occurred while running preprocess "
|
||||||
|
errMsg += "function '%s' ('%s')" % (function.__name__, getSafeExString(ex))
|
||||||
|
raise SqlmapGenericException(errMsg)
|
||||||
|
else:
|
||||||
|
post, headers = req.data, req.headers
|
||||||
|
|
||||||
requestHeaders += "\r\n".join(["%s: %s" % (getUnicode(key.capitalize() if hasattr(key, "capitalize") else key), getUnicode(value)) for (key, value) in req.header_items()])
|
requestHeaders += "\r\n".join(["%s: %s" % (getUnicode(key.capitalize() if hasattr(key, "capitalize") else key), getUnicode(value)) for (key, value) in req.header_items()])
|
||||||
|
|
||||||
if not getRequestHeader(req, HTTP_HEADER.COOKIE) and conf.cj:
|
if not getRequestHeader(req, HTTP_HEADER.COOKIE) and conf.cj:
|
||||||
|
@ -539,7 +549,7 @@ class Connect(object):
|
||||||
conn = _urllib.request.urlopen(req)
|
conn = _urllib.request.urlopen(req)
|
||||||
|
|
||||||
if not kb.authHeader and getRequestHeader(req, HTTP_HEADER.AUTHORIZATION) and (conf.authType or "").lower() == AUTH_TYPE.BASIC.lower():
|
if not kb.authHeader and getRequestHeader(req, HTTP_HEADER.AUTHORIZATION) and (conf.authType or "").lower() == AUTH_TYPE.BASIC.lower():
|
||||||
kb.authHeader = getRequestHeader(req, HTTP_HEADER.AUTHORIZATION)
|
kb.authHeader = getUnicode(getRequestHeader(req, HTTP_HEADER.AUTHORIZATION))
|
||||||
|
|
||||||
if not kb.proxyAuthHeader and getRequestHeader(req, HTTP_HEADER.PROXY_AUTHORIZATION):
|
if not kb.proxyAuthHeader and getRequestHeader(req, HTTP_HEADER.PROXY_AUTHORIZATION):
|
||||||
kb.proxyAuthHeader = getRequestHeader(req, HTTP_HEADER.PROXY_AUTHORIZATION)
|
kb.proxyAuthHeader = getRequestHeader(req, HTTP_HEADER.PROXY_AUTHORIZATION)
|
||||||
|
@ -815,11 +825,11 @@ class Connect(object):
|
||||||
else:
|
else:
|
||||||
page = getUnicode(page)
|
page = getUnicode(page)
|
||||||
|
|
||||||
for function in kb.preprocessFunctions:
|
for function in kb.postprocessFunctions:
|
||||||
try:
|
try:
|
||||||
page, responseHeaders, code = function(page, responseHeaders, code)
|
page, responseHeaders, code = function(page, responseHeaders, code)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
errMsg = "error occurred while running preprocess "
|
errMsg = "error occurred while running postprocess "
|
||||||
errMsg += "function '%s' ('%s')" % (function.__name__, getSafeExString(ex))
|
errMsg += "function '%s' ('%s')" % (function.__name__, getSafeExString(ex))
|
||||||
raise SqlmapGenericException(errMsg)
|
raise SqlmapGenericException(errMsg)
|
||||||
|
|
||||||
|
@ -1089,6 +1099,9 @@ class Connect(object):
|
||||||
if not match:
|
if not match:
|
||||||
match = re.search(r"\b(?P<name>%s)\s*=\s*['\"]?(?P<value>[^;'\"]+)" % conf.csrfToken, page or "", re.I)
|
match = re.search(r"\b(?P<name>%s)\s*=\s*['\"]?(?P<value>[^;'\"]+)" % conf.csrfToken, page or "", re.I)
|
||||||
|
|
||||||
|
if not match:
|
||||||
|
match = re.search(r"<meta\s+name=[\"']?(?P<name>%s)[\"']?[^>]+\b(value|content)=[\"']?(?P<value>[^>\"']+)" % conf.csrfToken, page or "", re.I)
|
||||||
|
|
||||||
if match:
|
if match:
|
||||||
token.name, token.value = match.group("name"), match.group("value")
|
token.name, token.value = match.group("name"), match.group("value")
|
||||||
|
|
||||||
|
@ -1131,7 +1144,7 @@ class Connect(object):
|
||||||
uri = _adjustParameter(uri, token.name, token.value)
|
uri = _adjustParameter(uri, token.name, token.value)
|
||||||
elif candidate == PLACE.GET and get:
|
elif candidate == PLACE.GET and get:
|
||||||
get = _adjustParameter(get, token.name, token.value)
|
get = _adjustParameter(get, token.name, token.value)
|
||||||
elif candidate in [PLACE.POST, PLACE.CUSTOM_POST] and post:
|
elif candidate in (PLACE.POST, PLACE.CUSTOM_POST) and post:
|
||||||
post = _adjustParameter(post, token.name, token.value)
|
post = _adjustParameter(post, token.name, token.value)
|
||||||
|
|
||||||
for i in xrange(len(conf.httpHeaders)):
|
for i in xrange(len(conf.httpHeaders)):
|
||||||
|
|
|
@ -11,6 +11,8 @@ import socket
|
||||||
|
|
||||||
from lib.core.common import filterNone
|
from lib.core.common import filterNone
|
||||||
from lib.core.common import getSafeExString
|
from lib.core.common import getSafeExString
|
||||||
|
from lib.core.compat import xrange
|
||||||
|
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
|
||||||
|
@ -43,6 +45,8 @@ class HTTPSConnection(_http_client.HTTPSConnection):
|
||||||
_contexts[None] = ssl._create_default_https_context()
|
_contexts[None] = ssl._create_default_https_context()
|
||||||
kwargs["context"] = _contexts[None]
|
kwargs["context"] = _contexts[None]
|
||||||
|
|
||||||
|
self.retrying = False
|
||||||
|
|
||||||
_http_client.HTTPSConnection.__init__(self, *args, **kwargs)
|
_http_client.HTTPSConnection.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
|
@ -58,7 +62,7 @@ class HTTPSConnection(_http_client.HTTPSConnection):
|
||||||
# Reference(s): https://docs.python.org/2/library/ssl.html#ssl.SSLContext
|
# Reference(s): https://docs.python.org/2/library/ssl.html#ssl.SSLContext
|
||||||
# https://www.mnot.net/blog/2014/12/27/python_2_and_tls_sni
|
# https://www.mnot.net/blog/2014/12/27/python_2_and_tls_sni
|
||||||
if re.search(r"\A[\d.]+\Z", self.host) is None and kb.tlsSNI.get(self.host) is not False and hasattr(ssl, "SSLContext"):
|
if re.search(r"\A[\d.]+\Z", self.host) is None and kb.tlsSNI.get(self.host) is not False and hasattr(ssl, "SSLContext"):
|
||||||
for protocol in [_ for _ in _protocols if _ >= ssl.PROTOCOL_TLSv1]:
|
for protocol in (_ for _ in _protocols if _ >= ssl.PROTOCOL_TLSv1):
|
||||||
try:
|
try:
|
||||||
sock = create_sock()
|
sock = create_sock()
|
||||||
if protocol not in _contexts:
|
if protocol not in _contexts:
|
||||||
|
@ -101,7 +105,21 @@ class HTTPSConnection(_http_client.HTTPSConnection):
|
||||||
# Reference: https://docs.python.org/2/library/ssl.html
|
# Reference: https://docs.python.org/2/library/ssl.html
|
||||||
if distutils.version.LooseVersion(PYVERSION) < distutils.version.LooseVersion("2.7.9"):
|
if distutils.version.LooseVersion(PYVERSION) < distutils.version.LooseVersion("2.7.9"):
|
||||||
errMsg += " (please retry with Python >= 2.7.9)"
|
errMsg += " (please retry with Python >= 2.7.9)"
|
||||||
|
|
||||||
|
if kb.sslSuccess and not self.retrying:
|
||||||
|
self.retrying = True
|
||||||
|
|
||||||
|
for _ in xrange(conf.retries):
|
||||||
|
try:
|
||||||
|
self.connect()
|
||||||
|
except SqlmapConnectionException:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
raise SqlmapConnectionException(errMsg)
|
raise SqlmapConnectionException(errMsg)
|
||||||
|
else:
|
||||||
|
kb.sslSuccess = True
|
||||||
|
|
||||||
class HTTPSHandler(_urllib.request.HTTPSHandler):
|
class HTTPSHandler(_urllib.request.HTTPSHandler):
|
||||||
def https_open(self, req):
|
def https_open(self, req):
|
||||||
|
|
|
@ -160,7 +160,7 @@ class SmartRedirectHandler(_urllib.request.HTTPRedirectHandler):
|
||||||
if not hasattr(result, "read"):
|
if not hasattr(result, "read"):
|
||||||
def _(self, length=None):
|
def _(self, length=None):
|
||||||
try:
|
try:
|
||||||
retVal = getSafeExString(ex)
|
retVal = getSafeExString(ex) # Note: pyflakes mistakenly marks 'ex' as undefined (NOTE: tested in both Python2 and Python3)
|
||||||
except:
|
except:
|
||||||
retVal = ""
|
retVal = ""
|
||||||
return retVal
|
return retVal
|
||||||
|
|
|
@ -1147,6 +1147,12 @@ def dictionaryAttack(attack_dict):
|
||||||
warnMsg = "user aborted during dictionary-based attack phase (Ctrl+C was pressed)"
|
warnMsg = "user aborted during dictionary-based attack phase (Ctrl+C was pressed)"
|
||||||
logger.warn(warnMsg)
|
logger.warn(warnMsg)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
if _multiprocessing:
|
||||||
|
gc.enable()
|
||||||
|
|
||||||
|
# NOTE: https://github.com/sqlmapproject/sqlmap/issues/4367
|
||||||
|
# NOTE: https://dzone.com/articles/python-101-creating-multiple-processes
|
||||||
for process in processes:
|
for process in processes:
|
||||||
try:
|
try:
|
||||||
process.terminate()
|
process.terminate()
|
||||||
|
@ -1154,10 +1160,6 @@ def dictionaryAttack(attack_dict):
|
||||||
except (OSError, AttributeError):
|
except (OSError, AttributeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
finally:
|
|
||||||
if _multiprocessing:
|
|
||||||
gc.enable()
|
|
||||||
|
|
||||||
if retVal:
|
if retVal:
|
||||||
if conf.hashDB:
|
if conf.hashDB:
|
||||||
conf.hashDB.beginTransaction()
|
conf.hashDB.beginTransaction()
|
||||||
|
|
|
@ -525,6 +525,9 @@ class Databases(object):
|
||||||
else:
|
else:
|
||||||
return kb.data.cachedColumns
|
return kb.data.cachedColumns
|
||||||
|
|
||||||
|
if conf.exclude:
|
||||||
|
tblList = [_ for _ in tblList if re.search(conf.exclude, _, re.I) is None]
|
||||||
|
|
||||||
tblList = filterNone(safeSQLIdentificatorNaming(_, True) for _ in tblList)
|
tblList = filterNone(safeSQLIdentificatorNaming(_, True) for _ in tblList)
|
||||||
|
|
||||||
if bruteForce is None:
|
if bruteForce is None:
|
||||||
|
|
|
@ -410,9 +410,11 @@ class Search(object):
|
||||||
|
|
||||||
if tblCond:
|
if tblCond:
|
||||||
if conf.tbl:
|
if conf.tbl:
|
||||||
_ = conf.tbl.split(',')
|
tbls = conf.tbl.split(',')
|
||||||
whereTblsQuery = " AND (" + " OR ".join("%s = '%s'" % (tblCond, unsafeSQLIdentificatorNaming(tbl)) for tbl in _) + ")"
|
if conf.exclude:
|
||||||
infoMsgTbl = " for table%s '%s'" % ("s" if len(_) > 1 else "", ", ".join(unsafeSQLIdentificatorNaming(tbl) for tbl in _))
|
tbls = [_ for _ in tbls if re.search(conf.exclude, _, re.I) is None]
|
||||||
|
whereTblsQuery = " AND (" + " OR ".join("%s = '%s'" % (tblCond, unsafeSQLIdentificatorNaming(tbl)) for tbl in tbls) + ")"
|
||||||
|
infoMsgTbl = " for table%s '%s'" % ("s" if len(tbls) > 1 else "", ", ".join(unsafeSQLIdentificatorNaming(tbl) for tbl in tbls))
|
||||||
|
|
||||||
if conf.db == CURRENT_DB:
|
if conf.db == CURRENT_DB:
|
||||||
conf.db = self.getCurrentDb()
|
conf.db = self.getCurrentDb()
|
||||||
|
|
|
@ -617,7 +617,8 @@ class Users(object):
|
||||||
|
|
||||||
# In Informix we get one letter for the highest privilege
|
# In Informix we get one letter for the highest privilege
|
||||||
elif Backend.isDbms(DBMS.INFORMIX):
|
elif Backend.isDbms(DBMS.INFORMIX):
|
||||||
privileges.add(INFORMIX_PRIVS[privilege.strip()])
|
if privilege.strip() in INFORMIX_PRIVS:
|
||||||
|
privileges.add(INFORMIX_PRIVS[privilege.strip()])
|
||||||
|
|
||||||
# In DB2 we get Y or G if the privilege is
|
# In DB2 we get Y or G if the privilege is
|
||||||
# True, N otherwise
|
# True, N otherwise
|
||||||
|
|
|
@ -769,9 +769,12 @@ outputDir =
|
||||||
# Valid: True or False
|
# Valid: True or False
|
||||||
parseErrors = False
|
parseErrors = False
|
||||||
|
|
||||||
# Use given script(s) for preprocessing of response data.
|
# Use given script(s) for preprocessing of request.
|
||||||
preprocess =
|
preprocess =
|
||||||
|
|
||||||
|
# Use given script(s) for postprocessing of response data.
|
||||||
|
postprocess =
|
||||||
|
|
||||||
# Redump entries having unknown character marker (?).
|
# Redump entries having unknown character marker (?).
|
||||||
# Valid: True or False
|
# Valid: True or False
|
||||||
repair = False
|
repair = False
|
||||||
|
|
|
@ -317,7 +317,7 @@ def main():
|
||||||
logger.critical(errMsg)
|
logger.critical(errMsg)
|
||||||
raise SystemExit
|
raise SystemExit
|
||||||
|
|
||||||
elif any(_ in excMsg for _ in ("tempfile.mkdtemp", "tempfile.mkstemp")):
|
elif any(_ in excMsg for _ in ("tempfile.mkdtemp", "tempfile.mkstemp", "tempfile.py")):
|
||||||
errMsg = "unable to write to the temporary directory '%s'. " % tempfile.gettempdir()
|
errMsg = "unable to write to the temporary directory '%s'. " % tempfile.gettempdir()
|
||||||
errMsg += "Please make sure that your disk is not full and "
|
errMsg += "Please make sure that your disk is not full and "
|
||||||
errMsg += "that you have sufficient write permissions to "
|
errMsg += "that you have sufficient write permissions to "
|
||||||
|
@ -428,6 +428,12 @@ def main():
|
||||||
logger.critical(errMsg)
|
logger.critical(errMsg)
|
||||||
raise SystemExit
|
raise SystemExit
|
||||||
|
|
||||||
|
elif all(_ in excMsg for _ in ("HTTPNtlmAuthHandler", "'str' object has no attribute 'decode'")):
|
||||||
|
errMsg = "package 'python-ntlm' has a known compatibility issue with the "
|
||||||
|
errMsg += "Python 3 (Reference: https://github.com/mullender/python-ntlm/pull/61)"
|
||||||
|
logger.critical(errMsg)
|
||||||
|
raise SystemExit
|
||||||
|
|
||||||
elif "'DictObject' object has no attribute '" in excMsg and all(_ in errMsg for _ in ("(fingerprinted)", "(identified)")):
|
elif "'DictObject' object has no attribute '" in excMsg and all(_ in errMsg for _ in ("(fingerprinted)", "(identified)")):
|
||||||
errMsg = "there has been a problem in enumeration. "
|
errMsg = "there has been a problem in enumeration. "
|
||||||
errMsg += "Because of a considerable chance of false-positive case "
|
errMsg += "Because of a considerable chance of false-positive case "
|
||||||
|
|
|
@ -29,4 +29,4 @@ def tamper(payload, **kwargs):
|
||||||
'1e0UNION ALL SELECT'
|
'1e0UNION ALL SELECT'
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return re.sub("(\d+)\s+(UNION )", r"\g<1>e0\g<2>", payload, re.I) if payload else payload
|
return re.sub(r"(\d+)\s+(UNION )", r"\g<1>e0\g<2>", payload, re.I) if payload else payload
|
||||||
|
|
|
@ -31,4 +31,4 @@ def tamper(payload, **kwargs):
|
||||||
'1DUNION ALL SELECT'
|
'1DUNION ALL SELECT'
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return re.sub("(\d+)\s+(UNION )", r"\g<1>D\g<2>", payload, re.I) if payload else payload
|
return re.sub(r"(\d+)\s+(UNION )", r"\g<1>D\g<2>", payload, re.I) if payload else payload
|
||||||
|
|
|
@ -5,6 +5,7 @@ Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/)
|
||||||
See the file 'LICENSE' for copying permission
|
See the file 'LICENSE' for copying permission
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from lib.core.common import singleTimeWarnMessage
|
from lib.core.common import singleTimeWarnMessage
|
||||||
|
|
|
@ -33,4 +33,4 @@ def tamper(payload, **kwargs):
|
||||||
'1"-.1UNION ALL SELECT'
|
'1"-.1UNION ALL SELECT'
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return re.sub("\s+(UNION )", r"-.1\g<1>", payload, re.I) if payload else payload
|
return re.sub(r"\s+(UNION )", r"-.1\g<1>", payload, re.I) if payload else payload
|
||||||
|
|
|
@ -16,7 +16,7 @@ def dependencies():
|
||||||
|
|
||||||
def tamper(payload, **kwargs):
|
def tamper(payload, **kwargs):
|
||||||
"""
|
"""
|
||||||
Replaces instances of <int> UNION with <int>e0UNION
|
Splits FROM schema identifiers (e.g. 'testdb.users') with whitespace (e.g. 'testdb 9.e.users')
|
||||||
|
|
||||||
Requirement:
|
Requirement:
|
||||||
* MySQL
|
* MySQL
|
||||||
|
@ -28,4 +28,4 @@ def tamper(payload, **kwargs):
|
||||||
'SELECT id FROM testdb 9.e.users'
|
'SELECT id FROM testdb 9.e.users'
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return re.sub("( FROM \w+)\.(\w+)", r"\g<1> 9.e.\g<2>", payload, re.I) if payload else payload
|
return re.sub(r"( FROM \w+)\.(\w+)", r"\g<1> 9.e.\g<2>", payload, re.I) if payload else payload
|
||||||
|
|
39
tamper/sleep2getlock.py
Normal file
39
tamper/sleep2getlock.py
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/)
|
||||||
|
See the file 'LICENSE' for copying permission
|
||||||
|
"""
|
||||||
|
|
||||||
|
from lib.core.data import kb
|
||||||
|
from lib.core.enums import PRIORITY
|
||||||
|
|
||||||
|
__priority__ = PRIORITY.HIGHEST
|
||||||
|
|
||||||
|
def dependencies():
|
||||||
|
pass
|
||||||
|
|
||||||
|
def tamper(payload, **kwargs):
|
||||||
|
"""
|
||||||
|
Replaces instances like 'SLEEP(5)' with (e.g.) "GET_LOCK('ETgP',5)"
|
||||||
|
|
||||||
|
Requirement:
|
||||||
|
* MySQL
|
||||||
|
|
||||||
|
Tested against:
|
||||||
|
* MySQL 5.0 and 5.5
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
* Useful to bypass very weak and bespoke web application firewalls
|
||||||
|
that filter the SLEEP() and BENCHMARK() functions
|
||||||
|
|
||||||
|
* Reference: https://zhuanlan.zhihu.com/p/35245598
|
||||||
|
|
||||||
|
>>> tamper('SLEEP(5)') == "GET_LOCK('%s',5)" % kb.aliasName
|
||||||
|
True
|
||||||
|
"""
|
||||||
|
|
||||||
|
if payload:
|
||||||
|
payload = payload.replace("SLEEP(", "GET_LOCK('%s'," % kb.aliasName)
|
||||||
|
|
||||||
|
return payload
|
88
thirdparty/six/__init__.py
vendored
88
thirdparty/six/__init__.py
vendored
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2010-2018 Benjamin Peterson
|
# Copyright (c) 2010-2020 Benjamin Peterson
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -29,7 +29,7 @@ import sys
|
||||||
import types
|
import types
|
||||||
|
|
||||||
__author__ = "Benjamin Peterson <benjamin@python.org>"
|
__author__ = "Benjamin Peterson <benjamin@python.org>"
|
||||||
__version__ = "1.12.0"
|
__version__ = "1.15.0"
|
||||||
|
|
||||||
|
|
||||||
# Useful for very coarse version differentiation.
|
# Useful for very coarse version differentiation.
|
||||||
|
@ -255,9 +255,11 @@ _moved_attributes = [
|
||||||
MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"),
|
MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"),
|
||||||
MovedModule("builtins", "__builtin__"),
|
MovedModule("builtins", "__builtin__"),
|
||||||
MovedModule("configparser", "ConfigParser"),
|
MovedModule("configparser", "ConfigParser"),
|
||||||
|
MovedModule("collections_abc", "collections", "collections.abc" if sys.version_info >= (3, 3) else "collections"),
|
||||||
MovedModule("copyreg", "copy_reg"),
|
MovedModule("copyreg", "copy_reg"),
|
||||||
MovedModule("dbm_gnu", "gdbm", "dbm.gnu"),
|
MovedModule("dbm_gnu", "gdbm", "dbm.gnu"),
|
||||||
MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"),
|
MovedModule("dbm_ndbm", "dbm", "dbm.ndbm"),
|
||||||
|
MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread" if sys.version_info < (3, 9) else "_thread"),
|
||||||
MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
|
MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
|
||||||
MovedModule("http_cookies", "Cookie", "http.cookies"),
|
MovedModule("http_cookies", "Cookie", "http.cookies"),
|
||||||
MovedModule("html_entities", "htmlentitydefs", "html.entities"),
|
MovedModule("html_entities", "htmlentitydefs", "html.entities"),
|
||||||
|
@ -637,13 +639,16 @@ if PY3:
|
||||||
import io
|
import io
|
||||||
StringIO = io.StringIO
|
StringIO = io.StringIO
|
||||||
BytesIO = io.BytesIO
|
BytesIO = io.BytesIO
|
||||||
|
del io
|
||||||
_assertCountEqual = "assertCountEqual"
|
_assertCountEqual = "assertCountEqual"
|
||||||
if sys.version_info[1] <= 1:
|
if sys.version_info[1] <= 1:
|
||||||
_assertRaisesRegex = "assertRaisesRegexp"
|
_assertRaisesRegex = "assertRaisesRegexp"
|
||||||
_assertRegex = "assertRegexpMatches"
|
_assertRegex = "assertRegexpMatches"
|
||||||
|
_assertNotRegex = "assertNotRegexpMatches"
|
||||||
else:
|
else:
|
||||||
_assertRaisesRegex = "assertRaisesRegex"
|
_assertRaisesRegex = "assertRaisesRegex"
|
||||||
_assertRegex = "assertRegex"
|
_assertRegex = "assertRegex"
|
||||||
|
_assertNotRegex = "assertNotRegex"
|
||||||
else:
|
else:
|
||||||
def b(s):
|
def b(s):
|
||||||
return s
|
return s
|
||||||
|
@ -665,6 +670,7 @@ else:
|
||||||
_assertCountEqual = "assertItemsEqual"
|
_assertCountEqual = "assertItemsEqual"
|
||||||
_assertRaisesRegex = "assertRaisesRegexp"
|
_assertRaisesRegex = "assertRaisesRegexp"
|
||||||
_assertRegex = "assertRegexpMatches"
|
_assertRegex = "assertRegexpMatches"
|
||||||
|
_assertNotRegex = "assertNotRegexpMatches"
|
||||||
_add_doc(b, """Byte literal""")
|
_add_doc(b, """Byte literal""")
|
||||||
_add_doc(u, """Text literal""")
|
_add_doc(u, """Text literal""")
|
||||||
|
|
||||||
|
@ -681,6 +687,10 @@ def assertRegex(self, *args, **kwargs):
|
||||||
return getattr(self, _assertRegex)(*args, **kwargs)
|
return getattr(self, _assertRegex)(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def assertNotRegex(self, *args, **kwargs):
|
||||||
|
return getattr(self, _assertNotRegex)(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
if PY3:
|
if PY3:
|
||||||
exec_ = getattr(moves.builtins, "exec")
|
exec_ = getattr(moves.builtins, "exec")
|
||||||
|
|
||||||
|
@ -716,16 +726,7 @@ else:
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
|
||||||
if sys.version_info[:2] == (3, 2):
|
if sys.version_info[:2] > (3,):
|
||||||
exec_("""def raise_from(value, from_value):
|
|
||||||
try:
|
|
||||||
if from_value is None:
|
|
||||||
raise value
|
|
||||||
raise value from from_value
|
|
||||||
finally:
|
|
||||||
value = None
|
|
||||||
""")
|
|
||||||
elif sys.version_info[:2] > (3, 2):
|
|
||||||
exec_("""def raise_from(value, from_value):
|
exec_("""def raise_from(value, from_value):
|
||||||
try:
|
try:
|
||||||
raise value from from_value
|
raise value from from_value
|
||||||
|
@ -805,13 +806,33 @@ if sys.version_info[:2] < (3, 3):
|
||||||
_add_doc(reraise, """Reraise an exception.""")
|
_add_doc(reraise, """Reraise an exception.""")
|
||||||
|
|
||||||
if sys.version_info[0:2] < (3, 4):
|
if sys.version_info[0:2] < (3, 4):
|
||||||
|
# This does exactly the same what the :func:`py3:functools.update_wrapper`
|
||||||
|
# function does on Python versions after 3.2. It sets the ``__wrapped__``
|
||||||
|
# attribute on ``wrapper`` object and it doesn't raise an error if any of
|
||||||
|
# the attributes mentioned in ``assigned`` and ``updated`` are missing on
|
||||||
|
# ``wrapped`` object.
|
||||||
|
def _update_wrapper(wrapper, wrapped,
|
||||||
|
assigned=functools.WRAPPER_ASSIGNMENTS,
|
||||||
|
updated=functools.WRAPPER_UPDATES):
|
||||||
|
for attr in assigned:
|
||||||
|
try:
|
||||||
|
value = getattr(wrapped, attr)
|
||||||
|
except AttributeError:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
setattr(wrapper, attr, value)
|
||||||
|
for attr in updated:
|
||||||
|
getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
|
||||||
|
wrapper.__wrapped__ = wrapped
|
||||||
|
return wrapper
|
||||||
|
_update_wrapper.__doc__ = functools.update_wrapper.__doc__
|
||||||
|
|
||||||
def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS,
|
def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS,
|
||||||
updated=functools.WRAPPER_UPDATES):
|
updated=functools.WRAPPER_UPDATES):
|
||||||
def wrapper(f):
|
return functools.partial(_update_wrapper, wrapped=wrapped,
|
||||||
f = functools.wraps(wrapped, assigned, updated)(f)
|
assigned=assigned, updated=updated)
|
||||||
f.__wrapped__ = wrapped
|
wraps.__doc__ = functools.wraps.__doc__
|
||||||
return f
|
|
||||||
return wrapper
|
|
||||||
else:
|
else:
|
||||||
wraps = functools.wraps
|
wraps = functools.wraps
|
||||||
|
|
||||||
|
@ -824,7 +845,15 @@ def with_metaclass(meta, *bases):
|
||||||
class metaclass(type):
|
class metaclass(type):
|
||||||
|
|
||||||
def __new__(cls, name, this_bases, d):
|
def __new__(cls, name, this_bases, d):
|
||||||
return meta(name, bases, d)
|
if sys.version_info[:2] >= (3, 7):
|
||||||
|
# This version introduced PEP 560 that requires a bit
|
||||||
|
# of extra care (we mimic what is done by __build_class__).
|
||||||
|
resolved_bases = types.resolve_bases(bases)
|
||||||
|
if resolved_bases is not bases:
|
||||||
|
d['__orig_bases__'] = bases
|
||||||
|
else:
|
||||||
|
resolved_bases = bases
|
||||||
|
return meta(name, resolved_bases, d)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def __prepare__(cls, name, this_bases):
|
def __prepare__(cls, name, this_bases):
|
||||||
|
@ -861,12 +890,11 @@ def ensure_binary(s, encoding='utf-8', errors='strict'):
|
||||||
- `str` -> encoded to `bytes`
|
- `str` -> encoded to `bytes`
|
||||||
- `bytes` -> `bytes`
|
- `bytes` -> `bytes`
|
||||||
"""
|
"""
|
||||||
|
if isinstance(s, binary_type):
|
||||||
|
return s
|
||||||
if isinstance(s, text_type):
|
if isinstance(s, text_type):
|
||||||
return s.encode(encoding, errors)
|
return s.encode(encoding, errors)
|
||||||
elif isinstance(s, binary_type):
|
raise TypeError("not expecting type '%s'" % type(s))
|
||||||
return s
|
|
||||||
else:
|
|
||||||
raise TypeError("not expecting type '%s'" % type(s))
|
|
||||||
|
|
||||||
|
|
||||||
def ensure_str(s, encoding='utf-8', errors='strict'):
|
def ensure_str(s, encoding='utf-8', errors='strict'):
|
||||||
|
@ -880,12 +908,15 @@ def ensure_str(s, encoding='utf-8', errors='strict'):
|
||||||
- `str` -> `str`
|
- `str` -> `str`
|
||||||
- `bytes` -> decoded to `str`
|
- `bytes` -> decoded to `str`
|
||||||
"""
|
"""
|
||||||
if not isinstance(s, (text_type, binary_type)):
|
# Optimization: Fast return for the common case.
|
||||||
raise TypeError("not expecting type '%s'" % type(s))
|
if type(s) is str:
|
||||||
|
return s
|
||||||
if PY2 and isinstance(s, text_type):
|
if PY2 and isinstance(s, text_type):
|
||||||
s = s.encode(encoding, errors)
|
return s.encode(encoding, errors)
|
||||||
elif PY3 and isinstance(s, binary_type):
|
elif PY3 and isinstance(s, binary_type):
|
||||||
s = s.decode(encoding, errors)
|
return s.decode(encoding, errors)
|
||||||
|
elif not isinstance(s, (text_type, binary_type)):
|
||||||
|
raise TypeError("not expecting type '%s'" % type(s))
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
@ -908,10 +939,9 @@ def ensure_text(s, encoding='utf-8', errors='strict'):
|
||||||
raise TypeError("not expecting type '%s'" % type(s))
|
raise TypeError("not expecting type '%s'" % type(s))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def python_2_unicode_compatible(klass):
|
def python_2_unicode_compatible(klass):
|
||||||
"""
|
"""
|
||||||
A decorator that defines __unicode__ and __str__ methods under Python 2.
|
A class decorator that defines __unicode__ and __str__ methods under Python 2.
|
||||||
Under Python 3 it does nothing.
|
Under Python 3 it does nothing.
|
||||||
|
|
||||||
To support Python 2 and 3 with a single code base, define a __str__ method
|
To support Python 2 and 3 with a single code base, define a __str__ method
|
||||||
|
|
Loading…
Reference in New Issue
Block a user