Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sqlmapproject
GitHub Repository: sqlmapproject/sqlmap
Path: blob/master/lib/core/testing.py
2989 views
1
#!/usr/bin/env python
2
3
"""
4
Copyright (c) 2006-2025 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'\" --technique=B --no-escape --string=luther --unstable", (u": '\u0161u\u0107uraj'",)),
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 --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 | 5")),
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", "2 entries", "6E616D6569736E756C6C")),
71
("-u <url> --technique=U --fresh-queries --force-partial --dump -T users --dump-format=HTML --answers=\"crack=n\" -v 3", ("performed 6 queries", "nameisnull", "~using default dictionary", "dumped to HTML file")),
72
("-u <url> --flush-session --technique=BU --all", ("5 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 [5]", "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 users --dump-format=SQLITE --binary-fields=name --where \"id=3\"", ("7775", "179ad45c6ce2cb97cf1029e212046e81 (testpass)", "dumped to SQLITE database")),
77
("-d \"<direct>\" --flush-session --banner --schema --sql-query=\"UPDATE users SET name='foobar' WHERE id=5; SELECT * FROM users; SELECT 987654321\"", ("banner: '3.", "INTEGER", "TEXT", "id", "name", "surname", "5,foobar,nameisnull", "'987654321'",)),
78
("--purge -v 3", ("~ERROR", "~CRITICAL", "deleting the whole directory tree")),
79
)
80
81
retVal = True
82
count = 0
83
84
while True:
85
address, port = "127.0.0.1", random.randint(10000, 65535)
86
try:
87
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
88
if s.connect_ex((address, port)):
89
break
90
else:
91
time.sleep(1)
92
finally:
93
s.close()
94
95
def _thread():
96
vulnserver.init(quiet=True)
97
vulnserver.run(address=address, port=port)
98
99
vulnserver._alive = True
100
101
thread = threading.Thread(target=_thread)
102
thread.daemon = True
103
thread.start()
104
105
while vulnserver._alive:
106
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
107
try:
108
s.connect((address, port))
109
s.sendall(b"GET / HTTP/1.1\r\n\r\n")
110
result = b""
111
while True:
112
current = s.recv(1024)
113
if not current:
114
break
115
else:
116
result += current
117
if b"vulnserver" in result:
118
break
119
except:
120
pass
121
finally:
122
s.close()
123
time.sleep(1)
124
125
if not vulnserver._alive:
126
logger.error("problem occurred in vulnserver instantiation (address: 'http://%s:%s')" % (address, port))
127
return False
128
else:
129
logger.info("vulnserver running at 'http://%s:%s'..." % (address, port))
130
131
handle, config = tempfile.mkstemp(suffix=".conf")
132
os.close(handle)
133
134
handle, database = tempfile.mkstemp(suffix=".sqlite")
135
os.close(handle)
136
137
with sqlite3.connect(database) as conn:
138
c = conn.cursor()
139
c.executescript(vulnserver.SCHEMA)
140
141
handle, request = tempfile.mkstemp(suffix=".req")
142
os.close(handle)
143
144
handle, log = tempfile.mkstemp(suffix=".log")
145
os.close(handle)
146
147
handle, multiple = tempfile.mkstemp(suffix=".lst")
148
os.close(handle)
149
150
content = "POST / HTTP/1.0\nUser-Agent: foobar\nHost: %s:%s\n\nid=1\n" % (address, port)
151
with open(request, "w+") as f:
152
f.write(content)
153
f.flush()
154
155
content = '<port>%d</port><request base64="true"><![CDATA[%s]]></request>' % (port, encodeBase64(content, binary=False))
156
with open(log, "w+") as f:
157
f.write(content)
158
f.flush()
159
160
base = "http://%s:%d/" % (address, port)
161
url = "%s?id=1" % base
162
direct = "sqlite3://%s" % database
163
tmpdir = tempfile.mkdtemp()
164
165
with open(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "sqlmap.conf"))) as f:
166
content = f.read().replace("url =", "url = %s" % url)
167
168
with open(config, "w+") as f:
169
f.write(content)
170
f.flush()
171
172
content = "%s?%s=%d\n%s?%s=%d\n%s&%s=1" % (base, randomStr(), randomInt(), base, randomStr(), randomInt(), url, randomStr())
173
with open(multiple, "w+") as f:
174
f.write(content)
175
f.flush()
176
177
for options, checks in TESTS:
178
status = '%d/%d (%d%%) ' % (count, len(TESTS), round(100.0 * count / len(TESTS)))
179
dataToStdout("\r[%s] [INFO] complete: %s" % (time.strftime("%X"), status))
180
181
if IS_WIN and "uraj" in options:
182
options = options.replace(u"\u0161u\u0107uraj", "sucuraj")
183
checks = [check.replace(u"\u0161u\u0107uraj", "sucuraj") for check in checks]
184
185
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"))):
186
options = options.replace(tag, value)
187
188
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)
189
190
if "<tmpfile>" in cmd:
191
handle, tmp = tempfile.mkstemp()
192
os.close(handle)
193
cmd = cmd.replace("<tmpfile>", tmp)
194
195
output = shellExec(cmd)
196
197
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:
198
dataToStdout("---\n\n$ %s\n" % cmd)
199
dataToStdout("%s---\n" % output, coloring=False)
200
retVal = False
201
202
count += 1
203
204
clearConsoleLine()
205
if retVal:
206
logger.info("vuln test final result: PASSED")
207
else:
208
logger.error("vuln test final result: FAILED")
209
210
return retVal
211
212
def smokeTest():
213
"""
214
Runs the basic smoke testing of a program
215
"""
216
217
unisonRandom()
218
219
with open(paths.ERRORS_XML, "r") as f:
220
content = f.read()
221
222
for regex in re.findall(r'<error regexp="(.+?)"/>', content):
223
try:
224
re.compile(regex)
225
except re.error:
226
errMsg = "smoke test failed at compiling '%s'" % regex
227
logger.error(errMsg)
228
return False
229
230
retVal = True
231
count, length = 0, 0
232
233
for root, _, files in os.walk(paths.SQLMAP_ROOT_PATH):
234
if any(_ in root for _ in ("thirdparty", "extra", "interbase")):
235
continue
236
237
for filename in files:
238
if os.path.splitext(filename)[1].lower() == ".py" and filename != "__init__.py":
239
length += 1
240
241
for root, _, files in os.walk(paths.SQLMAP_ROOT_PATH):
242
if any(_ in root for _ in ("thirdparty", "extra", "interbase")):
243
continue
244
245
for filename in files:
246
if os.path.splitext(filename)[1].lower() == ".py" and filename not in ("__init__.py", "gui.py"):
247
path = os.path.join(root, os.path.splitext(filename)[0])
248
path = path.replace(paths.SQLMAP_ROOT_PATH, '.')
249
path = path.replace(os.sep, '.').lstrip('.')
250
try:
251
__import__(path)
252
module = sys.modules[path]
253
except Exception as ex:
254
retVal = False
255
dataToStdout("\r")
256
errMsg = "smoke test failed at importing module '%s' (%s):\n%s" % (path, os.path.join(root, filename), ex)
257
logger.error(errMsg)
258
else:
259
logger.setLevel(logging.CRITICAL)
260
kb.smokeMode = True
261
262
(failure_count, _) = doctest.testmod(module)
263
264
kb.smokeMode = False
265
logger.setLevel(logging.INFO)
266
267
if failure_count > 0:
268
retVal = False
269
270
count += 1
271
status = '%d/%d (%d%%) ' % (count, length, round(100.0 * count / length))
272
dataToStdout("\r[%s] [INFO] complete: %s" % (time.strftime("%X"), status))
273
274
def _(node):
275
for __ in dir(node):
276
if not __.startswith('_'):
277
candidate = getattr(node, __)
278
if isinstance(candidate, str):
279
if '\\' in candidate:
280
try:
281
re.compile(candidate)
282
except:
283
errMsg = "smoke test failed at compiling '%s'" % candidate
284
logger.error(errMsg)
285
raise
286
else:
287
_(candidate)
288
289
for dbms in queries:
290
try:
291
_(queries[dbms])
292
except:
293
retVal = False
294
295
clearConsoleLine()
296
if retVal:
297
logger.info("smoke test final result: PASSED")
298
else:
299
logger.error("smoke test final result: FAILED")
300
301
return retVal
302
303