Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sqlmapproject
GitHub Repository: sqlmapproject/sqlmap
Path: blob/master/plugins/dbms/mysql/fingerprint.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 re
9
10
from lib.core.common import Backend
11
from lib.core.common import Format
12
from lib.core.common import hashDBRetrieve
13
from lib.core.common import hashDBWrite
14
from lib.core.compat import xrange
15
from lib.core.convert import getUnicode
16
from lib.core.data import conf
17
from lib.core.data import kb
18
from lib.core.data import logger
19
from lib.core.enums import DBMS
20
from lib.core.enums import FORK
21
from lib.core.enums import HASHDB_KEYS
22
from lib.core.enums import OS
23
from lib.core.session import setDbms
24
from lib.core.settings import MYSQL_ALIASES
25
from lib.request import inject
26
from plugins.generic.fingerprint import Fingerprint as GenericFingerprint
27
28
class Fingerprint(GenericFingerprint):
29
def __init__(self):
30
GenericFingerprint.__init__(self, DBMS.MYSQL)
31
32
def _commentCheck(self):
33
infoMsg = "executing %s comment injection fingerprint" % DBMS.MYSQL
34
logger.info(infoMsg)
35
36
result = inject.checkBooleanExpression("[RANDNUM]=[RANDNUM]/* NoValue */")
37
38
if not result:
39
warnMsg = "unable to perform %s comment injection" % DBMS.MYSQL
40
logger.warning(warnMsg)
41
42
return None
43
44
# Reference: https://downloads.mysql.com/archives/community/
45
# Reference: https://dev.mysql.com/doc/relnotes/mysql/<major>.<minor>/en/
46
47
versions = (
48
(90300, 90302), # MySQL 9.3
49
(90200, 90202), # MySQL 9.2
50
(90100, 90102), # MySQL 9.1
51
(90000, 90002), # MySQL 9.0
52
(80400, 80406), # MySQL 8.4
53
(80300, 80302), # MySQL 8.3
54
(80200, 80202), # MySQL 8.2
55
(80100, 80102), # MySQL 8.1
56
(80000, 80043), # MySQL 8.0
57
(60000, 60014), # MySQL 6.0
58
(50700, 50745), # MySQL 5.7
59
(50600, 50652), # MySQL 5.6
60
(50500, 50563), # MySQL 5.5
61
(50400, 50404), # MySQL 5.4
62
(50100, 50174), # MySQL 5.1
63
(50000, 50097), # MySQL 5.0
64
(40100, 40131), # MySQL 4.1
65
(40000, 40032), # MySQL 4.0
66
(32300, 32359), # MySQL 3.23
67
(32200, 32235), # MySQL 3.22
68
)
69
70
found = False
71
for candidate in versions:
72
result = inject.checkBooleanExpression("[RANDNUM]=[RANDNUM]/*!%d AND [RANDNUM1]=[RANDNUM2]*/" % candidate[0])
73
74
if not result:
75
found = True
76
break
77
78
if found:
79
for version in xrange(candidate[1], candidate[0] - 1, -1):
80
version = getUnicode(version)
81
result = inject.checkBooleanExpression("[RANDNUM]=[RANDNUM]/*!%s AND [RANDNUM1]=[RANDNUM2]*/" % version)
82
83
if not result:
84
if version[0] == "3":
85
midVer = version[1:3]
86
else:
87
midVer = version[2]
88
89
trueVer = "%s.%s.%s" % (version[0], midVer, version[3:])
90
91
return trueVer
92
93
return None
94
95
def getFingerprint(self):
96
fork = hashDBRetrieve(HASHDB_KEYS.DBMS_FORK)
97
98
if fork is None:
99
if inject.checkBooleanExpression("VERSION() LIKE '%MariaDB%'"):
100
fork = FORK.MARIADB
101
elif inject.checkBooleanExpression("VERSION() LIKE '%TiDB%'"):
102
fork = FORK.TIDB
103
elif inject.checkBooleanExpression("@@VERSION_COMMENT LIKE '%drizzle%'"):
104
fork = FORK.DRIZZLE
105
elif inject.checkBooleanExpression("@@VERSION_COMMENT LIKE '%Percona%'"):
106
fork = FORK.PERCONA
107
elif inject.checkBooleanExpression("@@VERSION_COMMENT LIKE '%Doris%'"):
108
fork = FORK.DORIS
109
elif inject.checkBooleanExpression("@@VERSION_COMMENT LIKE '%StarRocks%'"):
110
fork = FORK.STARROCKS
111
elif inject.checkBooleanExpression("AURORA_VERSION() LIKE '%'"): # Reference: https://aws.amazon.com/premiumsupport/knowledge-center/aurora-version-number/
112
fork = FORK.AURORA
113
else:
114
fork = ""
115
116
hashDBWrite(HASHDB_KEYS.DBMS_FORK, fork)
117
118
value = ""
119
wsOsFp = Format.getOs("web server", kb.headersFp)
120
121
if wsOsFp and not conf.api:
122
value += "%s\n" % wsOsFp
123
124
if kb.data.banner:
125
dbmsOsFp = Format.getOs("back-end DBMS", kb.bannerFp)
126
127
if dbmsOsFp and not conf.api:
128
value += "%s\n" % dbmsOsFp
129
130
value += "back-end DBMS: "
131
actVer = Format.getDbms()
132
133
if not conf.extensiveFp:
134
value += actVer
135
if fork:
136
value += " (%s fork)" % fork
137
return value
138
139
comVer = self._commentCheck()
140
blank = " " * 15
141
value += "active fingerprint: %s" % actVer
142
143
if comVer:
144
comVer = Format.getDbms([comVer])
145
value += "\n%scomment injection fingerprint: %s" % (blank, comVer)
146
147
if kb.bannerFp:
148
banVer = kb.bannerFp.get("dbmsVersion")
149
150
if banVer:
151
if banVer and re.search(r"-log$", kb.data.banner or ""):
152
banVer += ", logging enabled"
153
154
banVer = Format.getDbms([banVer])
155
value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer)
156
157
htmlErrorFp = Format.getErrorParsedDBMSes()
158
159
if htmlErrorFp:
160
value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp)
161
162
if fork:
163
value += "\n%sfork fingerprint: %s" % (blank, fork)
164
165
return value
166
167
def checkDbms(self):
168
"""
169
References for fingerprint:
170
171
* http://dev.mysql.com/doc/refman/5.0/en/news-5-0-x.html (up to 5.0.89)
172
* http://dev.mysql.com/doc/refman/5.1/en/news-5-1-x.html (up to 5.1.42)
173
* http://dev.mysql.com/doc/refman/5.4/en/news-5-4-x.html (up to 5.4.4)
174
* http://dev.mysql.com/doc/refman/5.5/en/news-5-5-x.html (up to 5.5.0)
175
* http://dev.mysql.com/doc/refman/6.0/en/news-6-0-x.html (manual has been withdrawn)
176
"""
177
178
if not conf.extensiveFp and Backend.isDbmsWithin(MYSQL_ALIASES):
179
setDbms("%s %s" % (DBMS.MYSQL, Backend.getVersion()))
180
181
if Backend.isVersionGreaterOrEqualThan("5") or inject.checkBooleanExpression("DATABASE() LIKE SCHEMA()"):
182
kb.data.has_information_schema = True
183
self.getBanner()
184
185
return True
186
187
infoMsg = "testing %s" % DBMS.MYSQL
188
logger.info(infoMsg)
189
190
result = inject.checkBooleanExpression("IFNULL(QUARTER(NULL),NULL XOR NULL) IS NULL")
191
192
if result:
193
infoMsg = "confirming %s" % DBMS.MYSQL
194
logger.info(infoMsg)
195
196
result = inject.checkBooleanExpression("COALESCE(SESSION_USER(),USER()) IS NOT NULL")
197
198
if not result:
199
# Note: MemSQL doesn't support SESSION_USER()
200
result = inject.checkBooleanExpression("GEOGRAPHY_AREA(NULL) IS NULL")
201
202
if result:
203
hashDBWrite(HASHDB_KEYS.DBMS_FORK, FORK.MEMSQL)
204
205
if not result:
206
warnMsg = "the back-end DBMS is not %s" % DBMS.MYSQL
207
logger.warning(warnMsg)
208
209
return False
210
211
# reading information_schema on some platforms is causing annoying timeout exits
212
# Reference: http://bugs.mysql.com/bug.php?id=15855
213
214
kb.data.has_information_schema = True
215
216
# Determine if it is MySQL >= 9.0.0
217
if inject.checkBooleanExpression("ISNULL(VECTOR_DIM(NULL))"):
218
Backend.setVersion(">= 9.0.0")
219
setDbms("%s 9" % DBMS.MYSQL)
220
self.getBanner()
221
222
# Determine if it is MySQL >= 8.0.0
223
elif inject.checkBooleanExpression("ISNULL(JSON_STORAGE_FREE(NULL))"):
224
Backend.setVersion(">= 8.0.0")
225
setDbms("%s 8" % DBMS.MYSQL)
226
self.getBanner()
227
228
# Determine if it is MySQL >= 5.0.0
229
elif inject.checkBooleanExpression("ISNULL(TIMESTAMPADD(MINUTE,[RANDNUM],NULL))"):
230
Backend.setVersion(">= 5.0.0")
231
setDbms("%s 5" % DBMS.MYSQL)
232
self.getBanner()
233
234
if not conf.extensiveFp:
235
return True
236
237
infoMsg = "actively fingerprinting %s" % DBMS.MYSQL
238
logger.info(infoMsg)
239
240
# Check if it is MySQL >= 5.7
241
if inject.checkBooleanExpression("ISNULL(JSON_QUOTE(NULL))"):
242
Backend.setVersion(">= 5.7")
243
244
# Check if it is MySQL >= 5.6
245
elif inject.checkBooleanExpression("ISNULL(VALIDATE_PASSWORD_STRENGTH(NULL))"):
246
Backend.setVersion(">= 5.6")
247
248
# Check if it is MySQL >= 5.5
249
elif inject.checkBooleanExpression("TO_SECONDS(950501)>0"):
250
Backend.setVersion(">= 5.5")
251
252
# Check if it is MySQL >= 5.1.2 and < 5.5.0
253
elif inject.checkBooleanExpression("@@table_open_cache=@@table_open_cache"):
254
if inject.checkBooleanExpression("[RANDNUM]=(SELECT [RANDNUM] FROM information_schema.GLOBAL_STATUS LIMIT 0, 1)"):
255
Backend.setVersionList([">= 5.1.12", "< 5.5.0"])
256
elif inject.checkBooleanExpression("[RANDNUM]=(SELECT [RANDNUM] FROM information_schema.PROCESSLIST LIMIT 0, 1)"):
257
Backend.setVersionList([">= 5.1.7", "< 5.1.12"])
258
elif inject.checkBooleanExpression("[RANDNUM]=(SELECT [RANDNUM] FROM information_schema.PARTITIONS LIMIT 0, 1)"):
259
Backend.setVersion("= 5.1.6")
260
elif inject.checkBooleanExpression("[RANDNUM]=(SELECT [RANDNUM] FROM information_schema.PLUGINS LIMIT 0, 1)"):
261
Backend.setVersionList([">= 5.1.5", "< 5.1.6"])
262
else:
263
Backend.setVersionList([">= 5.1.2", "< 5.1.5"])
264
265
# Check if it is MySQL >= 5.0.0 and < 5.1.2
266
elif inject.checkBooleanExpression("@@hostname=@@hostname"):
267
Backend.setVersionList([">= 5.0.38", "< 5.1.2"])
268
elif inject.checkBooleanExpression("@@character_set_filesystem=@@character_set_filesystem"):
269
Backend.setVersionList([">= 5.0.19", "< 5.0.38"])
270
elif not inject.checkBooleanExpression("[RANDNUM]=(SELECT [RANDNUM] FROM DUAL WHERE [RANDNUM1]!=[RANDNUM2])"):
271
Backend.setVersionList([">= 5.0.11", "< 5.0.19"])
272
elif inject.checkBooleanExpression("@@div_precision_increment=@@div_precision_increment"):
273
Backend.setVersionList([">= 5.0.6", "< 5.0.11"])
274
elif inject.checkBooleanExpression("@@automatic_sp_privileges=@@automatic_sp_privileges"):
275
Backend.setVersionList([">= 5.0.3", "< 5.0.6"])
276
else:
277
Backend.setVersionList([">= 5.0.0", "< 5.0.3"])
278
279
elif inject.checkBooleanExpression("DATABASE() LIKE SCHEMA()"):
280
Backend.setVersion(">= 5.0.2")
281
setDbms("%s 5" % DBMS.MYSQL)
282
self.getBanner()
283
284
elif inject.checkBooleanExpression("STRCMP(LOWER(CURRENT_USER()), UPPER(CURRENT_USER()))=0"):
285
Backend.setVersion("< 5.0.0")
286
setDbms("%s 4" % DBMS.MYSQL)
287
self.getBanner()
288
289
kb.data.has_information_schema = False
290
291
if not conf.extensiveFp:
292
return True
293
294
# Check which version of MySQL < 5.0.0 it is
295
if inject.checkBooleanExpression("3=(SELECT COERCIBILITY(USER()))"):
296
Backend.setVersionList([">= 4.1.11", "< 5.0.0"])
297
elif inject.checkBooleanExpression("2=(SELECT COERCIBILITY(USER()))"):
298
Backend.setVersionList([">= 4.1.1", "< 4.1.11"])
299
elif inject.checkBooleanExpression("CURRENT_USER()=CURRENT_USER()"):
300
Backend.setVersionList([">= 4.0.6", "< 4.1.1"])
301
302
if inject.checkBooleanExpression("'utf8'=(SELECT CHARSET(CURRENT_USER()))"):
303
Backend.setVersion("= 4.1.0")
304
else:
305
Backend.setVersionList([">= 4.0.6", "< 4.1.0"])
306
else:
307
Backend.setVersionList([">= 4.0.0", "< 4.0.6"])
308
else:
309
Backend.setVersion("< 4.0.0")
310
setDbms("%s 3" % DBMS.MYSQL)
311
self.getBanner()
312
313
kb.data.has_information_schema = False
314
315
return True
316
else:
317
warnMsg = "the back-end DBMS is not %s" % DBMS.MYSQL
318
logger.warning(warnMsg)
319
320
return False
321
322
def checkDbmsOs(self, detailed=False):
323
if Backend.getOs():
324
return
325
326
infoMsg = "fingerprinting the back-end DBMS operating system"
327
logger.info(infoMsg)
328
329
result = inject.checkBooleanExpression("'W'=UPPER(MID(@@version_compile_os,1,1))")
330
331
if result:
332
Backend.setOs(OS.WINDOWS)
333
elif not result:
334
Backend.setOs(OS.LINUX)
335
336
if Backend.getOs():
337
infoMsg = "the back-end DBMS operating system is %s" % Backend.getOs()
338
logger.info(infoMsg)
339
else:
340
self.userChooseDbmsOs()
341
342
self.cleanup(onlyFileTbl=True)
343
344