Compare commits

...

51 Commits

Author SHA1 Message Date
Miroslav Stampar
210a4c3a0a Fixes #4363 2020-10-05 11:35:49 +02:00
Miroslav Stampar
15225668d0 Somebody was fooling around (Issue #4357) 2020-09-28 13:12:59 +02:00
Miroslav Stampar
c1bf36b876 Better alternative 2020-09-24 14:57:45 +02:00
Miroslav Stampar
229f89004b Fixes #4355 2020-09-24 14:55:13 +02:00
Miroslav Stampar
443b1f2ed5 ORDER BY required 2020-09-24 14:54:59 +02:00
Miroslav Stampar
60f4520020 Minor update for #4353 2020-09-23 15:29:28 +02:00
Miroslav Stampar
7460b87f1d Update for #4353 2020-09-23 15:22:07 +02:00
Miroslav Stampar
5d08b9004e Minor update 2020-09-21 17:11:11 +02:00
Miroslav Stampar
c2b9e539ae Update for #4351 2020-09-21 17:04:54 +02:00
HerendraTJ
3d8eb62a59 Issue Tracker --> Pelacak Masalah. (#4347) 2020-09-18 11:58:29 +02:00
Miroslav Stampar
d51e45fd34 Minor update for #4344 2020-09-17 15:26:06 +02:00
Miroslav Stampar
3258e29cf9 Update for #4344 2020-09-17 15:22:50 +02:00
antichown
e0ea1ab5e9 new tamper script (#4344)
* new tamper script

works with time-based queries

* Update sleepgetlock.py

Co-authored-by: Miroslav Stampar <miroslav@sqlmap.org>
2020-09-17 15:06:47 +02:00
Miroslav Stampar
192ca02c41 Minor update (more intuitive) 2020-09-16 14:28:32 +02:00
Miroslav Stampar
f0bbbb0918 Fixes #4341 2020-09-11 16:28:10 +02:00
Miroslav Stampar
f6857d4ee4 Bug fix (304 not modified as original response) 2020-09-11 14:32:25 +02:00
Miroslav Stampar
a1342e04a5 Minor update 2020-09-10 16:34:01 +02:00
Miroslav Stampar
7963281c41 Minor update 2020-09-10 16:20:12 +02:00
Miroslav Stampar
715063f0d4 Patching session PY2<->PY3 incompatibility issue 2020-09-09 16:15:23 +02:00
Miroslav Stampar
1658331810 Trivial update 2020-09-09 14:07:13 +02:00
Miroslav Stampar
bfe93e20c5 Patch for #4337 2020-09-09 13:58:26 +02:00
Miroslav Stampar
bcea050f22 Fixes #4331 2020-09-06 23:32:47 +02:00
Miroslav Stampar
c4a692abe3 Patch for #4332 2020-09-06 23:21:12 +02:00
Miroslav Stampar
b42b62ae38 Major improvement in Base64 handling (late-binding) 2020-09-04 13:16:50 +02:00
Miroslav Stampar
a7f20c1d67 Minor update (base64 stuff) 2020-09-04 12:45:33 +02:00
Miroslav Stampar
f781367ac1 Fixes #4328 2020-09-04 10:49:17 +02:00
mkauschi
1bec3a953c fix #4325 (#4327)
Co-authored-by: manuel <manuel@crashtest-security.com>
2020-09-02 17:07:28 +02:00
Miroslav Stampar
66e07dfab6 Fixes #4322 2020-09-01 15:35:14 +02:00
Miroslav Stampar
226d467f6d Fixes #4321 2020-08-31 22:06:22 +02:00
Miroslav Stampar
ea5ae44b6c Minor improvement 2020-08-31 11:55:14 +02:00
Miroslav Stampar
95b9a47c6f Adding support for easier 'decloaking' (AV something something) 2020-08-31 11:34:12 +02:00
Miroslav Stampar
e05f65628d Minor update 2020-08-31 11:18:29 +02:00
Miroslav Stampar
609545176f Minor refactoring 2020-08-28 14:46:59 +02:00
Miroslav Stampar
8de4820b24 Minor update 2020-08-28 14:24:43 +02:00
Miroslav Stampar
df5fabbbbb Adding couple of doctests 2020-08-24 11:10:13 +02:00
Miroslav Stampar
0c48d0dbec Minor update on request 2020-08-23 22:11:24 +02:00
Miroslav Stampar
5108c2d06c Minor update regarding #4312 2020-08-23 21:16:56 +02:00
Miroslav Stampar
603d602550 Fixes #4313 2020-08-23 20:59:10 +02:00
Miroslav Stampar
907786edb8 Patch for #4314 2020-08-23 20:56:22 +02:00
Miroslav Stampar
85b73f872e Minor patch 2020-08-20 13:54:52 +02:00
Miroslav Stampar
a42ec7d9cb Trivial refactoring 2020-08-13 16:22:09 +02:00
tree-chtsec
b3f4c6d0fc Make asterisk work with --csrf-token option (#4305) 2020-08-13 16:18:31 +02:00
Miroslav Stampar
cec65f3a27 Adding new tamper script 2020-08-12 09:50:04 +02:00
Miroslav Stampar
cc79ae69aa Fixes #4303 2020-08-11 15:09:23 +02:00
Miroslav Stampar
5a9dc15cf2 Introduction of --base64-safe 2020-08-10 22:26:03 +02:00
Miroslav Stampar
f1fd080ba5 Minor improvement 2020-08-10 21:54:58 +02:00
Miroslav Stampar
cfe9fb4f5b Fixes #4301 2020-08-10 21:27:38 +02:00
Miroslav Stampar
7a55c9c145 Trivial update 2020-08-10 21:26:37 +02:00
Miroslav Stampar
4077a359f4 Fixes #4294 2020-08-05 22:43:32 +02:00
Miroslav Stampar
435fd49f1d Trivial update 2020-08-04 10:34:18 +02:00
Miroslav Stampar
bcfd9c3f48 Trivial update 2020-08-04 10:27:52 +02:00
83 changed files with 524 additions and 115 deletions

View File

@@ -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.

View File

@@ -1,2 +1,2 @@
:ЫХ&%╦С_m⌠D╗в?Е▓>и╠gI╛ё╠/З∙щб" ▄FщFчRs.k п⌡dЮa┌XЬ[°┘╧Юбlс5╖ZШйв ∙⌠▄% Eч:+b≈м┬m╫Eк ×<Ùñ\Íh<C38D>†i•å1ãŽUPÍ¡²=¤¹è“^R~ƒlâtÇ3q-yÄü®5RžÊ^°A™YŸ—çÌ÷F:£B¨pÄøÎj—<6A>ž{'$ˆJ\m½ò, ¿FÙTÙ6áânHñFºÿ© uµÁvλ|UQPÈT"bÃ_/FøóÕª¸·4¡v]SµiCp8Z˜ {Ì€ƒbõ<62>œ A9 FFÖj/åÌ“x\uý«pŽgH×&d?ô~³çiY¸f"ê§Ÿ¦b‰³¦EÕq#dëMø#%£Ñ§m¸<6D>£Ê®H¡zw#Vá iÁé6ÍjÁèdPå«]'§ºÃÇ
У╟╢?Чl┘м╟lw╤с(Б▌-Б&_Бk{╒],T╕ъЮШНгC╝\ba╛ik@bL6E ХztФ©╠{■▐[дO╟]I~IlИX6└ .щё²EЦS_б≥iОeKеxH2n╒дХCf┼CХ╓█ЬN╪БП5╒~ [ы,З 7ЩЩ=Ж;хЙ╛Ю+°Qюxt}zтq?╠·9ГUСЯSВУ>hvЯй╢М5gДW÷S=Н▓%√t"dЮ╒с╢wОnALg╛б▀ё$вО│ЁУ│э│ф Sок_ЕyJ╔Y┌Ч2У╫]╜;K;╚╞"√²6py|╙╡╟Х'*┌ ²)+.K┌у,R╬d(~U·│Ы6г9└▌b+аLi╪Ш©x °Т`АЩ┘╧X┤>~н╙zE÷√НЪИР┤gХ└├╓f·NбК╠P{═▀$▌▀─e%< ╒З╫У╩Sвв├┌$2#├╟P{╞╞Ф]╧├Ио└*⌠@°╔ж]╪kE Uý6<EFBFBD>P/°¾Ç"Rïˆì†nŽlB^9€÷ÚõT à«ŒÇ˜½ƒÅê 0™»(êS>x¼8<1C>ìlÙˆ ûK<41”<31>;÷Ÿ5b'PŸãæ˜P%¨¯0J,H<>9èw'Tj¬˜˜4Ä+Ú¢/3L[¶CCƒ(âÞzŽªLÔ¬ÓÉ/ˆAü³Gœ„°Óܣџ‹¬&–ÿMÐÊÇ„-Ðü+¤´²'?üÖˆB£Ë$Ø.è´ã&O׿ <>ÔDØ„êÝÚ¦ÊÕ¡R(ƒY\

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -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"/>

View File

@@ -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>

View File

@@ -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/`.

View File

@@ -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

View File

@@ -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)

View File

@@ -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.

16
extra/shutils/recloak.sh Executable file
View 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

View File

@@ -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"

View File

@@ -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 "

View File

@@ -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,8 +184,12 @@ 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) newValue = "%s%s%s" % (BOUNDED_BASE64_MARKER, newValue, BOUNDED_BASE64_MARKER)
origValue = encodeBase64(origValue, binary=False, encoding=conf.encoding or UNICODE_ENCODING)
if parameter in kb.base64Originals:
origValue = kb.base64Originals[parameter]
else:
origValue = encodeBase64(origValue, binary=False, encoding=conf.encoding or UNICODE_ENCODING)
if place in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER): if place in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER):
_ = "%s%s" % (origValue, kb.customInjectionMark) _ = "%s%s" % (origValue, kb.customInjectionMark)
@@ -393,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, "'")
@@ -1198,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("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)

View File

@@ -631,7 +631,8 @@ def paramToDict(place, parameters=None):
if parameter in (conf.base64Parameter or []): if parameter in (conf.base64Parameter or []):
try: try:
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:
@@ -1348,7 +1359,7 @@ def banner():
if not any(_ in sys.argv for _ in ("--version", "--api")) and not conf.get("disableBanner"): if not any(_ in sys.argv for _ in ("--version", "--api")) and not conf.get("disableBanner"):
result = BANNER result = BANNER
if not IS_TTY or "--disable-coloring" in sys.argv: if not IS_TTY or any(_ in sys.argv for _ in ("--disable-coloring", "--disable-colouring")):
result = clearColors(result) result = clearColors(result)
elif IS_WIN: elif IS_WIN:
coloramainit() coloramainit()
@@ -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
@@ -2220,6 +2245,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 +2262,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 +2471,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 +2580,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 +4774,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 +5004,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 +5045,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 +5148,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 +5297,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 ""):

View File

@@ -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
@@ -198,6 +198,8 @@ def decodeBase64(value, binary=True, encoding=None):
True True
>>> decodeBase64("MTIz", binary=False) >>> decodeBase64("MTIz", binary=False)
'123' '123'
>>> decodeBase64("A-B_CDE") == decodeBase64("A+B/CDE")
True
>>> decodeBase64(b"MTIzNA") == b"1234" >>> decodeBase64(b"MTIzNA") == b"1234"
True True
>>> decodeBase64("MTIzNA") == b"1234" >>> decodeBase64("MTIzNA") == b"1234"
@@ -206,12 +208,22 @@ def decodeBase64(value, binary=True, encoding=None):
True True
""" """
if value is None:
return None
padding = b'=' if isinstance(value, bytes) else '=' padding = b'=' if isinstance(value, bytes) else '='
# Reference: https://stackoverflow.com/a/49459036 # Reference: https://stackoverflow.com/a/49459036
if not value.endswith(padding): if not value.endswith(padding):
value += 3 * padding value += 3 * padding
# Reference: https://en.wikipedia.org/wiki/Base64#URL_applications
# Reference: https://perldoc.perl.org/MIME/Base64.html
if isinstance(value, bytes):
value = value.replace(b'-', b'+').replace(b'_', b'/')
else:
value = value.replace('-', '+').replace('_', '/')
retVal = base64.b64decode(value) retVal = base64.b64decode(value)
if not binary: if not binary:
@@ -219,16 +231,23 @@ def decodeBase64(value, binary=True, encoding=None):
return retVal return retVal
def encodeBase64(value, binary=True, encoding=None): def encodeBase64(value, binary=True, encoding=None, padding=True, safe=False):
""" """
Returns a decoded representation of provided Base64 value Returns a decoded representation of provided Base64 value
>>> encodeBase64(b"123") == b"MTIz" >>> encodeBase64(b"123") == b"MTIz"
True True
>>> encodeBase64(u"123", binary=False) >>> encodeBase64(u"1234", binary=False)
'MTIz' 'MTIzNA=='
>>> encodeBase64(u"1234", binary=False, padding=False)
'MTIzNA'
>>> encodeBase64(decodeBase64("A-B_CDE"), binary=False, safe=True)
'A-B_CDE'
""" """
if value is None:
return None
if isinstance(value, six.text_type): if isinstance(value, six.text_type):
value = value.encode(encoding or UNICODE_ENCODING) value = value.encode(encoding or UNICODE_ENCODING)
@@ -237,6 +256,19 @@ def encodeBase64(value, binary=True, encoding=None):
if not binary: if not binary:
retVal = getText(retVal, encoding) retVal = getText(retVal, encoding)
if safe:
padding = False
# Reference: https://en.wikipedia.org/wiki/Base64#URL_applications
# Reference: https://perldoc.perl.org/MIME/Base64.html
if isinstance(retVal, bytes):
retVal = retVal.replace(b'+', b'-').replace(b'/', b'_')
else:
retVal = retVal.replace('+', '-').replace('/', '_')
if not padding:
retVal = retVal.rstrip(b'=' if isinstance(retVal, bytes) else '=')
return retVal return retVal
def getBytes(value, encoding=None, errors="strict", unsafe=True): def getBytes(value, encoding=None, errors="strict", unsafe=True):

View File

@@ -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"

View File

@@ -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(" ", "")
@@ -1856,6 +1936,7 @@ def _setKnowledgeBaseAttributes(flushAll=True):
kb.arch = None kb.arch = None
kb.authHeader = None kb.authHeader = None
kb.bannerFp = AttribDict() kb.bannerFp = AttribDict()
kb.base64Originals = {}
kb.binaryField = False kb.binaryField = False
kb.browserVerification = None kb.browserVerification = None
@@ -2008,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
@@ -2021,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
@@ -2036,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
@@ -2682,6 +2765,7 @@ def init():
_listTamperingFunctions() _listTamperingFunctions()
_setTamperingFunctions() _setTamperingFunctions()
_setPreprocessFunctions() _setPreprocessFunctions()
_setPostprocessFunctions()
_setTrafficOutputFP() _setTrafficOutputFP()
_setupHTTPCollector() _setupHTTPCollector()
_setHttpChunked() _setHttpChunked()

View File

@@ -203,6 +203,7 @@ optDict = {
"answers": "string", "answers": "string",
"batch": "boolean", "batch": "boolean",
"base64Parameter": "string", "base64Parameter": "string",
"base64Safe": "boolean",
"binaryFields": "string", "binaryFields": "string",
"charset": "string", "charset": "string",
"checkInternet": "boolean", "checkInternet": "boolean",
@@ -221,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",

View File

@@ -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

View File

@@ -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.0" VERSION = "1.4.10.0"
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__"

View File

@@ -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)
@@ -400,7 +400,7 @@ def _setRequestParams():
raise SqlmapGenericException(errMsg) raise SqlmapGenericException(errMsg)
if conf.csrfToken: if conf.csrfToken:
if not any(re.search(conf.csrfToken, ' '.join(_), re.I) for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}), conf.paramDict.get(PLACE.COOKIE, {}))) and not re.search(r"\b%s\b" % conf.csrfToken, conf.data or "") and conf.csrfToken not in set(_[0].lower() for _ in conf.httpHeaders) and conf.csrfToken not in conf.paramDict.get(PLACE.COOKIE, {}): if not any(re.search(conf.csrfToken, ' '.join(_), re.I) for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}), conf.paramDict.get(PLACE.COOKIE, {}))) and not re.search(r"\b%s\b" % conf.csrfToken, conf.data or "") and conf.csrfToken not in set(_[0].lower() for _ in conf.httpHeaders) and conf.csrfToken not in conf.paramDict.get(PLACE.COOKIE, {}) and not all(re.search(conf.csrfToken, _, re.I) for _ in conf.paramDict.get(PLACE.URI, {}).values()):
errMsg = "anti-CSRF token parameter '%s' not " % conf.csrfToken._original errMsg = "anti-CSRF token parameter '%s' not " % conf.csrfToken._original
errMsg += "found in provided GET, POST, Cookie or header values" errMsg += "found in provided GET, POST, Cookie or header values"
raise SqlmapGenericException(errMsg) raise SqlmapGenericException(errMsg)

View File

@@ -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()

View File

@@ -622,6 +622,9 @@ def cmdLineParser(argv=None):
general.add_argument("--base64", dest="base64Parameter", general.add_argument("--base64", dest="base64Parameter",
help="Parameter(s) containing Base64 encoded data") help="Parameter(s) containing Base64 encoded data")
general.add_argument("--base64-safe", dest="base64Safe", action="store_true",
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")
@@ -680,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)
@@ -860,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:
@@ -949,17 +955,25 @@ 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"
elif argv[i] == "--disable-colouring":
argv[i] = "--disable-coloring"
elif argv[i] == "-r": elif argv[i] == "-r":
for j in xrange(i + 2, len(argv)): for j in xrange(i + 2, len(argv)):
value = argv[j] value = argv[j]
@@ -992,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

View File

@@ -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()

View File

@@ -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")
@@ -1125,11 +1138,13 @@ class Connect(object):
if token: if token:
token.value = token.value.strip("'\"") token.value = token.value.strip("'\"")
for candidate in (PLACE.GET, PLACE.POST): for candidate in (PLACE.GET, PLACE.POST, PLACE.CUSTOM_POST, PLACE.URI):
if candidate in conf.parameters: if candidate in conf.parameters:
if candidate == PLACE.GET and get: if candidate == PLACE.URI and uri:
uri = _adjustParameter(uri, token.name, token.value)
elif candidate == PLACE.GET and get:
get = _adjustParameter(get, token.name, token.value) get = _adjustParameter(get, token.name, token.value)
elif candidate == PLACE.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)):

View File

@@ -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):

View File

@@ -153,14 +153,14 @@ class SmartRedirectHandler(_urllib.request.HTTPRedirectHandler):
result.info() result.info()
except AttributeError: except AttributeError:
def _(self): def _(self):
return getattr(self, "hdrs") or {} return getattr(self, "hdrs", {})
result.info = types.MethodType(_, result) result.info = types.MethodType(_, result)
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

View File

@@ -7,6 +7,7 @@ See the file 'LICENSE' for copying permission
import os import os
from lib.core.common import openFile
from lib.core.common import randomStr from lib.core.common import randomStr
from lib.core.data import conf from lib.core.data import conf
from lib.core.data import logger from lib.core.data import logger
@@ -48,7 +49,7 @@ class Registry(object):
) )
def _createLocalBatchFile(self): def _createLocalBatchFile(self):
self._batPathFp = open(self._batPathLocal, "w") self._batPathFp = openFile(self._batPathLocal, "w")
if self._operation == REGISTRY_OPERATION.READ: if self._operation == REGISTRY_OPERATION.READ:
lines = self._batRead lines = self._batRead

View File

@@ -61,6 +61,7 @@ from thirdparty.bottle.bottle import request
from thirdparty.bottle.bottle import response from thirdparty.bottle.bottle import response
from thirdparty.bottle.bottle import run from thirdparty.bottle.bottle import run
from thirdparty.bottle.bottle import server_names from thirdparty.bottle.bottle import server_names
from thirdparty import six
from thirdparty.six.moves import http_client as _http_client from thirdparty.six.moves import http_client as _http_client
from thirdparty.six.moves import input as _input from thirdparty.six.moves import input as _input
from thirdparty.six.moves import urllib as _urllib from thirdparty.six.moves import urllib as _urllib
@@ -717,7 +718,7 @@ def server(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, adapter=REST
errMsg += "List of supported adapters: %s" % ', '.join(sorted(list(server_names.keys()))) errMsg += "List of supported adapters: %s" % ', '.join(sorted(list(server_names.keys())))
else: else:
errMsg = "Server support for adapter '%s' is not installed on this system " % adapter errMsg = "Server support for adapter '%s' is not installed on this system " % adapter
errMsg += "(Note: you can try to install it with 'sudo apt install python-%s' or 'sudo pip install %s')" % (adapter, adapter) errMsg += "(Note: you can try to install it with 'sudo apt install python-%s' or 'sudo pip%s install %s')" % (adapter, '3' if six.PY3 else "", adapter)
logger.critical(errMsg) logger.critical(errMsg)
def _client(url, options=None): def _client(url, options=None):

View File

@@ -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:

View File

@@ -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()

View File

@@ -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

View File

@@ -699,6 +699,10 @@ answers =
# Parameter(s) containing Base64 encoded data # Parameter(s) containing Base64 encoded data
base64Parameter = base64Parameter =
# Use URL and filename safe Base64 alphabet (Reference: https://en.wikipedia.org/wiki/Base64#URL_applications).
# Valid: True or False
base64Safe = False
# Never ask for user input, use the default behaviour. # Never ask for user input, use the default behaviour.
# Valid: True or False # Valid: True or False
batch = False batch = False
@@ -765,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

View File

@@ -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 "

View File

@@ -5,14 +5,17 @@ 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.enums import DBMS
from lib.core.enums import PRIORITY from lib.core.enums import PRIORITY
__priority__ = PRIORITY.HIGHEST __priority__ = PRIORITY.HIGHEST
def dependencies(): def dependencies():
pass singleTimeWarnMessage("tamper script '%s' is only meant to be run against %s" % (os.path.basename(__file__).split(".")[0], DBMS.ORACLE))
def tamper(payload, **kwargs): def tamper(payload, **kwargs):
""" """

View File

@@ -5,17 +5,14 @@ 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.enums import DBMS
from lib.core.enums import PRIORITY from lib.core.enums import PRIORITY
__priority__ = PRIORITY.HIGHEST __priority__ = PRIORITY.HIGHEST
def dependencies(): def dependencies():
singleTimeWarnMessage("tamper script '%s' is unlikely to work against %s" % (os.path.basename(__file__).split(".")[0], DBMS.PGSQL)) pass
def tamper(payload, **kwargs): def tamper(payload, **kwargs):
""" """

37
tamper/equaltorlike.py Normal file
View File

@@ -0,0 +1,37 @@
#!/usr/bin/env python
"""
Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""
import re
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.HIGHEST
def dependencies():
pass
def tamper(payload, **kwargs):
"""
Replaces all occurrences of operator equal ('=') with 'RLIKE' counterpart
Tested against:
* MySQL 4, 5.0 and 5.5
Notes:
* Useful to bypass weak and bespoke web application firewalls that
filter the equal character ('=')
>>> tamper('SELECT * FROM users WHERE id=1')
'SELECT * FROM users WHERE id RLIKE 1'
"""
retVal = payload
if payload:
retVal = re.sub(r"\s*=\s*", " RLIKE ", retVal)
return retVal

View File

@@ -5,16 +5,19 @@ 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.convert import decodeHex from lib.core.convert import decodeHex
from lib.core.convert import getOrds from lib.core.convert import getOrds
from lib.core.enums import DBMS
from lib.core.enums import PRIORITY from lib.core.enums import PRIORITY
__priority__ = PRIORITY.NORMAL __priority__ = PRIORITY.NORMAL
def dependencies(): def dependencies():
pass singleTimeWarnMessage("tamper script '%s' is only meant to be run against %s" % (os.path.basename(__file__).split(".")[0], DBMS.MYSQL))
def tamper(payload, **kwargs): def tamper(payload, **kwargs):
""" """

View File

@@ -5,14 +5,17 @@ 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.enums import DBMS
from lib.core.enums import PRIORITY from lib.core.enums import PRIORITY
__priority__ = PRIORITY.HIGHEST __priority__ = PRIORITY.HIGHEST
def dependencies(): def dependencies():
pass singleTimeWarnMessage("tamper script '%s' is only meant to be run against %s" % (os.path.basename(__file__).split(".")[0], DBMS.MYSQL))
def tamper(payload, **kwargs): def tamper(payload, **kwargs):
""" """

View File

@@ -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

39
tamper/sleep2getlock.py Normal file
View 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

View File

@@ -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