1#!/usr/bin/env python 2 3""" 4Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) 5See the file 'LICENSE' for copying permission 6""" 7 8import re 9 10from lib.core.agent import agent 11from lib.core.common import arrayizeValue 12from lib.core.common import getLimitRange 13from lib.core.common import isInferenceAvailable 14from lib.core.common import isNoneValue 15from lib.core.common import isNumPosStrValue 16from lib.core.common import isTechniqueAvailable 17from lib.core.common import safeSQLIdentificatorNaming 18from lib.core.common import safeStringFormat 19from lib.core.common import singleTimeLogMessage 20from lib.core.common import unArrayizeValue 21from lib.core.common import unsafeSQLIdentificatorNaming 22from lib.core.compat import xrange 23from lib.core.data import conf 24from lib.core.data import kb 25from lib.core.data import logger 26from lib.core.data import queries 27from lib.core.enums import CHARSET_TYPE 28from lib.core.enums import DBMS 29from lib.core.enums import EXPECTED 30from lib.core.enums import PAYLOAD 31from lib.core.exception import SqlmapNoneDataException 32from lib.core.settings import CURRENT_DB 33from lib.request import inject 34from plugins.generic.enumeration import Enumeration as GenericEnumeration 35from thirdparty import six 36 37class 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.warn(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.warn(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.warn(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.warn(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.warn(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.warn(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