Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sqlmapproject
GitHub Repository: sqlmapproject/sqlmap
Path: blob/master/plugins/dbms/mssqlserver/enumeration.py
2992 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 re
9
10
from lib.core.agent import agent
11
from lib.core.common import arrayizeValue
12
from lib.core.common import getLimitRange
13
from lib.core.common import isInferenceAvailable
14
from lib.core.common import isNoneValue
15
from lib.core.common import isNumPosStrValue
16
from lib.core.common import isTechniqueAvailable
17
from lib.core.common import safeSQLIdentificatorNaming
18
from lib.core.common import safeStringFormat
19
from lib.core.common import singleTimeLogMessage
20
from lib.core.common import unArrayizeValue
21
from lib.core.common import unsafeSQLIdentificatorNaming
22
from lib.core.compat import xrange
23
from lib.core.data import conf
24
from lib.core.data import kb
25
from lib.core.data import logger
26
from lib.core.data import queries
27
from lib.core.enums import CHARSET_TYPE
28
from lib.core.enums import DBMS
29
from lib.core.enums import EXPECTED
30
from lib.core.enums import PAYLOAD
31
from lib.core.exception import SqlmapNoneDataException
32
from lib.core.settings import CURRENT_DB
33
from lib.request import inject
34
from plugins.generic.enumeration import Enumeration as GenericEnumeration
35
from thirdparty import six
36
37
class Enumeration(GenericEnumeration):
38
def getPrivileges(self, *args, **kwargs):
39
warnMsg = "on Microsoft SQL Server it is not possible to fetch "
40
warnMsg += "database users privileges, sqlmap will check whether "
41
warnMsg += "or not the database users are database administrators"
42
logger.warning(warnMsg)
43
44
users = []
45
areAdmins = set()
46
47
if conf.user:
48
users = [conf.user]
49
elif not len(kb.data.cachedUsers):
50
users = self.getUsers()
51
else:
52
users = kb.data.cachedUsers
53
54
for user in users:
55
user = unArrayizeValue(user)
56
57
if user is None:
58
continue
59
60
isDba = self.isDba(user)
61
62
if isDba is True:
63
areAdmins.add(user)
64
65
kb.data.cachedUsersPrivileges[user] = None
66
67
return (kb.data.cachedUsersPrivileges, areAdmins)
68
69
def getTables(self):
70
if len(kb.data.cachedTables) > 0:
71
return kb.data.cachedTables
72
73
self.forceDbmsEnum()
74
75
if conf.db == CURRENT_DB:
76
conf.db = self.getCurrentDb()
77
78
if conf.db:
79
dbs = conf.db.split(',')
80
else:
81
dbs = self.getDbs()
82
83
for db in dbs:
84
dbs[dbs.index(db)] = safeSQLIdentificatorNaming(db)
85
86
dbs = [_ for _ in dbs if _]
87
88
infoMsg = "fetching tables for database"
89
infoMsg += "%s: %s" % ("s" if len(dbs) > 1 else "", ", ".join(db if isinstance(db, six.string_types) else db[0] for db in sorted(dbs)))
90
logger.info(infoMsg)
91
92
rootQuery = queries[DBMS.MSSQL].tables
93
94
if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct:
95
for db in dbs:
96
if conf.excludeSysDbs and db in self.excludeDbsList:
97
infoMsg = "skipping system database '%s'" % db
98
singleTimeLogMessage(infoMsg)
99
continue
100
101
if conf.exclude and re.search(conf.exclude, db, re.I) is not None:
102
infoMsg = "skipping database '%s'" % db
103
singleTimeLogMessage(infoMsg)
104
continue
105
106
for query in (rootQuery.inband.query, rootQuery.inband.query2, rootQuery.inband.query3):
107
query = query.replace("%s", db)
108
value = inject.getValue(query, blind=False, time=False)
109
if not isNoneValue(value):
110
break
111
112
if not isNoneValue(value):
113
value = [_ for _ in arrayizeValue(value) if _]
114
value = [safeSQLIdentificatorNaming(unArrayizeValue(_), True) for _ in value]
115
kb.data.cachedTables[db] = value
116
117
if not kb.data.cachedTables and isInferenceAvailable() and not conf.direct:
118
for db in dbs:
119
if conf.excludeSysDbs and db in self.excludeDbsList:
120
infoMsg = "skipping system database '%s'" % db
121
singleTimeLogMessage(infoMsg)
122
continue
123
124
if conf.exclude and re.search(conf.exclude, db, re.I) is not None:
125
infoMsg = "skipping database '%s'" % db
126
singleTimeLogMessage(infoMsg)
127
continue
128
129
infoMsg = "fetching number of tables for "
130
infoMsg += "database '%s'" % db
131
logger.info(infoMsg)
132
133
for query in (rootQuery.blind.count, rootQuery.blind.count2, rootQuery.blind.count3):
134
_ = query.replace("%s", db)
135
count = inject.getValue(_, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)
136
if not isNoneValue(count):
137
break
138
139
if not isNumPosStrValue(count):
140
if count != 0:
141
warnMsg = "unable to retrieve the number of "
142
warnMsg += "tables for database '%s'" % db
143
logger.warning(warnMsg)
144
continue
145
146
tables = []
147
148
for index in xrange(int(count)):
149
_ = safeStringFormat((rootQuery.blind.query if query == rootQuery.blind.count else rootQuery.blind.query2 if query == rootQuery.blind.count2 else rootQuery.blind.query3).replace("%s", db), index)
150
151
table = inject.getValue(_, union=False, error=False)
152
if not isNoneValue(table):
153
kb.hintValue = table
154
table = safeSQLIdentificatorNaming(table, True)
155
tables.append(table)
156
157
if tables:
158
kb.data.cachedTables[db] = tables
159
else:
160
warnMsg = "unable to retrieve the tables "
161
warnMsg += "for database '%s'" % db
162
logger.warning(warnMsg)
163
164
if not kb.data.cachedTables and not conf.search:
165
errMsg = "unable to retrieve the tables for any database"
166
raise SqlmapNoneDataException(errMsg)
167
else:
168
for db, tables in kb.data.cachedTables.items():
169
kb.data.cachedTables[db] = sorted(tables) if tables else tables
170
171
return kb.data.cachedTables
172
173
def searchTable(self):
174
foundTbls = {}
175
tblList = conf.tbl.split(',')
176
rootQuery = queries[DBMS.MSSQL].search_table
177
tblCond = rootQuery.inband.condition
178
tblConsider, tblCondParam = self.likeOrExact("table")
179
180
if conf.db == CURRENT_DB:
181
conf.db = self.getCurrentDb()
182
183
if conf.db:
184
enumDbs = conf.db.split(',')
185
elif not len(kb.data.cachedDbs):
186
enumDbs = self.getDbs()
187
else:
188
enumDbs = kb.data.cachedDbs
189
190
for db in enumDbs:
191
db = safeSQLIdentificatorNaming(db)
192
foundTbls[db] = []
193
194
for tbl in tblList:
195
tbl = safeSQLIdentificatorNaming(tbl, True)
196
197
infoMsg = "searching table"
198
if tblConsider == "1":
199
infoMsg += "s LIKE"
200
infoMsg += " '%s'" % unsafeSQLIdentificatorNaming(tbl)
201
logger.info(infoMsg)
202
203
tblQuery = "%s%s" % (tblCond, tblCondParam)
204
tblQuery = tblQuery % unsafeSQLIdentificatorNaming(tbl)
205
206
for db in foundTbls.keys():
207
db = safeSQLIdentificatorNaming(db)
208
209
if conf.excludeSysDbs and db in self.excludeDbsList:
210
infoMsg = "skipping system database '%s'" % db
211
singleTimeLogMessage(infoMsg)
212
continue
213
214
if conf.exclude and re.search(conf.exclude, db, re.I) is not None:
215
infoMsg = "skipping database '%s'" % db
216
singleTimeLogMessage(infoMsg)
217
continue
218
219
if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct:
220
query = rootQuery.inband.query.replace("%s", db)
221
query += tblQuery
222
values = inject.getValue(query, blind=False, time=False)
223
224
if not isNoneValue(values):
225
if isinstance(values, six.string_types):
226
values = [values]
227
228
for foundTbl in values:
229
if foundTbl is None:
230
continue
231
232
foundTbls[db].append(foundTbl)
233
else:
234
infoMsg = "fetching number of table"
235
if tblConsider == "1":
236
infoMsg += "s LIKE"
237
infoMsg += " '%s' in database '%s'" % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(db))
238
logger.info(infoMsg)
239
240
query = rootQuery.blind.count
241
query = query.replace("%s", db)
242
query += " AND %s" % tblQuery
243
count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)
244
245
if not isNumPosStrValue(count):
246
warnMsg = "no table"
247
if tblConsider == "1":
248
warnMsg += "s LIKE"
249
warnMsg += " '%s' " % unsafeSQLIdentificatorNaming(tbl)
250
warnMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(db)
251
logger.warning(warnMsg)
252
253
continue
254
255
indexRange = getLimitRange(count)
256
257
for index in indexRange:
258
query = rootQuery.blind.query
259
query = query.replace("%s", db)
260
query += " AND %s" % tblQuery
261
query = agent.limitQuery(index, query, tblCond)
262
tbl = inject.getValue(query, union=False, error=False)
263
kb.hintValue = tbl
264
foundTbls[db].append(tbl)
265
266
for db, tbls in list(foundTbls.items()):
267
if len(tbls) == 0:
268
foundTbls.pop(db)
269
270
if not foundTbls:
271
warnMsg = "no databases contain any of the provided tables"
272
logger.warning(warnMsg)
273
return
274
275
conf.dumper.dbTables(foundTbls)
276
self.dumpFoundTables(foundTbls)
277
278
def searchColumn(self):
279
rootQuery = queries[DBMS.MSSQL].search_column
280
foundCols = {}
281
dbs = {}
282
whereTblsQuery = ""
283
infoMsgTbl = ""
284
infoMsgDb = ""
285
colList = conf.col.split(',')
286
287
if conf.exclude:
288
colList = [_ for _ in colList if re.search(conf.exclude, _, re.I) is None]
289
290
origTbl = conf.tbl
291
origDb = conf.db
292
colCond = rootQuery.inband.condition
293
tblCond = rootQuery.inband.condition2
294
colConsider, colCondParam = self.likeOrExact("column")
295
296
if conf.db == CURRENT_DB:
297
conf.db = self.getCurrentDb()
298
299
if conf.db:
300
enumDbs = conf.db.split(',')
301
elif not len(kb.data.cachedDbs):
302
enumDbs = self.getDbs()
303
else:
304
enumDbs = kb.data.cachedDbs
305
306
for db in enumDbs:
307
db = safeSQLIdentificatorNaming(db)
308
dbs[db] = {}
309
310
for column in colList:
311
column = safeSQLIdentificatorNaming(column)
312
conf.db = origDb
313
conf.tbl = origTbl
314
315
infoMsg = "searching column"
316
if colConsider == "1":
317
infoMsg += "s LIKE"
318
infoMsg += " '%s'" % unsafeSQLIdentificatorNaming(column)
319
320
foundCols[column] = {}
321
322
if conf.tbl:
323
_ = conf.tbl.split(',')
324
whereTblsQuery = " AND (" + " OR ".join("%s = '%s'" % (tblCond, unsafeSQLIdentificatorNaming(tbl)) for tbl in _) + ")"
325
infoMsgTbl = " for table%s '%s'" % ("s" if len(_) > 1 else "", ", ".join(tbl for tbl in _))
326
327
if conf.db == CURRENT_DB:
328
conf.db = self.getCurrentDb()
329
330
if conf.db:
331
_ = conf.db.split(',')
332
infoMsgDb = " in database%s '%s'" % ("s" if len(_) > 1 else "", ", ".join(db for db in _))
333
elif conf.excludeSysDbs:
334
infoMsgDb = " not in system database%s '%s'" % ("s" if len(self.excludeDbsList) > 1 else "", ", ".join(db for db in self.excludeDbsList))
335
else:
336
infoMsgDb = " across all databases"
337
338
logger.info("%s%s%s" % (infoMsg, infoMsgTbl, infoMsgDb))
339
340
colQuery = "%s%s" % (colCond, colCondParam)
341
colQuery = colQuery % unsafeSQLIdentificatorNaming(column)
342
343
for db in (_ for _ in dbs if _):
344
db = safeSQLIdentificatorNaming(db)
345
346
if conf.excludeSysDbs and db in self.excludeDbsList:
347
continue
348
349
if conf.exclude and re.search(conf.exclude, db, re.I) is not None:
350
continue
351
352
if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct:
353
query = rootQuery.inband.query % (db, db, db, db, db, db)
354
query += " AND %s" % colQuery.replace("[DB]", db)
355
query += whereTblsQuery.replace("[DB]", db)
356
values = inject.getValue(query, blind=False, time=False)
357
358
if not isNoneValue(values):
359
if isinstance(values, six.string_types):
360
values = [values]
361
362
for foundTbl in values:
363
foundTbl = safeSQLIdentificatorNaming(unArrayizeValue(foundTbl), True)
364
365
if foundTbl is None:
366
continue
367
368
if foundTbl not in dbs[db]:
369
dbs[db][foundTbl] = {}
370
371
if colConsider == '1':
372
conf.db = db
373
conf.tbl = foundTbl
374
conf.col = column
375
376
self.getColumns(onlyColNames=True, colTuple=(colConsider, colCondParam), bruteForce=False)
377
378
if db in kb.data.cachedColumns and foundTbl in kb.data.cachedColumns[db] and not isNoneValue(kb.data.cachedColumns[db][foundTbl]):
379
dbs[db][foundTbl].update(kb.data.cachedColumns[db][foundTbl])
380
381
kb.data.cachedColumns = {}
382
else:
383
dbs[db][foundTbl][column] = None
384
385
if db in foundCols[column]:
386
foundCols[column][db].append(foundTbl)
387
else:
388
foundCols[column][db] = [foundTbl]
389
else:
390
foundCols[column][db] = []
391
392
infoMsg = "fetching number of tables containing column"
393
if colConsider == "1":
394
infoMsg += "s LIKE"
395
infoMsg += " '%s' in database '%s'" % (column, db)
396
logger.info("%s%s" % (infoMsg, infoMsgTbl))
397
398
query = rootQuery.blind.count
399
query = query % (db, db, db, db, db, db)
400
query += " AND %s" % colQuery.replace("[DB]", db)
401
query += whereTblsQuery.replace("[DB]", db)
402
count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)
403
404
if not isNumPosStrValue(count):
405
warnMsg = "no tables contain column"
406
if colConsider == "1":
407
warnMsg += "s LIKE"
408
warnMsg += " '%s' " % column
409
warnMsg += "in database '%s'" % db
410
logger.warning(warnMsg)
411
412
continue
413
414
indexRange = getLimitRange(count)
415
416
for index in indexRange:
417
query = rootQuery.blind.query
418
query = query % (db, db, db, db, db, db)
419
query += " AND %s" % colQuery.replace("[DB]", db)
420
query += whereTblsQuery.replace("[DB]", db)
421
query = agent.limitQuery(index, query, colCond.replace("[DB]", db))
422
tbl = inject.getValue(query, union=False, error=False)
423
kb.hintValue = tbl
424
425
tbl = safeSQLIdentificatorNaming(tbl, True)
426
427
if tbl not in dbs[db]:
428
dbs[db][tbl] = {}
429
430
if colConsider == "1":
431
conf.db = db
432
conf.tbl = tbl
433
conf.col = column
434
435
self.getColumns(onlyColNames=True, colTuple=(colConsider, colCondParam), bruteForce=False)
436
437
if db in kb.data.cachedColumns and tbl in kb.data.cachedColumns[db]:
438
dbs[db][tbl].update(kb.data.cachedColumns[db][tbl])
439
kb.data.cachedColumns = {}
440
else:
441
dbs[db][tbl][column] = None
442
443
foundCols[column][db].append(tbl)
444
445
conf.dumper.dbColumns(foundCols, colConsider, dbs)
446
self.dumpFoundColumn(dbs, foundCols, colConsider)
447
448