entries批注

pull/18/head
Warmlight 3 months ago
parent 9b093e7aa4
commit bb93bced04

@ -5,10 +5,14 @@ Copyright (c) 2006-2024 sqlmap developers (https://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
# 导入正则表达式模块
import re import re
# 从lib.core.agent模块导入agent对象用于处理注入过程中的各种细节
from lib.core.agent import agent from lib.core.agent import agent
# 从lib.core.bigarray模块导入BigArray类用于高效存储和处理大量数据
from lib.core.bigarray import BigArray from lib.core.bigarray import BigArray
# 从lib.core.common模块导入各种常用函数和常量如Backend、clearConsoleLine、getLimitRange等
from lib.core.common import Backend from lib.core.common import Backend
from lib.core.common import clearConsoleLine from lib.core.common import clearConsoleLine
from lib.core.common import getLimitRange from lib.core.common import getLimitRange
@ -25,33 +29,47 @@ from lib.core.common import singleTimeLogMessage
from lib.core.common import singleTimeWarnMessage from lib.core.common import singleTimeWarnMessage
from lib.core.common import unArrayizeValue from lib.core.common import unArrayizeValue
from lib.core.common import unsafeSQLIdentificatorNaming from lib.core.common import unsafeSQLIdentificatorNaming
# 从lib.core.convert模块导入getConsoleLength函数用于获取控制台字符串的长度
from lib.core.convert import getConsoleLength from lib.core.convert import getConsoleLength
from lib.core.convert import getUnicode from lib.core.convert import getUnicode
# 从lib.core.data模块导入conf和kb对象用于存储配置信息和知识库信息
from lib.core.data import conf from lib.core.data import conf
from lib.core.data import kb from lib.core.data import kb
# 从lib.core.data模块导入logger对象用于记录日志信息
from lib.core.data import logger from lib.core.data import logger
# 从lib.core.data模块导入queries对象用于存储各种数据库的查询语句
from lib.core.data import queries from lib.core.data import queries
# 从lib.core.dicts模块导入DUMP_REPLACEMENTS字典用于替换转储数据中的特殊字符
from lib.core.dicts import DUMP_REPLACEMENTS from lib.core.dicts import DUMP_REPLACEMENTS
# 从lib.core.enums模块导入各种枚举类型如CHARSET_TYPE、DBMS、EXPECTED、PAYLOAD等
from lib.core.enums import CHARSET_TYPE from lib.core.enums import CHARSET_TYPE
from lib.core.enums import DBMS from lib.core.enums import DBMS
from lib.core.enums import EXPECTED from lib.core.enums import EXPECTED
from lib.core.enums import PAYLOAD from lib.core.enums import PAYLOAD
# 从lib.core.exception模块导入各种自定义异常类
from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapConnectionException
from lib.core.exception import SqlmapMissingMandatoryOptionException from lib.core.exception import SqlmapMissingMandatoryOptionException
from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapNoneDataException
from lib.core.exception import SqlmapUnsupportedFeatureException from lib.core.exception import SqlmapUnsupportedFeatureException
# 从lib.core.settings模块导入各种配置常量
from lib.core.settings import CHECK_ZERO_COLUMNS_THRESHOLD from lib.core.settings import CHECK_ZERO_COLUMNS_THRESHOLD
from lib.core.settings import CURRENT_DB from lib.core.settings import CURRENT_DB
from lib.core.settings import METADB_SUFFIX from lib.core.settings import METADB_SUFFIX
from lib.core.settings import NULL from lib.core.settings import NULL
from lib.core.settings import PLUS_ONE_DBMSES from lib.core.settings import PLUS_ONE_DBMSES
from lib.core.settings import UPPER_CASE_DBMSES from lib.core.settings import UPPER_CASE_DBMSES
# 从lib.request模块导入inject函数用于执行SQL注入
from lib.request import inject from lib.request import inject
# 从lib.utils.hash模块导入attackDumpedTable函数用于对转储的表数据进行攻击
from lib.utils.hash import attackDumpedTable from lib.utils.hash import attackDumpedTable
# 从lib.utils.pivotdumptable模块导入pivotDumpTable函数用于执行透视转储表操作
from lib.utils.pivotdumptable import pivotDumpTable from lib.utils.pivotdumptable import pivotDumpTable
# 导入第三方six库用于兼容Python 2和Python 3
from thirdparty import six from thirdparty import six
# 导入第三方six库的zip函数并重命名为_zip
from thirdparty.six.moves import zip as _zip from thirdparty.six.moves import zip as _zip
# 定义Entries类用于封装枚举数据库条目的相关功能
class Entries(object): class Entries(object):
""" """
This class defines entries' enumeration functionalities for plugins. This class defines entries' enumeration functionalities for plugins.
@ -60,9 +78,12 @@ class Entries(object):
def __init__(self): def __init__(self):
pass pass
# 定义dumpTable方法用于转储指定表的条目
def dumpTable(self, foundData=None): def dumpTable(self, foundData=None):
# 强制执行数据库枚举,确保已获取数据库类型
self.forceDbmsEnum() self.forceDbmsEnum()
# 如果没有指定数据库或指定为当前数据库,则获取当前数据库
if conf.db is None or conf.db == CURRENT_DB: if conf.db is None or conf.db == CURRENT_DB:
if conf.db is None: if conf.db is None:
warnMsg = "missing database parameter. sqlmap is going " warnMsg = "missing database parameter. sqlmap is going "
@ -71,65 +92,81 @@ class Entries(object):
logger.warning(warnMsg) logger.warning(warnMsg)
conf.db = self.getCurrentDb() conf.db = self.getCurrentDb()
# 如果指定了数据库
elif conf.db is not None: elif conf.db is not None:
# 如果数据库是属于大写数据库类型,则将其转换为大写
if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES:
conf.db = conf.db.upper() conf.db = conf.db.upper()
# 如果数据库名包含逗号,则抛出异常,因为只允许一个数据库名
if ',' in conf.db: if ',' in conf.db:
errMsg = "only one database name is allowed when enumerating " errMsg = "only one database name is allowed when enumerating "
errMsg += "the tables' columns" errMsg += "the tables' columns"
raise SqlmapMissingMandatoryOptionException(errMsg) raise SqlmapMissingMandatoryOptionException(errMsg)
# 如果数据库名匹配排除模式,则跳过
if conf.exclude and re.search(conf.exclude, conf.db, re.I) is not None: if conf.exclude and re.search(conf.exclude, conf.db, re.I) is not None:
infoMsg = "skipping database '%s'" % unsafeSQLIdentificatorNaming(conf.db) infoMsg = "skipping database '%s'" % unsafeSQLIdentificatorNaming(conf.db)
singleTimeLogMessage(infoMsg) singleTimeLogMessage(infoMsg)
return return
# 对数据库名进行安全处理
conf.db = safeSQLIdentificatorNaming(conf.db) or "" conf.db = safeSQLIdentificatorNaming(conf.db) or ""
# 如果指定了表
if conf.tbl: if conf.tbl:
# 如果表名是属于大写数据库类型,则将其转换为大写
if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES:
conf.tbl = conf.tbl.upper() conf.tbl = conf.tbl.upper()
# 将表名拆分为列表
tblList = conf.tbl.split(',') tblList = conf.tbl.split(',')
# 如果没有指定表
else: else:
# 获取所有表
self.getTables() self.getTables()
# 如果已缓存表信息
if len(kb.data.cachedTables) > 0: if len(kb.data.cachedTables) > 0:
# 获取表列表
tblList = list(six.itervalues(kb.data.cachedTables)) tblList = list(six.itervalues(kb.data.cachedTables))
# 如果表列表嵌套,则解包
if tblList and isListLike(tblList[0]): if tblList and isListLike(tblList[0]):
tblList = tblList[0] tblList = tblList[0]
# 如果指定了数据库但未能获取表信息
elif conf.db and not conf.search: elif conf.db and not conf.search:
errMsg = "unable to retrieve the tables " errMsg = "unable to retrieve the tables "
errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db)
raise SqlmapNoneDataException(errMsg) raise SqlmapNoneDataException(errMsg)
else: else:
return return
# 对表名列表中的表名进行安全处理
for tbl in tblList: for tbl in tblList:
tblList[tblList.index(tbl)] = safeSQLIdentificatorNaming(tbl, True) tblList[tblList.index(tbl)] = safeSQLIdentificatorNaming(tbl, True)
# 遍历表列表
for tbl in tblList: for tbl in tblList:
# 如果检测到键盘中断,则跳出循环
if kb.dumpKeyboardInterrupt: if kb.dumpKeyboardInterrupt:
break break
# 如果表名匹配排除模式,则跳过
if conf.exclude and re.search(conf.exclude, tbl, re.I) is not None: if conf.exclude and re.search(conf.exclude, tbl, re.I) is not None:
infoMsg = "skipping table '%s'" % unsafeSQLIdentificatorNaming(tbl) infoMsg = "skipping table '%s'" % unsafeSQLIdentificatorNaming(tbl)
singleTimeLogMessage(infoMsg) singleTimeLogMessage(infoMsg)
continue continue
# 设置当前表名
conf.tbl = tbl conf.tbl = tbl
# 初始化已转储的表数据
kb.data.dumpedTable = {} kb.data.dumpedTable = {}
# 如果没有传入已发现的列数据
if foundData is None: if foundData is None:
# 清空缓存的列信息
kb.data.cachedColumns = {} kb.data.cachedColumns = {}
# 获取列信息,仅获取列名,并设置转储模式
self.getColumns(onlyColNames=True, dumpMode=True) self.getColumns(onlyColNames=True, dumpMode=True)
# 如果传入了已发现的列数据,则直接使用
else: else:
kb.data.cachedColumns = foundData kb.data.cachedColumns = foundData
try: try:
# 根据数据库类型设置转储表名
if Backend.isDbms(DBMS.INFORMIX): if Backend.isDbms(DBMS.INFORMIX):
kb.dumpTable = "%s:%s" % (conf.db, tbl) kb.dumpTable = "%s:%s" % (conf.db, tbl)
elif Backend.isDbms(DBMS.SQLITE): elif Backend.isDbms(DBMS.SQLITE):
@ -139,6 +176,7 @@ class Entries(object):
else: else:
kb.dumpTable = "%s.%s" % (conf.db, tbl) 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)]: 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 '%s'" % unsafeSQLIdentificatorNaming(tbl) warnMsg = "unable to enumerate the columns for table '%s'" % unsafeSQLIdentificatorNaming(tbl)
if METADB_SUFFIX.upper() not in conf.db.upper(): if METADB_SUFFIX.upper() not in conf.db.upper():
@ -148,12 +186,16 @@ class Entries(object):
continue continue
# 获取当前表的列信息
columns = kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)] columns = kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)]
# 对列名列表进行排序
colList = sorted(column for column in columns if column) colList = sorted(column for column in columns if column)
# 如果指定了排除模式,则从列名列表中排除匹配的列
if conf.exclude: if conf.exclude:
colList = [_ for _ in colList if re.search(conf.exclude, _, re.I) is None] colList = [_ for _ in colList if re.search(conf.exclude, _, re.I) is None]
# 如果没有可用的列名,则跳过当前表
if not colList: if not colList:
warnMsg = "skipping table '%s'" % unsafeSQLIdentificatorNaming(tbl) warnMsg = "skipping table '%s'" % unsafeSQLIdentificatorNaming(tbl)
if METADB_SUFFIX.upper() not in conf.db.upper(): if METADB_SUFFIX.upper() not in conf.db.upper():
@ -161,9 +203,11 @@ class Entries(object):
warnMsg += " (no usable column names)" warnMsg += " (no usable column names)"
logger.warning(warnMsg) logger.warning(warnMsg)
continue continue
# 设置全局变量 kb.dumpColumns 为当前表需要转储的列名列表
kb.dumpColumns = [unsafeSQLIdentificatorNaming(_) for _ in colList] kb.dumpColumns = [unsafeSQLIdentificatorNaming(_) for _ in colList]
# 将列名列表转换为逗号分隔的字符串
colNames = colString = ','.join(column for column in colList) colNames = colString = ','.join(column for column in colList)
# 获取转储表的根查询
rootQuery = queries[Backend.getIdentifiedDbms()].dump_table rootQuery = queries[Backend.getIdentifiedDbms()].dump_table
infoMsg = "fetching entries" infoMsg = "fetching entries"
@ -174,17 +218,21 @@ class Entries(object):
infoMsg += " in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) infoMsg += " in database '%s'" % unsafeSQLIdentificatorNaming(conf.db)
logger.info(infoMsg) logger.info(infoMsg)
# 遍历列名列表,对每个列名进行预处理
for column in colList: for column in colList:
_ = agent.preprocessField(tbl, column) _ = agent.preprocessField(tbl, column)
if _ != column: if _ != column:
colString = re.sub(r"\b%s\b" % re.escape(column), _.replace("\\", r"\\"), colString) colString = re.sub(r"\b%s\b" % re.escape(column), _.replace("\\", r"\\"), colString)
# 初始化条目计数
entriesCount = 0 entriesCount = 0
# 如果存在可用的注入技术,或使用了--direct参数
if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct:
# 初始化条目列表
entries = [] entries = []
query = None query = None
# 根据数据库类型构建查询语句
if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE, DBMS.MIMERSQL): 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()))) 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, DBMS.RAIMA): elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MAXDB, DBMS.MCKOI, DBMS.EXTREMEDB, DBMS.RAIMA):
@ -247,9 +295,10 @@ class Entries(object):
query = rootQuery.inband.query % (colString, conf.db, tbl) query = rootQuery.inband.query % (colString, conf.db, tbl)
query = agent.whereQuery(query) query = agent.whereQuery(query)
# 如果没有获取到条目,并且查询语句存在,并且没有检测到键盘中断
if not entries and query and not kb.dumpKeyboardInterrupt: if not entries and query and not kb.dumpKeyboardInterrupt:
try: try:
# 执行查询语句,获取条目信息
entries = inject.getValue(query, blind=False, time=False, dump=True) entries = inject.getValue(query, blind=False, time=False, dump=True)
except KeyboardInterrupt: except KeyboardInterrupt:
entries = None entries = None
@ -257,7 +306,7 @@ class Entries(object):
clearConsoleLine() clearConsoleLine()
warnMsg = "Ctrl+C detected in dumping phase" warnMsg = "Ctrl+C detected in dumping phase"
logger.warning(warnMsg) logger.warning(warnMsg)
# 如果成功获取到条目
if not isNoneValue(entries): if not isNoneValue(entries):
if isinstance(entries, six.string_types): if isinstance(entries, six.string_types):
entries = [entries] entries = [entries]
@ -266,6 +315,7 @@ class Entries(object):
entriesCount = len(entries) entriesCount = len(entries)
# 遍历每个列名和条目,更新转储的表数据信息
for index, column in enumerate(colList): for index, column in enumerate(colList):
if column not in kb.data.dumpedTable: if column not in kb.data.dumpedTable:
kb.data.dumpedTable[column] = {"length": len(column), "values": BigArray()} kb.data.dumpedTable[column] = {"length": len(column), "values": BigArray()}
@ -273,19 +323,20 @@ class Entries(object):
for entry in entries: for entry in entries:
if entry is None or len(entry) == 0: if entry is None or len(entry) == 0:
continue continue
# 如果条目是字符串类型
if isinstance(entry, six.string_types): if isinstance(entry, six.string_types):
colEntry = entry colEntry = entry
# 否则,获取指定索引的条目
else: else:
colEntry = unArrayizeValue(entry[index]) if index < len(entry) else u'' colEntry = unArrayizeValue(entry[index]) if index < len(entry) else u''
maxLen = max(getConsoleLength(column), getConsoleLength(DUMP_REPLACEMENTS.get(getUnicode(colEntry), getUnicode(colEntry)))) maxLen = max(getConsoleLength(column), getConsoleLength(DUMP_REPLACEMENTS.get(getUnicode(colEntry), getUnicode(colEntry))))
# 更新最大长度
if maxLen > kb.data.dumpedTable[column]["length"]: if maxLen > kb.data.dumpedTable[column]["length"]:
kb.data.dumpedTable[column]["length"] = maxLen kb.data.dumpedTable[column]["length"] = maxLen
# 添加条目值
kb.data.dumpedTable[column]["values"].append(colEntry) kb.data.dumpedTable[column]["values"].append(colEntry)
# 如果没有转储表数据,并且可以使用盲注方式,且不是直接模式
if not kb.data.dumpedTable and isInferenceAvailable() and not conf.direct: if not kb.data.dumpedTable and isInferenceAvailable() and not conf.direct:
infoMsg = "fetching number of " infoMsg = "fetching number of "
if conf.col: if conf.col:
@ -294,6 +345,7 @@ class Entries(object):
infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db)
logger.info(infoMsg) logger.info(infoMsg)
# 构建盲注获取条目计数的查询语句
if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE, DBMS.MIMERSQL): 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()))) 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.MAXDB, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MCKOI, DBMS.EXTREMEDB, DBMS.RAIMA): elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.MAXDB, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MCKOI, DBMS.EXTREMEDB, DBMS.RAIMA):
@ -307,21 +359,22 @@ class Entries(object):
query = agent.whereQuery(query) query = agent.whereQuery(query)
# 执行盲注查询,获取条目计数
count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)
lengths = {} lengths = {}
entries = {} entries = {}
# 如果条目计数为0
if count == 0: if count == 0:
warnMsg = "table '%s' " % unsafeSQLIdentificatorNaming(tbl) warnMsg = "table '%s' " % unsafeSQLIdentificatorNaming(tbl)
warnMsg += "in database '%s' " % unsafeSQLIdentificatorNaming(conf.db) warnMsg += "in database '%s' " % unsafeSQLIdentificatorNaming(conf.db)
warnMsg += "appears to be empty" warnMsg += "appears to be empty"
logger.warning(warnMsg) logger.warning(warnMsg)
# 初始化长度和条目
for column in colList: for column in colList:
lengths[column] = len(column) lengths[column] = len(column)
entries[column] = [] entries[column] = []
# 如果未能获取条目计数
elif not isNumPosStrValue(count): elif not isNumPosStrValue(count):
warnMsg = "unable to retrieve the number of " warnMsg = "unable to retrieve the number of "
if conf.col: if conf.col:
@ -331,7 +384,7 @@ class Entries(object):
logger.warning(warnMsg) logger.warning(warnMsg)
continue continue
# 对于特定数据库
elif Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.SYBASE, DBMS.MAXDB, DBMS.MSSQL, DBMS.INFORMIX, DBMS.MCKOI, DBMS.RAIMA): elif Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.SYBASE, DBMS.MAXDB, DBMS.MSSQL, DBMS.INFORMIX, DBMS.MCKOI, DBMS.RAIMA):
if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI, DBMS.RAIMA): if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI, DBMS.RAIMA):
table = tbl table = tbl
@ -339,7 +392,7 @@ class Entries(object):
table = "%s.%s" % (conf.db, tbl) if conf.db else tbl table = "%s.%s" % (conf.db, tbl) if conf.db else tbl
elif Backend.isDbms(DBMS.INFORMIX): elif Backend.isDbms(DBMS.INFORMIX):
table = "%s:%s" % (conf.db, tbl) if conf.db else tbl table = "%s:%s" % (conf.db, tbl) if conf.db else tbl
# 如果是mssql并且没有强制透视
if Backend.isDbms(DBMS.MSSQL) and not conf.forcePivoting: if Backend.isDbms(DBMS.MSSQL) and not conf.forcePivoting:
warnMsg = "in case of table dumping problems (e.g. column entry order) " warnMsg = "in case of table dumping problems (e.g. column entry order) "
warnMsg += "you are advised to rerun with '--force-pivoting'" warnMsg += "you are advised to rerun with '--force-pivoting'"
@ -369,7 +422,7 @@ class Entries(object):
clearConsoleLine() clearConsoleLine()
warnMsg = "Ctrl+C detected in dumping phase" warnMsg = "Ctrl+C detected in dumping phase"
logger.warning(warnMsg) logger.warning(warnMsg)
# 如果没有获取到条目,且没有检测到键盘中断
if not entries and not kb.dumpKeyboardInterrupt: if not entries and not kb.dumpKeyboardInterrupt:
try: try:
retVal = pivotDumpTable(table, colList, count, blind=True) retVal = pivotDumpTable(table, colList, count, blind=True)
@ -382,12 +435,12 @@ class Entries(object):
if retVal: if retVal:
entries, lengths = retVal entries, lengths = retVal
# 对于其他数据库
else: else:
emptyColumns = [] emptyColumns = []
plusOne = Backend.getIdentifiedDbms() in PLUS_ONE_DBMSES plusOne = Backend.getIdentifiedDbms() in PLUS_ONE_DBMSES
indexRange = getLimitRange(count, plusOne=plusOne) indexRange = getLimitRange(count, plusOne=plusOne)
# 如果列的数量小于行数,且大于阈值,则进行空列检查
if len(colList) < len(indexRange) > CHECK_ZERO_COLUMNS_THRESHOLD: if len(colList) < len(indexRange) > CHECK_ZERO_COLUMNS_THRESHOLD:
debugMsg = "checking for empty columns" debugMsg = "checking for empty columns"
logger.debug(infoMsg) logger.debug(infoMsg)
@ -409,7 +462,7 @@ class Entries(object):
if column not in entries: if column not in entries:
entries[column] = BigArray() entries[column] = BigArray()
# 根据不同的数据库类型,构建盲注查询语句
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2, DBMS.VERTICA, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CACHE, DBMS.CLICKHOUSE): if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2, DBMS.VERTICA, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CACHE, DBMS.CLICKHOUSE):
query = rootQuery.blind.query % (agent.preprocessField(tbl, column), conf.db, conf.tbl, sorted(colList, key=len)[0], index) 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,): elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE,):
@ -428,10 +481,10 @@ class Entries(object):
query = rootQuery.blind.query % (agent.preprocessField(tbl, column), conf.db, tbl, index) query = rootQuery.blind.query % (agent.preprocessField(tbl, column), conf.db, tbl, index)
query = agent.whereQuery(query) query = agent.whereQuery(query)
# 执行盲注查询,获取值
value = NULL if column in emptyColumns else inject.getValue(query, union=False, error=False, dump=True) value = NULL if column in emptyColumns else inject.getValue(query, union=False, error=False, dump=True)
value = '' if value is None else value value = '' if value is None else value
# 更新最大长度和条目值
lengths[column] = max(lengths[column], len(DUMP_REPLACEMENTS.get(getUnicode(value), getUnicode(value)))) lengths[column] = max(lengths[column], len(DUMP_REPLACEMENTS.get(getUnicode(value), getUnicode(value))))
entries[column].append(value) entries[column].append(value)
@ -440,14 +493,15 @@ class Entries(object):
clearConsoleLine() clearConsoleLine()
warnMsg = "Ctrl+C detected in dumping phase" warnMsg = "Ctrl+C detected in dumping phase"
logger.warning(warnMsg) logger.warning(warnMsg)
# 遍历获取到的条目将结果保存到kb.data.dumpedTable中
for column, columnEntries in entries.items(): for column, columnEntries in entries.items():
length = max(lengths[column], len(column)) length = max(lengths[column], len(column))
kb.data.dumpedTable[column] = {"length": length, "values": columnEntries} kb.data.dumpedTable[column] = {"length": length, "values": columnEntries}
# 获取总行数
entriesCount = len(columnEntries) entriesCount = len(columnEntries)
# 如果没有转储表数据或者条目数为0且有权限标识
if len(kb.data.dumpedTable) == 0 or (entriesCount == 0 and kb.permissionFlag): if len(kb.data.dumpedTable) == 0 or (entriesCount == 0 and kb.permissionFlag):
warnMsg = "unable to retrieve the entries " warnMsg = "unable to retrieve the entries "
if conf.col: if conf.col:
@ -456,15 +510,18 @@ class Entries(object):
warnMsg += "in database '%s'%s" % (unsafeSQLIdentificatorNaming(conf.db), " (permission denied)" if kb.permissionFlag else "") warnMsg += "in database '%s'%s" % (unsafeSQLIdentificatorNaming(conf.db), " (permission denied)" if kb.permissionFlag else "")
logger.warning(warnMsg) logger.warning(warnMsg)
else: else:
# 保存转储的信息,包括总行数、表名和数据库名
kb.data.dumpedTable["__infos__"] = {"count": entriesCount, kb.data.dumpedTable["__infos__"] = {"count": entriesCount,
"table": safeSQLIdentificatorNaming(tbl, True), "table": safeSQLIdentificatorNaming(tbl, True),
"db": safeSQLIdentificatorNaming(conf.db)} "db": safeSQLIdentificatorNaming(conf.db)}
try: try:
# 对转储的数据进行攻击
attackDumpedTable() attackDumpedTable()
except (IOError, OSError) as ex: except (IOError, OSError) as ex:
errMsg = "an error occurred while attacking " errMsg = "an error occurred while attacking "
errMsg += "table dump ('%s')" % getSafeExString(ex) errMsg += "table dump ('%s')" % getSafeExString(ex)
logger.critical(errMsg) logger.critical(errMsg)
# 将转储的数据传递给dumper
conf.dumper.dbTableValues(kb.data.dumpedTable) conf.dumper.dbTableValues(kb.data.dumpedTable)
except SqlmapConnectionException as ex: except SqlmapConnectionException as ex:
@ -473,14 +530,17 @@ class Entries(object):
logger.critical(errMsg) logger.critical(errMsg)
finally: finally:
# 清空全局变量
kb.dumpColumns = None kb.dumpColumns = None
kb.dumpTable = None kb.dumpTable = None
# 定义dumpAll方法用于转储所有数据库中的所有表的所有条目
def dumpAll(self): def dumpAll(self):
# 如果指定了数据库,但没有指定表,则只转储该数据库下的表
if conf.db is not None and conf.tbl is None: if conf.db is not None and conf.tbl is None:
self.dumpTable() self.dumpTable()
return return
# 如果是MySQL数据库且没有information_schema
if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema: if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema:
errMsg = "information_schema not available, " errMsg = "information_schema not available, "
errMsg += "back-end DBMS is MySQL < 5.0" errMsg += "back-end DBMS is MySQL < 5.0"
@ -489,15 +549,19 @@ class Entries(object):
infoMsg = "sqlmap will dump entries of all tables from all databases now" infoMsg = "sqlmap will dump entries of all tables from all databases now"
logger.info(infoMsg) logger.info(infoMsg)
# 清空表和列的全局变量
conf.tbl = None conf.tbl = None
conf.col = None conf.col = None
# 获取所有表
self.getTables() self.getTables()
# 如果有缓存的表信息
if kb.data.cachedTables: if kb.data.cachedTables:
if isinstance(kb.data.cachedTables, list): if isinstance(kb.data.cachedTables, list):
kb.data.cachedTables = {None: kb.data.cachedTables} kb.data.cachedTables = {None: kb.data.cachedTables}
# 遍历数据库和表
for db, tables in kb.data.cachedTables.items(): for db, tables in kb.data.cachedTables.items():
conf.db = db conf.db = db
@ -506,7 +570,6 @@ class Entries(object):
infoMsg = "skipping table '%s'" % unsafeSQLIdentificatorNaming(table) infoMsg = "skipping table '%s'" % unsafeSQLIdentificatorNaming(table)
logger.info(infoMsg) logger.info(infoMsg)
continue continue
try: try:
conf.tbl = table conf.tbl = table
kb.data.cachedColumns = {} kb.data.cachedColumns = {}
@ -517,45 +580,57 @@ class Entries(object):
infoMsg = "skipping table '%s'" % unsafeSQLIdentificatorNaming(table) infoMsg = "skipping table '%s'" % unsafeSQLIdentificatorNaming(table)
logger.info(infoMsg) logger.info(infoMsg)
# 定义dumpFoundColumn方法用于转储已发现的列
def dumpFoundColumn(self, dbs, foundCols, colConsider): def dumpFoundColumn(self, dbs, foundCols, colConsider):
message = "do you want to dump found column(s) entries? [Y/n] " message = "do you want to dump found column(s) entries? [Y/n] "
# 询问用户是否要转储已发现的列
if not readInput(message, default='Y', boolean=True): if not readInput(message, default='Y', boolean=True):
return return
dumpFromDbs = [] dumpFromDbs = []
message = "which database(s)?\n[a]ll (default)\n" message = "which database(s)?\
[a]ll (default)\
"
# 构建数据库选项
for db, tblData in dbs.items(): for db, tblData in dbs.items():
if tblData: if tblData:
message += "[%s]\n" % unsafeSQLIdentificatorNaming(db) message += "[%s]\
" % unsafeSQLIdentificatorNaming(db)
message += "[q]uit" message += "[q]uit"
# 接收用户选择
choice = readInput(message, default='a') choice = readInput(message, default='a')
# 处理用户选择
if not choice or choice in ('a', 'A'): if not choice or choice in ('a', 'A'):
dumpFromDbs = list(dbs.keys()) dumpFromDbs = list(dbs.keys())
elif choice in ('q', 'Q'): elif choice in ('q', 'Q'):
return return
else: else:
dumpFromDbs = choice.replace(" ", "").split(',') dumpFromDbs = choice.replace(" ", "").split(',')
# 遍历数据库
for db, tblData in dbs.items(): for db, tblData in dbs.items():
if db not in dumpFromDbs or not tblData: if db not in dumpFromDbs or not tblData:
continue continue
conf.db = db conf.db = db
dumpFromTbls = [] dumpFromTbls = []
message = "which table(s) of database '%s'?\n" % unsafeSQLIdentificatorNaming(db) message = "which table(s) of database '%s'?\
message += "[a]ll (default)\n" " % unsafeSQLIdentificatorNaming(db)
message += "[a]ll (default)\
"
# 构建表选项
for tbl in tblData: for tbl in tblData:
message += "[%s]\n" % tbl message += "[%s]\
" % tbl
message += "[s]kip\n" message += "[s]kip\
"
message += "[q]uit" message += "[q]uit"
# 接收用户选择
choice = readInput(message, default='a') choice = readInput(message, default='a')
# 处理用户选择
if not choice or choice in ('a', 'A'): if not choice or choice in ('a', 'A'):
dumpFromTbls = tblData dumpFromTbls = tblData
elif choice in ('s', 'S'): elif choice in ('s', 'S'):
@ -564,80 +639,120 @@ class Entries(object):
return return
else: else:
dumpFromTbls = choice.replace(" ", "").split(',') dumpFromTbls = choice.replace(" ", "").split(',')
# 遍历表
for table, columns in tblData.items(): for table, columns in tblData.items():
if table not in dumpFromTbls: if table not in dumpFromTbls:
continue continue
conf.tbl = table conf.tbl = table
colList = [_ for _ in columns if _] colList = [_ for _ in columns if _]
# 如果指定了排除模式,则排除匹配的列
if conf.exclude: if conf.exclude:
colList = [_ for _ in colList if re.search(conf.exclude, _, re.I) is None] colList = [_ for _ in colList if re.search(conf.exclude, _, re.I) is None]
# 设置需要转储的列
conf.col = ','.join(colList) conf.col = ','.join(colList)
kb.data.cachedColumns = {} kb.data.cachedColumns = {}
kb.data.dumpedTable = {} kb.data.dumpedTable = {}
data = self.dumpTable(dbs) data = self.dumpTable(dbs)
# 如果成功转储了数据则传递给dumper
if data: if data:
conf.dumper.dbTableValues(data) conf.dumper.dbTableValues(data)
def dumpFoundTables(self, tables): def dumpFoundTables(self, tables):
# 1. 定义一个消息,询问用户是否要转储发现的表条目
message = "do you want to dump found table(s) entries? [Y/n] " message = "do you want to dump found table(s) entries? [Y/n] "
# 2. 使用 readInput 函数获取用户输入,默认值为 'Y' (是),并将其转换为布尔值
# 如果用户输入 'n' (否) 或者其他非 'y' 的值,则返回 False否则返回 True。
# 如果用户输入为否,则直接返回,不进行后续的转储操作
if not readInput(message, default='Y', boolean=True): if not readInput(message, default='Y', boolean=True):
return return
# 3. 初始化一个空列表 dumpFromDbs用于存储用户选择要转储的数据库
dumpFromDbs = [] dumpFromDbs = []
message = "which database(s)?\n[a]ll (default)\n" # 4. 构建一个消息,用于提示用户选择要转储的数据库,其中 [a]ll (default) 表示默认选择全部数据库
message = "which database(s)?\
[a]ll (default)\
"
# 5. 遍历传入的 tables 字典,该字典的键是数据库名,值是该数据库下的表列表
for db, tablesList in tables.items(): for db, tablesList in tables.items():
# 6. 如果该数据库有表,则将数据库名添加到消息中,并进行安全 SQL 标识符命名处理
if tablesList: if tablesList:
message += "[%s]\n" % unsafeSQLIdentificatorNaming(db) message += "[%s]\
" % unsafeSQLIdentificatorNaming(db)
# 7. 在消息中添加一个选项 [q]uit允许用户退出
message += "[q]uit" message += "[q]uit"
# 8. 使用 readInput 函数获取用户输入,默认值为 'a',表示选择全部数据库
choice = readInput(message, default='a') choice = readInput(message, default='a')
# 9. 如果用户没有输入或者输入为 'a' (或 'A'),则将所有数据库添加到 dumpFromDbs 列表中
if not choice or choice.lower() == 'a': if not choice or choice.lower() == 'a':
dumpFromDbs = list(tables.keys()) dumpFromDbs = list(tables.keys())
# 10. 如果用户输入为 'q' (或 'Q'),则直接返回,不进行后续的转储操作
elif choice.lower() == 'q': elif choice.lower() == 'q':
return return
# 11. 否则,将用户输入的数据库名按逗号分割,并添加到 dumpFromDbs 列表中
else: else:
dumpFromDbs = choice.replace(" ", "").split(',') dumpFromDbs = choice.replace(" ", "").split(',')
# 12. 遍历 tables 字典,键是数据库名,值是该数据库下的表列表
for db, tablesList in tables.items(): for db, tablesList in tables.items():
# 13. 如果当前数据库不在 dumpFromDbs 列表中,或者当前数据库没有表,则跳过该数据库
if db not in dumpFromDbs or not tablesList: if db not in dumpFromDbs or not tablesList:
continue continue
# 14. 将当前数据库名赋值给 conf.db (全局配置参数)
conf.db = db conf.db = db
# 15. 初始化一个空列表 dumpFromTbls用于存储用户选择要转储的表
dumpFromTbls = [] dumpFromTbls = []
message = "which table(s) of database '%s'?\n" % unsafeSQLIdentificatorNaming(db) # 16. 构建一个消息,用于提示用户选择当前数据库下要转储的表
message += "[a]ll (default)\n" message = "which table(s) of database '%s'?\
" % unsafeSQLIdentificatorNaming(db)
# 17. 在消息中添加一个选项 [a]ll (default),表示默认选择全部表
message += "[a]ll (default)\
"
# 18. 遍历当前数据库下的表列表,将表名添加到消息中,并进行安全 SQL 标识符命名处理
for tbl in tablesList: for tbl in tablesList:
message += "[%s]\n" % unsafeSQLIdentificatorNaming(tbl) message += "[%s]\
" % unsafeSQLIdentificatorNaming(tbl)
message += "[s]kip\n" # 19. 在消息中添加一个选项 [s]kip允许用户跳过当前数据库的表
message += "[s]kip\
"
# 20. 在消息中添加一个选项 [q]uit允许用户退出
message += "[q]uit" message += "[q]uit"
# 21. 使用 readInput 函数获取用户输入,默认值为 'a',表示选择全部表
choice = readInput(message, default='a') choice = readInput(message, default='a')
# 22. 如果用户没有输入或者输入为 'a' (或 'A'),则将所有表添加到 dumpFromTbls 列表中
if not choice or choice.lower() == 'a': if not choice or choice.lower() == 'a':
dumpFromTbls = tablesList dumpFromTbls = tablesList
# 23. 如果用户输入为 's' (或 'S'),则跳过当前数据库的表,继续处理下一个数据库
elif choice.lower() == 's': elif choice.lower() == 's':
continue continue
# 24. 如果用户输入为 'q' (或 'Q'),则直接返回,不进行后续的转储操作
elif choice.lower() == 'q': elif choice.lower() == 'q':
return return
# 25. 否则,将用户输入的表名按逗号分割,并添加到 dumpFromTbls 列表中
else: else:
dumpFromTbls = choice.replace(" ", "").split(',') dumpFromTbls = choice.replace(" ", "").split(',')
# 26. 遍历 dumpFromTbls 列表,该列表存储当前数据库下要转储的表名
for table in dumpFromTbls: for table in dumpFromTbls:
# 27. 将当前表名赋值给 conf.tbl (全局配置参数)
conf.tbl = table conf.tbl = table
# 28. 清空 kb.data.cachedColumns (全局配置参数) 缓存的列信息
kb.data.cachedColumns = {} kb.data.cachedColumns = {}
# 29. 清空 kb.data.dumpedTable (全局配置参数) 缓存的表数据
kb.data.dumpedTable = {} kb.data.dumpedTable = {}
# 30. 调用 self.dumpTable() 函数,转储当前表的数据
data = self.dumpTable() data = self.dumpTable()
# 31. 如果转储成功 (data 不为空),则将转储的数据传递给 conf.dumper.dbTableValues() 函数进行后续处理
if data: if data:
conf.dumper.dbTableValues(data) conf.dumper.dbTableValues(data)
Loading…
Cancel
Save