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 Backend 13from lib.core.common import extractRegexResult 14from lib.core.common import filterNone 15from lib.core.common import filterPairValues 16from lib.core.common import flattenValue 17from lib.core.common import getLimitRange 18from lib.core.common import isInferenceAvailable 19from lib.core.common import isListLike 20from lib.core.common import isNoneValue 21from lib.core.common import isNumPosStrValue 22from lib.core.common import isTechniqueAvailable 23from lib.core.common import parseSqliteTableSchema 24from lib.core.common import popValue 25from lib.core.common import pushValue 26from lib.core.common import randomStr 27from lib.core.common import readInput 28from lib.core.common import safeSQLIdentificatorNaming 29from lib.core.common import singleTimeLogMessage 30from lib.core.common import singleTimeWarnMessage 31from lib.core.common import unArrayizeValue 32from lib.core.common import unsafeSQLIdentificatorNaming 33from lib.core.data import conf 34from lib.core.data import kb 35from lib.core.data import logger 36from lib.core.data import paths 37from lib.core.data import queries 38from lib.core.decorators import stackedmethod 39from lib.core.dicts import FIREBIRD_TYPES 40from lib.core.dicts import INFORMIX_TYPES 41from lib.core.enums import CHARSET_TYPE 42from lib.core.enums import DBMS 43from lib.core.enums import EXPECTED 44from lib.core.enums import PAYLOAD 45from lib.core.exception import SqlmapMissingMandatoryOptionException 46from lib.core.exception import SqlmapNoneDataException 47from lib.core.exception import SqlmapUserQuitException 48from lib.core.settings import CURRENT_DB 49from lib.core.settings import REFLECTED_VALUE_MARKER 50from lib.request import inject 51from lib.techniques.union.use import unionUse 52from lib.utils.brute import columnExists 53from lib.utils.brute import tableExists 54from thirdparty import six 55 56class Databases(object): 57 """ 58 This class defines databases' enumeration functionalities for plugins. 59 """ 60 61 def __init__(self): 62 kb.data.currentDb = "" 63 kb.data.cachedDbs = [] 64 kb.data.cachedTables = {} 65 kb.data.cachedColumns = {} 66 kb.data.cachedCounts = {} 67 kb.data.dumpedTable = {} 68 kb.data.cachedStatements = [] 69 70 def getCurrentDb(self): 71 infoMsg = "fetching current database" 72 logger.info(infoMsg) 73 74 query = queries[Backend.getIdentifiedDbms()].current_db.query 75 76 if not kb.data.currentDb: 77 kb.data.currentDb = unArrayizeValue(inject.getValue(query, safeCharEncode=False)) 78 79 if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.PGSQL): 80 warnMsg = "on %s you'll need to use " % Backend.getIdentifiedDbms() 81 warnMsg += "schema names for enumeration as the counterpart to database " 82 warnMsg += "names on other DBMSes" 83 singleTimeWarnMessage(warnMsg) 84 85 return kb.data.currentDb 86 87 def getDbs(self): 88 if len(kb.data.cachedDbs) > 0: 89 return kb.data.cachedDbs 90 91 infoMsg = None 92 93 if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema: 94 warnMsg = "information_schema not available, " 95 warnMsg += "back-end DBMS is MySQL < 5. database " 96 warnMsg += "names will be fetched from 'mysql' database" 97 logger.warn(warnMsg) 98 99 elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.PGSQL): 100 warnMsg = "schema names are going to be used on %s " % Backend.getIdentifiedDbms() 101 warnMsg += "for enumeration as the counterpart to database " 102 warnMsg += "names on other DBMSes" 103 logger.warn(warnMsg) 104 105 infoMsg = "fetching database (schema) names" 106 107 else: 108 infoMsg = "fetching database names" 109 110 if infoMsg: 111 logger.info(infoMsg) 112 113 rootQuery = queries[Backend.getIdentifiedDbms()].dbs 114 115 if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: 116 if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema: 117 query = rootQuery.inband.query2 118 else: 119 query = rootQuery.inband.query 120 values = inject.getValue(query, blind=False, time=False) 121 122 if not isNoneValue(values): 123 kb.data.cachedDbs = arrayizeValue(values) 124 125 if not kb.data.cachedDbs and isInferenceAvailable() and not conf.direct: 126 infoMsg = "fetching number of databases" 127 logger.info(infoMsg) 128 129 if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema: 130 query = rootQuery.blind.count2 131 else: 132 query = rootQuery.blind.count 133 count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) 134 135 if not isNumPosStrValue(count): 136 errMsg = "unable to retrieve the number of databases" 137 logger.error(errMsg) 138 else: 139 plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) 140 indexRange = getLimitRange(count, plusOne=plusOne) 141 142 for index in indexRange: 143 if Backend.isDbms(DBMS.SYBASE): 144 query = rootQuery.blind.query % (kb.data.cachedDbs[-1] if kb.data.cachedDbs else " ") 145 elif Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema: 146 query = rootQuery.blind.query2 % index 147 else: 148 query = rootQuery.blind.query % index 149 150 db = unArrayizeValue(inject.getValue(query, union=False, error=False)) 151 152 if not isNoneValue(db): 153 kb.data.cachedDbs.append(safeSQLIdentificatorNaming(db)) 154 155 if not kb.data.cachedDbs and Backend.isDbms(DBMS.MSSQL): 156 if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: 157 blinds = (False, True) 158 else: 159 blinds = (True,) 160 161 for blind in blinds: 162 count = 0 163 kb.data.cachedDbs = [] 164 while True: 165 query = rootQuery.inband.query2 % count 166 value = unArrayizeValue(inject.getValue(query, blind=blind)) 167 if not (value or "").strip(): 168 break 169 else: 170 kb.data.cachedDbs.append(value) 171 count += 1 172 if kb.data.cachedDbs: 173 break 174 175 if not kb.data.cachedDbs: 176 infoMsg = "falling back to current database" 177 logger.info(infoMsg) 178 self.getCurrentDb() 179 180 if kb.data.currentDb: 181 kb.data.cachedDbs = [kb.data.currentDb] 182 else: 183 errMsg = "unable to retrieve the database names" 184 raise SqlmapNoneDataException(errMsg) 185 else: 186 kb.data.cachedDbs.sort() 187 188 if kb.data.cachedDbs: 189 kb.data.cachedDbs = [_ for _ in set(flattenValue(kb.data.cachedDbs)) if _] 190 191 return kb.data.cachedDbs 192 193 def getTables(self, bruteForce=None): 194 if len(kb.data.cachedTables) > 0: 195 return kb.data.cachedTables 196 197 self.forceDbmsEnum() 198 199 if bruteForce is None: 200 if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema: 201 errMsg = "information_schema not available, " 202 errMsg += "back-end DBMS is MySQL < 5.0" 203 logger.error(errMsg) 204 bruteForce = True 205 206 elif Backend.isDbms(DBMS.ACCESS): 207 try: 208 tables = self.getTables(False) 209 except SqlmapNoneDataException: 210 tables = None 211 212 if not tables: 213 errMsg = "cannot retrieve table names, " 214 errMsg += "back-end DBMS is Access" 215 logger.error(errMsg) 216 bruteForce = True 217 else: 218 return tables 219 220 if conf.db == CURRENT_DB: 221 conf.db = self.getCurrentDb() 222 223 if conf.db and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB): 224 conf.db = conf.db.upper() 225 226 if conf.db: 227 dbs = conf.db.split(',') 228 else: 229 dbs = self.getDbs() 230 231 dbs = [_ for _ in dbs if _ and _.strip()] 232 233 for db in dbs: 234 dbs[dbs.index(db)] = safeSQLIdentificatorNaming(db) 235 236 if bruteForce: 237 resumeAvailable = False 238 239 for db, table in kb.brute.tables: 240 if db == conf.db: 241 resumeAvailable = True 242 break 243 244 if resumeAvailable and not conf.freshQueries: 245 for db, table in kb.brute.tables: 246 if db == conf.db: 247 if conf.db not in kb.data.cachedTables: 248 kb.data.cachedTables[conf.db] = [table] 249 else: 250 kb.data.cachedTables[conf.db].append(table) 251 252 return kb.data.cachedTables 253 254 message = "do you want to use common table existence check? %s " % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS,) else "[y/N/q]") 255 choice = readInput(message, default='Y' if 'Y' in message else 'N').upper() 256 257 if choice == 'N': 258 return 259 elif choice == 'Q': 260 raise SqlmapUserQuitException 261 else: 262 return tableExists(paths.COMMON_TABLES) 263 264 infoMsg = "fetching tables for database" 265 infoMsg += "%s: '%s'" % ("s" if len(dbs) > 1 else "", ", ".join(unsafeSQLIdentificatorNaming(unArrayizeValue(db)) for db in sorted(dbs))) 266 logger.info(infoMsg) 267 268 rootQuery = queries[Backend.getIdentifiedDbms()].tables 269 270 if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: 271 values = [] 272 273 for query, condition in ((rootQuery.inband.query, getattr(rootQuery.inband, "condition", None)), (getattr(rootQuery.inband, "query2", None), getattr(rootQuery.inband, "condition2", None))): 274 if not isNoneValue(values) or not query: 275 break 276 277 if condition: 278 if not Backend.isDbms(DBMS.SQLITE): 279 query += " WHERE %s" % condition 280 281 if conf.excludeSysDbs: 282 infoMsg = "skipping system database%s '%s'" % ("s" if len(self.excludeDbsList) > 1 else "", ", ".join(unsafeSQLIdentificatorNaming(db) for db in self.excludeDbsList)) 283 logger.info(infoMsg) 284 query += " IN (%s)" % ','.join("'%s'" % unsafeSQLIdentificatorNaming(db) for db in sorted(dbs) if db not in self.excludeDbsList) 285 else: 286 query += " IN (%s)" % ','.join("'%s'" % unsafeSQLIdentificatorNaming(db) for db in sorted(dbs)) 287 288 if len(dbs) < 2 and ("%s," % condition) in query: 289 query = query.replace("%s," % condition, "", 1) 290 291 if query: 292 values = inject.getValue(query, blind=False, time=False) 293 294 if not isNoneValue(values): 295 values = [_ for _ in arrayizeValue(values) if _] 296 297 if len(values) > 0 and not isListLike(values[0]): 298 values = [(dbs[0], _) for _ in values] 299 300 for db, table in filterPairValues(values): 301 table = unArrayizeValue(table) 302 303 if not isNoneValue(table): 304 db = safeSQLIdentificatorNaming(db) 305 table = safeSQLIdentificatorNaming(table, True) 306 307 if conf.getComments: 308 _ = queries[Backend.getIdentifiedDbms()].table_comment 309 if hasattr(_, "query"): 310 if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): 311 query = _.query % (unsafeSQLIdentificatorNaming(db.upper()), unsafeSQLIdentificatorNaming(table.upper())) 312 else: 313 query = _.query % (unsafeSQLIdentificatorNaming(db), unsafeSQLIdentificatorNaming(table)) 314 315 comment = unArrayizeValue(inject.getValue(query, blind=False, time=False)) 316 if not isNoneValue(comment): 317 infoMsg = "retrieved comment '%s' for table '%s' " % (comment, unsafeSQLIdentificatorNaming(table)) 318 infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(db) 319 logger.info(infoMsg) 320 else: 321 warnMsg = "on %s it is not " % Backend.getIdentifiedDbms() 322 warnMsg += "possible to get table comments" 323 singleTimeWarnMessage(warnMsg) 324 325 if db not in kb.data.cachedTables: 326 kb.data.cachedTables[db] = [table] 327 else: 328 kb.data.cachedTables[db].append(table) 329 330 if not kb.data.cachedTables and isInferenceAvailable() and not conf.direct: 331 for db in dbs: 332 if conf.excludeSysDbs and db in self.excludeDbsList: 333 infoMsg = "skipping system database '%s'" % unsafeSQLIdentificatorNaming(db) 334 logger.info(infoMsg) 335 continue 336 337 if conf.exclude and re.search(conf.exclude, db, re.I) is not None: 338 infoMsg = "skipping database '%s'" % unsafeSQLIdentificatorNaming(db) 339 singleTimeLogMessage(infoMsg) 340 continue 341 342 infoMsg = "fetching number of tables for " 343 infoMsg += "database '%s'" % unsafeSQLIdentificatorNaming(db) 344 logger.info(infoMsg) 345 346 if Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD, DBMS.MAXDB, DBMS.ACCESS): 347 query = rootQuery.blind.count 348 else: 349 query = rootQuery.blind.count % unsafeSQLIdentificatorNaming(db) 350 351 count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) 352 353 if count == 0: 354 warnMsg = "database '%s' " % unsafeSQLIdentificatorNaming(db) 355 warnMsg += "appears to be empty" 356 logger.warn(warnMsg) 357 continue 358 359 elif not isNumPosStrValue(count): 360 warnMsg = "unable to retrieve the number of " 361 warnMsg += "tables for database '%s'" % unsafeSQLIdentificatorNaming(db) 362 logger.warn(warnMsg) 363 continue 364 365 tables = [] 366 367 plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) 368 indexRange = getLimitRange(count, plusOne=plusOne) 369 370 for index in indexRange: 371 if Backend.isDbms(DBMS.SYBASE): 372 query = rootQuery.blind.query % (db, (kb.data.cachedTables[-1] if kb.data.cachedTables else " ")) 373 elif Backend.getIdentifiedDbms() in (DBMS.MAXDB, DBMS.ACCESS): 374 query = rootQuery.blind.query % (kb.data.cachedTables[-1] if kb.data.cachedTables else " ") 375 elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD): 376 query = rootQuery.blind.query % index 377 elif Backend.getIdentifiedDbms() in (DBMS.HSQLDB, DBMS.INFORMIX): 378 query = rootQuery.blind.query % (index, unsafeSQLIdentificatorNaming(db)) 379 else: 380 query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(db), index) 381 382 table = unArrayizeValue(inject.getValue(query, union=False, error=False)) 383 384 if not isNoneValue(table): 385 kb.hintValue = table 386 table = safeSQLIdentificatorNaming(table, True) 387 tables.append(table) 388 389 if conf.getComments: 390 _ = queries[Backend.getIdentifiedDbms()].table_comment 391 if hasattr(_, "query"): 392 if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): 393 query = _.query % (unsafeSQLIdentificatorNaming(db.upper()), unsafeSQLIdentificatorNaming(table.upper())) 394 else: 395 query = _.query % (unsafeSQLIdentificatorNaming(db), unsafeSQLIdentificatorNaming(table)) 396 397 comment = unArrayizeValue(inject.getValue(query, union=False, error=False)) 398 if not isNoneValue(comment): 399 infoMsg = "retrieved comment '%s' for table '%s' " % (comment, unsafeSQLIdentificatorNaming(table)) 400 infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(db) 401 logger.info(infoMsg) 402 else: 403 warnMsg = "on %s it is not " % Backend.getIdentifiedDbms() 404 warnMsg += "possible to get table comments" 405 singleTimeWarnMessage(warnMsg) 406 407 if tables: 408 kb.data.cachedTables[db] = tables 409 else: 410 warnMsg = "unable to retrieve the table names " 411 warnMsg += "for database '%s'" % unsafeSQLIdentificatorNaming(db) 412 logger.warn(warnMsg) 413 414 if isNoneValue(kb.data.cachedTables): 415 kb.data.cachedTables.clear() 416 417 if not kb.data.cachedTables: 418 errMsg = "unable to retrieve the table names for any database" 419 if bruteForce is None: 420 logger.error(errMsg) 421 return self.getTables(bruteForce=True) 422 elif not conf.search: 423 raise SqlmapNoneDataException(errMsg) 424 else: 425 for db, tables in kb.data.cachedTables.items(): 426 kb.data.cachedTables[db] = sorted(tables) if tables else tables 427 428 if kb.data.cachedTables: 429 for db in kb.data.cachedTables: 430 kb.data.cachedTables[db] = list(set(kb.data.cachedTables[db])) 431 432 return kb.data.cachedTables 433 434 def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMode=False): 435 self.forceDbmsEnum() 436 437 if conf.db is None or conf.db == CURRENT_DB: 438 if conf.db is None: 439 warnMsg = "missing database parameter. sqlmap is going " 440 warnMsg += "to use the current database to enumerate " 441 warnMsg += "table(s) columns" 442 logger.warn(warnMsg) 443 444 conf.db = self.getCurrentDb() 445 446 if not conf.db: 447 errMsg = "unable to retrieve the current " 448 errMsg += "database name" 449 raise SqlmapNoneDataException(errMsg) 450 451 elif conf.db is not None: 452 if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB, DBMS.H2): 453 conf.db = conf.db.upper() 454 455 if ',' in conf.db: 456 errMsg = "only one database name is allowed when enumerating " 457 errMsg += "the tables' columns" 458 raise SqlmapMissingMandatoryOptionException(errMsg) 459 460 conf.db = safeSQLIdentificatorNaming(conf.db) 461 462 if conf.col: 463 if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): 464 conf.col = conf.col.upper() 465 466 colList = conf.col.split(',') 467 else: 468 colList = [] 469 470 if conf.exclude: 471 colList = [_ for _ in colList if re.search(conf.exclude, _, re.I) is None] 472 473 for col in colList: 474 colList[colList.index(col)] = safeSQLIdentificatorNaming(col) 475 476 colList = [_ for _ in colList if _] 477 478 if conf.tbl: 479 if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB, DBMS.H2): 480 conf.tbl = conf.tbl.upper() 481 482 tblList = conf.tbl.split(',') 483 else: 484 self.getTables() 485 486 if len(kb.data.cachedTables) > 0: 487 if conf.db in kb.data.cachedTables: 488 tblList = kb.data.cachedTables[conf.db] 489 else: 490 tblList = list(six.itervalues(kb.data.cachedTables)) 491 492 if tblList and isListLike(tblList[0]): 493 tblList = tblList[0] 494 495 tblList = list(tblList) 496 elif not conf.search: 497 errMsg = "unable to retrieve the tables " 498 errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) 499 raise SqlmapNoneDataException(errMsg) 500 else: 501 return kb.data.cachedColumns 502 503 tblList = filterNone(safeSQLIdentificatorNaming(_, True) for _ in tblList) 504 505 if bruteForce is None: 506 if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema: 507 errMsg = "information_schema not available, " 508 errMsg += "back-end DBMS is MySQL < 5.0" 509 logger.error(errMsg) 510 bruteForce = True 511 512 elif Backend.isDbms(DBMS.ACCESS): 513 errMsg = "cannot retrieve column names, " 514 errMsg += "back-end DBMS is %s" % DBMS.ACCESS 515 logger.error(errMsg) 516 bruteForce = True 517 518 if bruteForce: 519 resumeAvailable = False 520 521 for tbl in tblList: 522 for db, table, colName, colType in kb.brute.columns: 523 if db == conf.db and table == tbl: 524 resumeAvailable = True 525 break 526 527 if resumeAvailable and not conf.freshQueries or colList: 528 columns = {} 529 530 for column in colList: 531 columns[column] = None 532 533 for tbl in tblList: 534 for db, table, colName, colType in kb.brute.columns: 535 if db == conf.db and table == tbl: 536 columns[colName] = colType 537 538 if conf.db in kb.data.cachedColumns: 539 kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)] = columns 540 else: 541 kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = {safeSQLIdentificatorNaming(tbl, True): columns} 542 543 return kb.data.cachedColumns 544 545 message = "do you want to use common column existence check? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS,) else "[y/N/q]") 546 choice = readInput(message, default='Y' if 'Y' in message else 'N').upper() 547 548 if choice == 'N': 549 return 550 elif choice == 'Q': 551 raise SqlmapUserQuitException 552 else: 553 return columnExists(paths.COMMON_COLUMNS) 554 555 rootQuery = queries[Backend.getIdentifiedDbms()].columns 556 condition = rootQuery.blind.condition if 'condition' in rootQuery.blind else None 557 558 if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: 559 for tbl in tblList: 560 if conf.db is not None and len(kb.data.cachedColumns) > 0 \ 561 and conf.db in kb.data.cachedColumns and tbl in \ 562 kb.data.cachedColumns[conf.db]: 563 infoMsg = "fetched tables' columns on " 564 infoMsg += "database '%s'" % unsafeSQLIdentificatorNaming(conf.db) 565 logger.info(infoMsg) 566 567 return {conf.db: kb.data.cachedColumns[conf.db]} 568 569 infoMsg = "fetching columns " 570 condQuery = "" 571 572 if len(colList) > 0: 573 if colTuple: 574 _, colCondParam = colTuple 575 infoMsg += "LIKE '%s' " % ", ".join(unsafeSQLIdentificatorNaming(col) for col in sorted(colList)) 576 else: 577 colCondParam = "='%s'" 578 infoMsg += "'%s' " % ", ".join(unsafeSQLIdentificatorNaming(col) for col in sorted(colList)) 579 580 condQueryStr = "%%s%s" % colCondParam 581 condQuery = " AND (%s)" % " OR ".join(condQueryStr % (condition, unsafeSQLIdentificatorNaming(col)) for col in sorted(colList)) 582 583 if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2): 584 query = rootQuery.inband.query % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) 585 query += condQuery 586 587 elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): 588 query = rootQuery.inband.query % (unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper())) 589 query += condQuery 590 591 elif Backend.isDbms(DBMS.MSSQL): 592 query = rootQuery.inband.query % (conf.db, conf.db, conf.db, conf.db, 593 conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl).split(".")[-1]) 594 query += condQuery.replace("[DB]", conf.db) 595 596 elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD): 597 query = rootQuery.inband.query % unsafeSQLIdentificatorNaming(tbl) 598 599 elif Backend.isDbms(DBMS.INFORMIX): 600 query = rootQuery.inband.query % (conf.db, conf.db, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl)) 601 query += condQuery 602 603 if dumpMode and colList: 604 values = [(_,) for _ in colList] 605 else: 606 infoMsg += "for table '%s' " % unsafeSQLIdentificatorNaming(tbl) 607 infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) 608 logger.info(infoMsg) 609 610 values = None 611 if Backend.isDbms(DBMS.MSSQL) and isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION): 612 expression = query 613 kb.dumpColumns = [] 614 kb.rowXmlMode = True 615 616 for column in (extractRegexResult(r"SELECT (?P<result>.+?) FROM", query) or "").split(','): 617 kb.dumpColumns.append(randomStr().lower()) 618 expression = expression.replace(column, "%s AS %s" % (column, kb.dumpColumns[-1]), 1) 619 620 values = unionUse(expression) 621 kb.rowXmlMode = False 622 kb.dumpColumns = None 623 624 if values is None: 625 values = inject.getValue(query, blind=False, time=False) 626 if values and isinstance(values[0], six.string_types): 627 values = [values] 628 629 if Backend.isDbms(DBMS.MSSQL) and isNoneValue(values): 630 index, values = 1, [] 631 632 while True: 633 query = rootQuery.inband.query2 % (conf.db, unsafeSQLIdentificatorNaming(tbl), index) 634 value = unArrayizeValue(inject.getValue(query, blind=False, time=False)) 635 636 if isNoneValue(value) or value == " ": 637 break 638 else: 639 values.append((value,)) 640 index += 1 641 642 if Backend.isDbms(DBMS.SQLITE): 643 if dumpMode and colList: 644 if conf.db not in kb.data.cachedColumns: 645 kb.data.cachedColumns[conf.db] = {} 646 kb.data.cachedColumns[conf.db][safeSQLIdentificatorNaming(conf.tbl, True)] = dict((_, None) for _ in colList) 647 else: 648 parseSqliteTableSchema(unArrayizeValue(values)) 649 650 elif not isNoneValue(values): 651 table = {} 652 columns = {} 653 654 for columnData in values: 655 if not isNoneValue(columnData): 656 columnData = [unArrayizeValue(_) for _ in columnData] 657 name = safeSQLIdentificatorNaming(columnData[0]) 658 659 if name: 660 if conf.getComments: 661 _ = queries[Backend.getIdentifiedDbms()].column_comment 662 if hasattr(_, "query"): 663 if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): 664 query = _.query % (unsafeSQLIdentificatorNaming(conf.db.upper()), unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(name.upper())) 665 else: 666 query = _.query % (unsafeSQLIdentificatorNaming(conf.db), unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(name)) 667 668 comment = unArrayizeValue(inject.getValue(query, blind=False, time=False)) 669 if not isNoneValue(comment): 670 infoMsg = "retrieved comment '%s' for column '%s'" % (comment, name) 671 logger.info(infoMsg) 672 else: 673 warnMsg = "on %s it is not " % Backend.getIdentifiedDbms() 674 warnMsg += "possible to get column comments" 675 singleTimeWarnMessage(warnMsg) 676 677 if len(columnData) == 1: 678 columns[name] = None 679 else: 680 key = int(columnData[1]) if isinstance(columnData[1], six.string_types) and columnData[1].isdigit() else columnData[1] 681 if Backend.isDbms(DBMS.FIREBIRD): 682 columnData[1] = FIREBIRD_TYPES.get(key, columnData[1]) 683 elif Backend.isDbms(DBMS.INFORMIX): 684 notNull = False 685 if isinstance(key, int) and key > 255: 686 key -= 256 687 notNull = True 688 columnData[1] = INFORMIX_TYPES.get(key, columnData[1]) 689 if notNull: 690 columnData[1] = "%s NOT NULL" % columnData[1] 691 692 columns[name] = columnData[1] 693 694 if conf.db in kb.data.cachedColumns: 695 kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)] = columns 696 else: 697 table[safeSQLIdentificatorNaming(tbl, True)] = columns 698 kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = table 699 700 elif isInferenceAvailable() and not conf.direct: 701 for tbl in tblList: 702 if conf.db is not None and len(kb.data.cachedColumns) > 0 \ 703 and conf.db in kb.data.cachedColumns and tbl in \ 704 kb.data.cachedColumns[conf.db]: 705 infoMsg = "fetched tables' columns on " 706 infoMsg += "database '%s'" % unsafeSQLIdentificatorNaming(conf.db) 707 logger.info(infoMsg) 708 709 return {conf.db: kb.data.cachedColumns[conf.db]} 710 711 infoMsg = "fetching columns " 712 condQuery = "" 713 714 if len(colList) > 0: 715 if colTuple: 716 _, colCondParam = colTuple 717 infoMsg += "LIKE '%s' " % ", ".join(unsafeSQLIdentificatorNaming(col) for col in sorted(colList)) 718 else: 719 colCondParam = "='%s'" 720 infoMsg += "'%s' " % ", ".join(unsafeSQLIdentificatorNaming(col) for col in sorted(colList)) 721 722 condQueryStr = "%%s%s" % colCondParam 723 condQuery = " AND (%s)" % " OR ".join(condQueryStr % (condition, unsafeSQLIdentificatorNaming(col)) for col in sorted(colList)) 724 725 if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2): 726 query = rootQuery.blind.count % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) 727 query += condQuery 728 729 elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): 730 query = rootQuery.blind.count % (unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper())) 731 query += condQuery 732 733 elif Backend.isDbms(DBMS.MSSQL): 734 query = rootQuery.blind.count % (conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl).split(".")[-1]) 735 query += condQuery.replace("[DB]", conf.db) 736 737 elif Backend.isDbms(DBMS.FIREBIRD): 738 query = rootQuery.blind.count % unsafeSQLIdentificatorNaming(tbl) 739 query += condQuery 740 741 elif Backend.isDbms(DBMS.INFORMIX): 742 query = rootQuery.blind.count % (conf.db, conf.db, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl)) 743 query += condQuery 744 745 elif Backend.isDbms(DBMS.SQLITE): 746 if dumpMode and colList: 747 if conf.db not in kb.data.cachedColumns: 748 kb.data.cachedColumns[conf.db] = {} 749 kb.data.cachedColumns[conf.db][safeSQLIdentificatorNaming(conf.tbl, True)] = dict((_, None) for _ in colList) 750 else: 751 query = rootQuery.blind.query % unsafeSQLIdentificatorNaming(tbl) 752 value = unArrayizeValue(inject.getValue(query, union=False, error=False)) 753 parseSqliteTableSchema(unArrayizeValue(value)) 754 755 return kb.data.cachedColumns 756 757 table = {} 758 columns = {} 759 760 if dumpMode and colList: 761 count = 0 762 for value in colList: 763 columns[safeSQLIdentificatorNaming(value)] = None 764 else: 765 infoMsg += "for table '%s' " % unsafeSQLIdentificatorNaming(tbl) 766 infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) 767 logger.info(infoMsg) 768 769 count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) 770 771 if not isNumPosStrValue(count): 772 if Backend.isDbms(DBMS.MSSQL): 773 count, index, values = 0, 1, [] 774 while True: 775 query = rootQuery.blind.query3 % (conf.db, unsafeSQLIdentificatorNaming(tbl), index) 776 value = unArrayizeValue(inject.getValue(query, union=False, error=False)) 777 778 if isNoneValue(value) or value == " ": 779 break 780 else: 781 columns[safeSQLIdentificatorNaming(value)] = None 782 index += 1 783 784 if not columns: 785 errMsg = "unable to retrieve the %scolumns " % ("number of " if not Backend.isDbms(DBMS.MSSQL) else "") 786 errMsg += "for table '%s' " % unsafeSQLIdentificatorNaming(tbl) 787 errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) 788 logger.error(errMsg) 789 continue 790 791 for index in getLimitRange(count): 792 if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB): 793 query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) 794 query += condQuery 795 field = None 796 elif Backend.isDbms(DBMS.H2): 797 query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) 798 query = query.replace(" ORDER BY ", "%s ORDER BY " % condQuery) 799 field = None 800 elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): 801 query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper())) 802 query += condQuery 803 field = None 804 elif Backend.isDbms(DBMS.MSSQL): 805 query = rootQuery.blind.query.replace("'%s'", "'%s'" % unsafeSQLIdentificatorNaming(tbl).split(".")[-1]).replace("%s", conf.db).replace("%d", str(index)) 806 query += condQuery.replace("[DB]", conf.db) 807 field = condition.replace("[DB]", conf.db) 808 elif Backend.isDbms(DBMS.FIREBIRD): 809 query = rootQuery.blind.query % unsafeSQLIdentificatorNaming(tbl) 810 query += condQuery 811 field = None 812 elif Backend.isDbms(DBMS.INFORMIX): 813 query = rootQuery.blind.query % (index, conf.db, conf.db, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl)) 814 query += condQuery 815 field = condition 816 817 query = agent.limitQuery(index, query, field, field) 818 column = unArrayizeValue(inject.getValue(query, union=False, error=False)) 819 820 if not isNoneValue(column): 821 if conf.getComments: 822 _ = queries[Backend.getIdentifiedDbms()].column_comment 823 if hasattr(_, "query"): 824 if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): 825 query = _.query % (unsafeSQLIdentificatorNaming(conf.db.upper()), unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(column.upper())) 826 else: 827 query = _.query % (unsafeSQLIdentificatorNaming(conf.db), unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(column)) 828 829 comment = unArrayizeValue(inject.getValue(query, union=False, error=False)) 830 if not isNoneValue(comment): 831 infoMsg = "retrieved comment '%s' for column '%s'" % (comment, column) 832 logger.info(infoMsg) 833 else: 834 warnMsg = "on %s it is not " % Backend.getIdentifiedDbms() 835 warnMsg += "possible to get column comments" 836 singleTimeWarnMessage(warnMsg) 837 838 if not onlyColNames: 839 if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2): 840 query = rootQuery.blind.query2 % (unsafeSQLIdentificatorNaming(tbl), column, unsafeSQLIdentificatorNaming(conf.db)) 841 elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): 842 query = rootQuery.blind.query2 % (unsafeSQLIdentificatorNaming(tbl.upper()), column, unsafeSQLIdentificatorNaming(conf.db.upper())) 843 elif Backend.isDbms(DBMS.MSSQL): 844 query = rootQuery.blind.query2 % (conf.db, conf.db, conf.db, conf.db, column, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl).split(".")[-1]) 845 elif Backend.isDbms(DBMS.FIREBIRD): 846 query = rootQuery.blind.query2 % (unsafeSQLIdentificatorNaming(tbl), column) 847 elif Backend.isDbms(DBMS.INFORMIX): 848 query = rootQuery.blind.query2 % (conf.db, conf.db, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl), column) 849 850 colType = unArrayizeValue(inject.getValue(query, union=False, error=False)) 851 key = int(colType) if hasattr(colType, "isdigit") and colType.isdigit() else colType 852 853 if Backend.isDbms(DBMS.FIREBIRD): 854 colType = FIREBIRD_TYPES.get(key, colType) 855 elif Backend.isDbms(DBMS.INFORMIX): 856 notNull = False 857 if isinstance(key, int) and key > 255: 858 key -= 256 859 notNull = True 860 colType = INFORMIX_TYPES.get(key, colType) 861 if notNull: 862 colType = "%s NOT NULL" % colType 863 864 column = safeSQLIdentificatorNaming(column) 865 columns[column] = colType 866 else: 867 column = safeSQLIdentificatorNaming(column) 868 columns[column] = None 869 870 if columns: 871 if conf.db in kb.data.cachedColumns: 872 kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)] = columns 873 else: 874 table[safeSQLIdentificatorNaming(tbl, True)] = columns 875 kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = table 876 877 if not kb.data.cachedColumns: 878 warnMsg = "unable to retrieve column names for " 879 warnMsg += ("table '%s' " % unsafeSQLIdentificatorNaming(unArrayizeValue(tblList))) if len(tblList) == 1 else "any table " 880 warnMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) 881 logger.warn(warnMsg) 882 883 if bruteForce is None: 884 return self.getColumns(onlyColNames=onlyColNames, colTuple=colTuple, bruteForce=True) 885 886 return kb.data.cachedColumns 887 888 @stackedmethod 889 def getSchema(self): 890 infoMsg = "enumerating database management system schema" 891 logger.info(infoMsg) 892 893 try: 894 pushValue(conf.db) 895 pushValue(conf.tbl) 896 pushValue(conf.col) 897 898 kb.data.cachedTables = {} 899 kb.data.cachedColumns = {} 900 901 self.getTables() 902 903 infoMsg = "fetched tables: " 904 infoMsg += ", ".join(["%s" % ", ".join("%s%s%s" % (unsafeSQLIdentificatorNaming(db), ".." if Backend.isDbms(DBMS.MSSQL) or Backend.isDbms(DBMS.SYBASE) else '.', unsafeSQLIdentificatorNaming(_)) for _ in tbl) for db, tbl in kb.data.cachedTables.items()]) 905 logger.info(infoMsg) 906 907 for db, tables in kb.data.cachedTables.items(): 908 for tbl in tables: 909 conf.db = db 910 conf.tbl = tbl 911 912 self.getColumns() 913 finally: 914 conf.col = popValue() 915 conf.tbl = popValue() 916 conf.db = popValue() 917 918 return kb.data.cachedColumns 919 920 def _tableGetCount(self, db, table): 921 if not db or not table: 922 return None 923 924 if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): 925 db = db.upper() 926 table = table.upper() 927 928 if Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): 929 query = "SELECT %s FROM %s" % (queries[Backend.getIdentifiedDbms()].count.query % '*', safeSQLIdentificatorNaming(table, True)) 930 else: 931 query = "SELECT %s FROM %s.%s" % (queries[Backend.getIdentifiedDbms()].count.query % '*', safeSQLIdentificatorNaming(db), safeSQLIdentificatorNaming(table, True)) 932 933 query = agent.whereQuery(query) 934 count = inject.getValue(query, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) 935 936 if isNumPosStrValue(count): 937 if safeSQLIdentificatorNaming(db) not in kb.data.cachedCounts: 938 kb.data.cachedCounts[safeSQLIdentificatorNaming(db)] = {} 939 940 if int(count) in kb.data.cachedCounts[safeSQLIdentificatorNaming(db)]: 941 kb.data.cachedCounts[safeSQLIdentificatorNaming(db)][int(count)].append(safeSQLIdentificatorNaming(table, True)) 942 else: 943 kb.data.cachedCounts[safeSQLIdentificatorNaming(db)][int(count)] = [safeSQLIdentificatorNaming(table, True)] 944 945 def getCount(self): 946 if not conf.tbl: 947 warnMsg = "missing table parameter, sqlmap will retrieve " 948 warnMsg += "the number of entries for all database " 949 warnMsg += "management system databases' tables" 950 logger.warn(warnMsg) 951 952 elif "." in conf.tbl: 953 if not conf.db: 954 conf.db, conf.tbl = conf.tbl.split('.', 1) 955 956 if conf.tbl is not None and conf.db is None and Backend.getIdentifiedDbms() not in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): 957 warnMsg = "missing database parameter. sqlmap is going to " 958 warnMsg += "use the current database to retrieve the " 959 warnMsg += "number of entries for table '%s'" % unsafeSQLIdentificatorNaming(conf.tbl) 960 logger.warn(warnMsg) 961 962 conf.db = self.getCurrentDb() 963 964 self.forceDbmsEnum() 965 966 if conf.tbl: 967 for table in conf.tbl.split(','): 968 self._tableGetCount(conf.db, table) 969 else: 970 self.getTables() 971 972 for db, tables in kb.data.cachedTables.items(): 973 for table in tables: 974 self._tableGetCount(db, table) 975 976 return kb.data.cachedCounts 977 978 def getStatements(self): 979 infoMsg = "fetching SQL statements" 980 logger.info(infoMsg) 981 982 rootQuery = queries[Backend.getIdentifiedDbms()].statements 983 984 if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: 985 query = rootQuery.inband.query 986 987 while True: 988 values = inject.getValue(query, blind=False, time=False) 989 990 if not isNoneValue(values): 991 kb.data.cachedStatements = [] 992 for value in arrayizeValue(values): 993 value = (unArrayizeValue(value) or "").strip() 994 if not isNoneValue(value): 995 kb.data.cachedStatements.append(value.strip()) 996 997 elif Backend.isDbms(DBMS.PGSQL) and "current_query" not in query: 998 query = query.replace("query", "current_query") 999 continue 1000 1001 break 1002 1003 if not kb.data.cachedStatements and isInferenceAvailable() and not conf.direct: 1004 infoMsg = "fetching number of statements" 1005 logger.info(infoMsg) 1006 1007 query = rootQuery.blind.count 1008 count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) 1009 1010 if count == 0: 1011 return kb.data.cachedStatements 1012 elif not isNumPosStrValue(count): 1013 errMsg = "unable to retrieve the number of statements" 1014 raise SqlmapNoneDataException(errMsg) 1015 1016 plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) 1017 indexRange = getLimitRange(count, plusOne=plusOne) 1018 1019 for index in indexRange: 1020 value = None 1021 1022 if Backend.getIdentifiedDbms() in (DBMS.MYSQL,): # case with multiple processes 1023 query = rootQuery.blind.query3 % index 1024 identifier = unArrayizeValue(inject.getValue(query, union=False, error=False, expected=EXPECTED.INT)) 1025 1026 if not isNoneValue(identifier): 1027 query = rootQuery.blind.query2 % identifier 1028 value = unArrayizeValue(inject.getValue(query, union=False, error=False, expected=EXPECTED.INT)) 1029 1030 if isNoneValue(value): 1031 query = rootQuery.blind.query % index 1032 value = unArrayizeValue(inject.getValue(query, union=False, error=False)) 1033 1034 if not isNoneValue(value): 1035 kb.data.cachedStatements.append(value) 1036 1037 if not kb.data.cachedStatements: 1038 errMsg = "unable to retrieve the statements" 1039 logger.error(errMsg) 1040 else: 1041 kb.data.cachedStatements = [_.replace(REFLECTED_VALUE_MARKER, "<payload>") for _ in kb.data.cachedStatements] 1042 1043 return kb.data.cachedStatements 1044