Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sqlmapproject
GitHub Repository: sqlmapproject/sqlmap
Path: blob/master/lib/core/testing.py
3554 views
1
#!/usr/bin/env python
2
3
"""
4
Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org)
5
See the file 'LICENSE' for copying permission
6
"""
7
8
import doctest
9
import logging
10
import os
11
import random
12
import re
13
import socket
14
import sqlite3
15
import sys
16
import tempfile
17
import threading
18
import time
19
20
from extra.vulnserver import vulnserver
21
from lib.core.common import clearConsoleLine
22
from lib.core.common import dataToStdout
23
from lib.core.common import randomInt
24
from lib.core.common import randomStr
25
from lib.core.common import shellExec
26
from lib.core.compat import round
27
from lib.core.convert import encodeBase64
28
from lib.core.data import kb
29
from lib.core.data import logger
30
from lib.core.data import paths
31
from lib.core.data import queries
32
from lib.core.patch import unisonRandom
33
from lib.core.settings import IS_WIN
34
35
def vulnTest():
36
"""
37
Runs the testing against 'vulnserver'
38
"""
39
40
TESTS = (
41
("-h", ("to see full list of options run with '-hh'",)),
42
("--dependencies", ("sqlmap requires", "third-party library")),
43
("-u <url> --data=\"reflect=1\" --flush-session --wizard --disable-coloring", ("Please choose:", "back-end DBMS: SQLite", "current user is DBA: True", "banner: '3.")),
44
("-u <url> --data=\"code=1\" --code=200 --technique=B --banner --no-cast --flush-session", ("back-end DBMS: SQLite", "banner: '3.", "~COALESCE(CAST(")),
45
(u"-c <config> --flush-session --output-dir=\"<tmpdir>\" --smart --roles --statements --hostname --privileges --sql-query=\"SELECT '\u0161u\u0107uraj'\" --technique=U", (u": '\u0161u\u0107uraj'", "on SQLite it is not possible", "as the output directory")),
46
(u"-u <url> --flush-session --sql-query=\"SELECT '\u0161u\u0107uraj'\" --titles --technique=B --no-escape --string=luther --unstable", (u": '\u0161u\u0107uraj'", "~with --string",)),
47
("-m <multiple> --flush-session --technique=B --banner", ("/3] URL:", "back-end DBMS: SQLite", "banner: '3.")),
48
("--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")),
49
("-u \"<url>&id2=1\" -p id2 -v 5 --flush-session --level=5 --text-only --test-filter=\"AND boolean-based blind - WHERE or HAVING clause (MySQL comment)\"", ("~1AND",)),
50
("--list-tampers", ("between", "MySQL", "xforwardedfor")),
51
("-r <request> --flush-session -v 5 --test-skip=\"heavy\" --save=<config>", ("CloudFlare", "web application technology: Express", "possible DBMS: 'SQLite'", "User-Agent: foobar", "~Type: time-based blind", "saved command line options to the configuration file")),
52
("-c <config>", ("CloudFlare", "possible DBMS: 'SQLite'", "User-Agent: foobar", "~Type: time-based blind")),
53
("-l <log> --flush-session --keep-alive --skip-waf -vvvvv --technique=U --union-from=users --banner --parse-errors", ("banner: '3.", "ORDER BY term out of range", "~xp_cmdshell", "Connection: keep-alive")),
54
("-l <log> --offline --banner -v 5", ("banner: '3.", "~[TRAFFIC OUT]")),
55
("-u <base> --flush-session --data=\"id=1&_=Eewef6oh\" --chunked --randomize=_ --random-agent --banner", ("fetched random HTTP User-Agent header value", "Parameter: id (POST)", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", "banner: '3.")),
56
("-u <base64> -p id --base64=id --data=\"base64=true\" --flush-session --banner --technique=B", ("banner: '3.",)),
57
("-u <base64> -p id --base64=id --data=\"base64=true\" --flush-session --tables --technique=U", (" users ",)),
58
("-u <url> --flush-session --banner --technique=B --disable-precon --not-string \"no results\"", ("banner: '3.",)),
59
("-u <url> --flush-session --encoding=gbk --banner --technique=B --first=1 --last=2", ("banner: '3.'",)),
60
("-u <url> --flush-session --encoding=ascii --forms --crawl=2 --threads=2 --banner", ("total of 2 targets", "might be injectable", "Type: UNION query", "banner: '3.")),
61
("-u <base> --flush-session --technique=BU --data=\"{\\\"id\\\": 1}\" --banner", ("might be injectable", "3 columns", "Payload: {\"id\"", "Type: boolean-based blind", "Type: UNION query", "banner: '3.")),
62
("-u <base> --flush-session -H \"Foo: Bar\" -H \"Sna: Fu\" --data=\"<root><param name=\\\"id\\\" value=\\\"1*\\\"/></root>\" --union-char=1 --mobile --answers=\"smartphone=3\" --banner --smart -v 5", ("might be injectable", "Payload: <root><param name=\"id\" value=\"1", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", "banner: '3.", "Nexus", "Sna: Fu", "Foo: Bar")),
63
("-u <base> --flush-session --technique=BU --method=PUT --data=\"a=1;id=1;b=2\" --param-del=\";\" --skip-static --har=<tmpfile> --dump -T users --start=1 --stop=2", ("might be injectable", "Parameter: id (PUT)", "Type: boolean-based blind", "Type: UNION query", "2 entries")),
64
("-u <url> --flush-session -H \"id: 1*\" --tables -t <tmpfile>", ("might be injectable", "Parameter: id #1* ((custom) HEADER)", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", " users ")),
65
("-u <url> --flush-session --banner --invalid-logical --technique=B --predict-output --titles --test-filter=\"OR boolean\" --tamper=space2dash", ("banner: '3.", " LIKE ")),
66
("-u <url> --flush-session --cookie=\"PHPSESSID=d41d8cd98f00b204e9800998ecf8427e; id=1*; id2=2\" --tables --union-cols=3", ("might be injectable", "Cookie #1* ((custom) HEADER)", "Type: boolean-based blind", "Type: time-based blind", "Type: UNION query", " users ")),
67
("-u <url> --flush-session --null-connection --technique=B --tamper=between,randomcase --banner --count -T users", ("NULL connection is supported with HEAD method", "banner: '3.", "users | 30")),
68
("-u <base> --data=\"aWQ9MQ==\" --flush-session --base64=POST -v 6", ("aWQ9MTtXQUlURk9SIERFTEFZICcwOjA",)),
69
("-u <url> --flush-session --parse-errors --test-filter=\"subquery\" --eval=\"import hashlib; id2=2; id3=hashlib.md5(id.encode()).hexdigest()\" --referer=\"localhost\"", ("might be injectable", ": syntax error", "back-end DBMS: SQLite", "WHERE or HAVING clause (subquery")),
70
("-u <url> --banner --schema --dump -T users --binary-fields=surname --where \"id>3\"", ("banner: '3.", "INTEGER", "TEXT", "id", "name", "surname", "27 entries", "6E616D6569736E756C6C")),
71
("-u <url> --technique=U --fresh-queries --force-partial --dump -T users --dump-format=HTML --answers=\"crack=n\" -v 3", ("performed 31 queries", "nameisnull", "~using default dictionary", "dumped to HTML file")),
72
("-u <url> --flush-session --technique=BU --all", ("30 entries", "Type: boolean-based blind", "Type: UNION query", "luther", "blisset", "fluffy", "179ad45c6ce2cb97cf1029e212046e81", "NULL", "nameisnull", "testpass")),
73
("-u <url> -z \"tec=B\" --hex --fresh-queries --threads=4 --sql-query=\"SELECT * FROM users\"", ("SELECT * FROM users [30]", "nameisnull")),
74
("-u \"<url>&echo=foobar*\" --flush-session", ("might be vulnerable to cross-site scripting",)),
75
("-u \"<url>&query=*\" --flush-session --technique=Q --banner", ("Title: SQLite inline queries", "banner: '3.")),
76
("-d \"<direct>\" --flush-session --dump -T creds --dump-format=SQLITE --binary-fields=password_hash --where \"user_id=5\"", ("3137396164343563366365326362393763663130323965323132303436653831", "dumped to SQLITE database")),
77
("-d \"<direct>\" --flush-session --banner --schema --sql-query=\"UPDATE users SET name='foobar' WHERE id=4; SELECT * FROM users; SELECT 987654321\"", ("banner: '3.", "INTEGER", "TEXT", "id", "name", "surname", "4,foobar,nameisnull", "'987654321'",)),
78
("-u <base>csrf --data=\"id=1&csrf_token=1\" --banner --answers=\"update=y\" --flush-session", ("back-end DBMS: SQLite", "banner: '3.")),
79
("--purge -v 3", ("~ERROR", "~CRITICAL", "deleting the whole directory tree")),
80
)
81
82
retVal = True
83
count = 0
84
cleanups = []
85
86
while True:
87
address, port = "127.0.0.1", random.randint(10000, 65535)
88
try:
89
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
90
if s.connect_ex((address, port)):
91
break
92
else:
93
time.sleep(1)
94
finally:
95
s.close()
96
97
def _thread():
98
vulnserver.init(quiet=True)
99
vulnserver.run(address=address, port=port)
100
101
vulnserver._alive = True
102
103
thread = threading.Thread(target=_thread)
104
thread.daemon = True
105
thread.start()
106
107
while vulnserver._alive:
108
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
109
try:
110
s.connect((address, port))
111
s.sendall(b"GET / HTTP/1.1\r\n\r\n")
112
result = b""
113
while True:
114
current = s.recv(1024)
115
if not current:
116
break
117
else:
118
result += current
119
if b"vulnserver" in result:
120
break
121
except:
122
pass
123
finally:
124
s.close()
125
time.sleep(1)
126
127
if not vulnserver._alive:
128
logger.error("problem occurred in vulnserver instantiation (address: 'http://%s:%s')" % (address, port))
129
return False
130
else:
131
logger.info("vulnserver running at 'http://%s:%s'..." % (address, port))
132
133
handle, config = tempfile.mkstemp(suffix=".conf")
134
os.close(handle)
135
cleanups.append(config)
136
137
handle, database = tempfile.mkstemp(suffix=".sqlite")
138
os.close(handle)
139
cleanups.append(database)
140
141
with sqlite3.connect(database) as conn:
142
c = conn.cursor()
143
c.executescript(vulnserver.SCHEMA)
144
145
handle, request = tempfile.mkstemp(suffix=".req")
146
os.close(handle)
147
cleanups.append(request)
148
149
handle, log = tempfile.mkstemp(suffix=".log")
150
os.close(handle)
151
cleanups.append(log)
152
153
handle, multiple = tempfile.mkstemp(suffix=".lst")
154
os.close(handle)
155
cleanups.append(multiple)
156
157
content = "POST / HTTP/1.0\nUser-Agent: foobar\nHost: %s:%s\n\nid=1\n" % (address, port)
158
with open(request, "w+") as f:
159
f.write(content)
160
f.flush()
161
162
content = '<port>%d</port><request base64="true"><![CDATA[%s]]></request>' % (port, encodeBase64(content, binary=False))
163
with open(log, "w+") as f:
164
f.write(content)
165
f.flush()
166
167
base = "http://%s:%d/" % (address, port)
168
url = "%s?id=1" % base
169
direct = "sqlite3://%s" % database
170
tmpdir = tempfile.mkdtemp()
171
172
with open(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "sqlmap.conf"))) as f:
173
content = f.read().replace("url =", "url = %s" % url)
174
175
with open(config, "w+") as f:
176
f.write(content)
177
f.flush()
178
179
content = "%s?%s=%d\n%s?%s=%d\n%s&%s=1" % (base, randomStr(), randomInt(), base, randomStr(), randomInt(), url, randomStr())
180
with open(multiple, "w+") as f:
181
f.write(content)
182
f.flush()
183
184
for options, checks in TESTS:
185
status = '%d/%d (%d%%) ' % (count, len(TESTS), round(100.0 * count / len(TESTS)))
186
dataToStdout("\r[%s] [INFO] completed: %s" % (time.strftime("%X"), status))
187
188
if IS_WIN and "uraj" in options:
189
options = options.replace(u"\u0161u\u0107uraj", "sucuraj")
190
checks = [check.replace(u"\u0161u\u0107uraj", "sucuraj") for check in checks]
191
192
for tag, value in (("<url>", url), ("<base>", base), ("<direct>", direct), ("<tmpdir>", tmpdir), ("<request>", request), ("<log>", log), ("<multiple>", multiple), ("<config>", config), ("<base64>", url.replace("id=1", "id=MZ=%3d"))):
193
options = options.replace(tag, value)
194
195
cmd = "%s \"%s\" %s --batch --non-interactive --debug --time-sec=1" % (sys.executable if ' ' not in sys.executable else '"%s"' % sys.executable, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "sqlmap.py")), options)
196
197
if "<tmpfile>" in cmd:
198
handle, tmp = tempfile.mkstemp()
199
os.close(handle)
200
cmd = cmd.replace("<tmpfile>", tmp)
201
202
output = shellExec(cmd)
203
204
if not all((check in output if not check.startswith('~') else check[1:] not in output) for check in checks) or "unhandled exception" in output:
205
dataToStdout("---\n\n$ %s\n" % cmd)
206
dataToStdout("%s---\n" % output, coloring=False)
207
retVal = False
208
209
count += 1
210
211
clearConsoleLine()
212
if retVal:
213
logger.info("vuln test final result: PASSED")
214
else:
215
logger.error("vuln test final result: FAILED")
216
217
for filename in cleanups:
218
try:
219
os.remove(filename)
220
except:
221
pass
222
223
return retVal
224
225
def smokeTest():
226
"""
227
Runs the basic smoke testing of a program
228
"""
229
230
unisonRandom()
231
232
with open(paths.ERRORS_XML, "r") as f:
233
content = f.read()
234
235
for regex in re.findall(r'<error regexp="(.+?)"/>', content):
236
try:
237
re.compile(regex)
238
except re.error:
239
errMsg = "smoke test failed at compiling '%s'" % regex
240
logger.error(errMsg)
241
return False
242
243
retVal = True
244
count, length = 0, 0
245
246
for root, _, files in os.walk(paths.SQLMAP_ROOT_PATH):
247
if any(_ in root for _ in ("thirdparty", "extra", "interbase")):
248
continue
249
250
for filename in files:
251
if os.path.splitext(filename)[1].lower() == ".py" and filename != "__init__.py":
252
length += 1
253
254
for root, _, files in os.walk(paths.SQLMAP_ROOT_PATH):
255
if any(_ in root for _ in ("thirdparty", "extra", "interbase")):
256
continue
257
258
for filename in files:
259
if os.path.splitext(filename)[1].lower() == ".py" and filename not in ("__init__.py", "gui.py"):
260
path = os.path.join(root, os.path.splitext(filename)[0])
261
path = path.replace(paths.SQLMAP_ROOT_PATH, '.')
262
path = path.replace(os.sep, '.').lstrip('.')
263
try:
264
__import__(path)
265
module = sys.modules[path]
266
except Exception as ex:
267
retVal = False
268
dataToStdout("\r")
269
errMsg = "smoke test failed at importing module '%s' (%s):\n%s" % (path, os.path.join(root, filename), ex)
270
logger.error(errMsg)
271
else:
272
logger.setLevel(logging.CRITICAL)
273
kb.smokeMode = True
274
275
(failure_count, _) = doctest.testmod(module)
276
277
kb.smokeMode = False
278
logger.setLevel(logging.INFO)
279
280
if failure_count > 0:
281
retVal = False
282
283
count += 1
284
status = '%d/%d (%d%%) ' % (count, length, round(100.0 * count / length))
285
dataToStdout("\r[%s] [INFO] completed: %s" % (time.strftime("%X"), status))
286
287
def _(node):
288
for __ in dir(node):
289
if not __.startswith('_'):
290
candidate = getattr(node, __)
291
if isinstance(candidate, str):
292
if '\\' in candidate:
293
try:
294
re.compile(candidate)
295
except:
296
errMsg = "smoke test failed at compiling '%s'" % candidate
297
logger.error(errMsg)
298
raise
299
else:
300
_(candidate)
301
302
for dbms in queries:
303
try:
304
_(queries[dbms])
305
except:
306
retVal = False
307
308
clearConsoleLine()
309
if retVal:
310
logger.info("smoke test final result: PASSED")
311
else:
312
logger.error("smoke test final result: FAILED")
313
314
return retVal
315
316