filesystem批注

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

@ -5,320 +5,496 @@ Copyright (c) 2006-2024 sqlmap developers (https://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
import codecs import codecs # 用于处理不同编码的文本
import os import os # 用于与操作系统进行交互
import sys import sys # 用于访问与系统相关的参数和函数
from lib.core.agent import agent from lib.core.agent import agent # 导入 agent 模块,用于与数据库进行交互
from lib.core.common import Backend from lib.core.common import Backend # 导入 Backend 模块,用于获取数据库信息
from lib.core.common import checkFile from lib.core.common import checkFile # 导入 checkFile 函数,用于检查文件是否存在
from lib.core.common import dataToOutFile from lib.core.common import dataToOutFile # 导入 dataToOutFile 函数,用于将数据写入文件
from lib.core.common import decloakToTemp from lib.core.common import decloakToTemp # 导入 decloakToTemp 函数,用于将伪装的文件名转换为临时文件路径
from lib.core.common import decodeDbmsHexValue from lib.core.common import decodeDbmsHexValue # 导入 decodeDbmsHexValue 函数,用于解码数据库的十六进制数据
from lib.core.common import isListLike from lib.core.common import isListLike # 导入 isListLike 函数,用于检查是否为列表类型
from lib.core.common import isNumPosStrValue from lib.core.common import isNumPosStrValue # 导入 isNumPosStrValue 函数,用于检查是否为正数字符串
from lib.core.common import isStackingAvailable from lib.core.common import isStackingAvailable # 导入 isStackingAvailable 函数,用于检查是否支持堆叠查询
from lib.core.common import isTechniqueAvailable from lib.core.common import isTechniqueAvailable # 导入 isTechniqueAvailable 函数,用于检查是否支持某种注入技术
from lib.core.common import readInput from lib.core.common import readInput # 导入 readInput 函数,用于读取用户输入
from lib.core.compat import xrange from lib.core.compat import xrange # 导入 xrange 函数,用于生成数字序列
from lib.core.convert import encodeBase64 from lib.core.convert import encodeBase64 # 导入 encodeBase64 函数,用于进行 Base64 编码
from lib.core.convert import encodeHex from lib.core.convert import encodeHex # 导入 encodeHex 函数,用于进行十六进制编码
from lib.core.convert import getText from lib.core.convert import getText # 导入 getText 函数,用于将数据转换为文本
from lib.core.convert import getUnicode from lib.core.convert import getUnicode # 导入 getUnicode 函数,用于将数据转换为 Unicode
from lib.core.data import conf from lib.core.data import conf # 导入 conf 模块,用于获取全局配置信息
from lib.core.data import kb from lib.core.data import kb # 导入 kb 模块,用于获取全局知识库信息
from lib.core.data import logger from lib.core.data import logger # 导入 logger 模块,用于打印日志信息
from lib.core.enums import CHARSET_TYPE from lib.core.enums import CHARSET_TYPE # 导入 CHARSET_TYPE 枚举,用于指定字符集类型
from lib.core.enums import DBMS from lib.core.enums import DBMS # 导入 DBMS 枚举,用于指定数据库类型
from lib.core.enums import EXPECTED from lib.core.enums import EXPECTED # 导入 EXPECTED 枚举,用于指定预期的数据类型
from lib.core.enums import PAYLOAD from lib.core.enums import PAYLOAD # 导入 PAYLOAD 枚举,用于指定攻击载荷类型
from lib.core.exception import SqlmapUndefinedMethod from lib.core.exception import SqlmapUndefinedMethod # 导入 SqlmapUndefinedMethod 异常,用于处理未定义的方法
from lib.core.settings import UNICODE_ENCODING from lib.core.settings import UNICODE_ENCODING # 导入 UNICODE_ENCODING 变量,用于指定 Unicode 编码
from lib.request import inject from lib.request import inject # 导入 inject 模块,用于进行 SQL 注入
class Filesystem(object): class Filesystem(object):
""" """
This class defines generic OS file system functionalities for plugins. 这个类定义了插件的通用操作系统文件系统功能
""" """
def __init__(self): def __init__(self):
self.fileTblName = "%sfile" % conf.tablePrefix # 初始化文件表名
self.tblField = "data" self.fileTblName = "%sfile" % conf.tablePrefix # 将配置中的表前缀与 "file" 组合,生成表名
# 初始化表字段名
self.tblField = "data" # 设置表字段名为 "data"
def _checkFileLength(self, localFile, remoteFile, fileRead=False): def _checkFileLength(self, localFile, remoteFile, fileRead=False):
"""
检查本地文件和远程文件长度是否相同
Args:
localFile (str): 本地文件路径
remoteFile (str): 远程文件路径
fileRead (bool, optional): 是否为读取文件操作. Defaults to False.
Returns:
bool: 如果文件长度相同返回 True否则返回 False如果无法判断则返回 None
"""
if Backend.isDbms(DBMS.MYSQL): if Backend.isDbms(DBMS.MYSQL):
lengthQuery = "LENGTH(LOAD_FILE('%s'))" % remoteFile # 如果是 MySQL 数据库
lengthQuery = "LENGTH(LOAD_FILE('%s'))" % remoteFile # 构建获取远程文件长度的 SQL 查询
elif Backend.isDbms(DBMS.PGSQL) and not fileRead: elif Backend.isDbms(DBMS.PGSQL) and not fileRead:
lengthQuery = "SELECT SUM(LENGTH(data)) FROM pg_largeobject WHERE loid=%d" % self.oid # 如果是 PostgreSQL 数据库且不是读取文件操作
lengthQuery = "SELECT SUM(LENGTH(data)) FROM pg_largeobject WHERE loid=%d" % self.oid # 构建获取大对象长度的 SQL 查询
elif Backend.isDbms(DBMS.MSSQL): elif Backend.isDbms(DBMS.MSSQL):
self.createSupportTbl(self.fileTblName, self.tblField, "VARBINARY(MAX)") # 如果是 MSSQL 数据库
inject.goStacked("INSERT INTO %s(%s) SELECT %s FROM OPENROWSET(BULK '%s', SINGLE_BLOB) AS %s(%s)" % (self.fileTblName, self.tblField, self.tblField, remoteFile, self.fileTblName, self.tblField)) self.createSupportTbl(self.fileTblName, self.tblField, "VARBINARY(MAX)") # 创建支持表
inject.goStacked("INSERT INTO %s(%s) SELECT %s FROM OPENROWSET(BULK '%s', SINGLE_BLOB) AS %s(%s)" % (self.fileTblName, self.tblField, self.tblField, remoteFile, self.fileTblName, self.tblField)) # 使用 OPENROWSET 将文件内容插入表中
lengthQuery = "SELECT DATALENGTH(%s) FROM %s" % (self.tblField, self.fileTblName) lengthQuery = "SELECT DATALENGTH(%s) FROM %s" % (self.tblField, self.fileTblName) # 构建获取表数据长度的 SQL 查询
try: try:
localFileSize = os.path.getsize(localFile) localFileSize = os.path.getsize(localFile) # 获取本地文件大小
except OSError: except OSError:
warnMsg = "file '%s' is missing" % localFile # 如果本地文件不存在
logger.warning(warnMsg) warnMsg = "file '%s' is missing" % localFile # 构造警告消息
localFileSize = 0 logger.warning(warnMsg) # 打印警告信息
localFileSize = 0 # 将本地文件大小设置为 0
if fileRead and Backend.isDbms(DBMS.PGSQL): if fileRead and Backend.isDbms(DBMS.PGSQL):
logger.info("length of read file '%s' cannot be checked on PostgreSQL" % remoteFile) # 如果是读取文件操作且是 PostgreSQL 数据库
sameFile = True logger.info("length of read file '%s' cannot be checked on PostgreSQL" % remoteFile) # 打印信息,表示 PostgreSQL 无法检查读取文件长度
sameFile = True # 将 sameFile 设置为 True
else: else:
logger.debug("checking the length of the remote file '%s'" % remoteFile) # 如果不是读取文件或者不是 PostgreSQL 数据库
remoteFileSize = inject.getValue(lengthQuery, resumeValue=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) logger.debug("checking the length of the remote file '%s'" % remoteFile) # 打印调试信息,表示正在检查远程文件长度
sameFile = None remoteFileSize = inject.getValue(lengthQuery, resumeValue=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) # 获取远程文件大小
sameFile = None # 将 sameFile 初始化为 None
if isNumPosStrValue(remoteFileSize): if isNumPosStrValue(remoteFileSize):
remoteFileSize = int(remoteFileSize) # 如果远程文件大小为有效的数字字符串
localFile = getUnicode(localFile, encoding=sys.getfilesystemencoding() or UNICODE_ENCODING) remoteFileSize = int(remoteFileSize) # 将远程文件大小转换为整数
sameFile = False localFile = getUnicode(localFile, encoding=sys.getfilesystemencoding() or UNICODE_ENCODING) # 将本地文件路径转换为 Unicode
sameFile = False # 将 sameFile 设置为 False
if localFileSize == remoteFileSize: if localFileSize == remoteFileSize:
sameFile = True # 如果本地文件大小和远程文件大小相同
infoMsg = "the local file '%s' and the remote file " % localFile sameFile = True # 将 sameFile 设置为 True
infoMsg += "'%s' have the same size (%d B)" % (remoteFile, localFileSize) infoMsg = "the local file '%s' and the remote file " % localFile # 构造信息消息
infoMsg += "'%s' have the same size (%d B)" % (remoteFile, localFileSize) # 将远程文件路径和文件大小添加到信息消息中
elif remoteFileSize > localFileSize: elif remoteFileSize > localFileSize:
infoMsg = "the remote file '%s' is larger (%d B) than " % (remoteFile, remoteFileSize) # 如果远程文件大小大于本地文件大小
infoMsg += "the local file '%s' (%dB)" % (localFile, localFileSize) infoMsg = "the remote file '%s' is larger (%d B) than " % (remoteFile, remoteFileSize) # 构造信息消息
infoMsg += "the local file '%s' (%dB)" % (localFile, localFileSize) # 将本地文件路径和文件大小添加到信息消息中
else: else:
infoMsg = "the remote file '%s' is smaller (%d B) than " % (remoteFile, remoteFileSize) # 如果远程文件大小小于本地文件大小
infoMsg += "file '%s' (%d B)" % (localFile, localFileSize) infoMsg = "the remote file '%s' is smaller (%d B) than " % (remoteFile, remoteFileSize) # 构造信息消息
infoMsg += "file '%s' (%d B)" % (localFile, localFileSize) # 将本地文件路径和文件大小添加到信息消息中
logger.info(infoMsg) logger.info(infoMsg) # 打印信息消息
else: else:
sameFile = False # 如果远程文件大小不是有效的数字字符串
warnMsg = "it looks like the file has not been written (usually " sameFile = False # 将 sameFile 设置为 False
warnMsg += "occurs if the DBMS process user has no write " warnMsg = "it looks like the file has not been written (usually " # 构造警告消息
warnMsg += "privileges in the destination path)" warnMsg += "occurs if the DBMS process user has no write " # 警告消息补充
logger.warning(warnMsg) warnMsg += "privileges in the destination path)" # 警告消息补充
logger.warning(warnMsg) # 打印警告消息
return sameFile return sameFile # 返回文件长度检查结果
def fileToSqlQueries(self, fcEncodedList): def fileToSqlQueries(self, fcEncodedList):
""" """
Called by MySQL and PostgreSQL plugins to write a file on the 将编码后的文件内容转换为 SQL 查询语句用于 MySQL PostgreSQL
back-end DBMS underlying file system
""" Args:
fcEncodedList (list): 编码后的文件内容列表
counter = 0 Returns:
sqlQueries = [] list: SQL 查询语句列表
"""
counter = 0 # 初始化计数器
sqlQueries = [] # 初始化 SQL 查询语句列表
for fcEncodedLine in fcEncodedList: for fcEncodedLine in fcEncodedList: # 遍历编码后的文件内容列表
if counter == 0: if counter == 0:
# 如果是第一个编码行
sqlQueries.append("INSERT INTO %s(%s) VALUES (%s)" % (self.fileTblName, self.tblField, fcEncodedLine)) sqlQueries.append("INSERT INTO %s(%s) VALUES (%s)" % (self.fileTblName, self.tblField, fcEncodedLine))
# 将带有编码数据的插入语句添加到 SQL 查询列表中
else: else:
# 如果不是第一个编码行
updatedField = agent.simpleConcatenate(self.tblField, fcEncodedLine) updatedField = agent.simpleConcatenate(self.tblField, fcEncodedLine)
# 构建更新语句,将编码行添加到数据字段中
sqlQueries.append("UPDATE %s SET %s=%s" % (self.fileTblName, self.tblField, updatedField)) sqlQueries.append("UPDATE %s SET %s=%s" % (self.fileTblName, self.tblField, updatedField))
# 将更新语句添加到 SQL 查询列表中
counter += 1 counter += 1 # 计数器加 1
return sqlQueries return sqlQueries # 返回 SQL 查询语句列表
def fileEncode(self, fileName, encoding, single, chunkSize=256): def fileEncode(self, fileName, encoding, single, chunkSize=256):
""" """
Called by MySQL and PostgreSQL plugins to write a file on the 读取文件内容并进行编码
back-end DBMS underlying file system
""" Args:
fileName (str): 文件路径
encoding (str): 编码方式例如 "hex""base64" 或其他编码
single (bool): 是否将所有内容编码为单行
chunkSize (int, optional): 分块大小. Defaults to 256.
checkFile(fileName) Returns:
list: 编码后的文件内容列表
"""
checkFile(fileName) # 检查文件是否存在
with open(fileName, "rb") as f: with open(fileName, "rb") as f:
content = f.read() # 打开文件进行读取
content = f.read() # 读取文件内容
return self.fileContentEncode(content, encoding, single, chunkSize) return self.fileContentEncode(content, encoding, single, chunkSize) # 返回编码后的文件内容
def fileContentEncode(self, content, encoding, single, chunkSize=256): def fileContentEncode(self, content, encoding, single, chunkSize=256):
retVal = [] """
对文件内容进行编码
Args:
content (bytes): 文件内容
encoding (str): 编码方式例如 "hex""base64" 或其他编码
single (bool): 是否将所有内容编码为单行
chunkSize (int, optional): 分块大小. Defaults to 256.
Returns:
list: 编码后的文件内容列表
"""
retVal = [] # 初始化返回列表
if encoding == "hex": if encoding == "hex":
content = encodeHex(content) # 如果编码方式为 "hex"
content = encodeHex(content) # 将文件内容进行十六进制编码
elif encoding == "base64": elif encoding == "base64":
content = encodeBase64(content) # 如果编码方式为 "base64"
content = encodeBase64(content) # 将文件内容进行 Base64 编码
else: else:
content = codecs.encode(content, encoding) # 如果编码方式不是 "hex" 或 "base64"
content = codecs.encode(content, encoding) # 使用指定的编码方式进行编码
content = getText(content).replace("\n", "") content = getText(content).replace("\
", "") # 将编码后的内容转换为文本,并删除换行符
if not single: if not single:
# 如果不是单行编码
if len(content) > chunkSize: if len(content) > chunkSize:
# 如果内容长度大于分块大小
for i in xrange(0, len(content), chunkSize): for i in xrange(0, len(content), chunkSize):
_ = content[i:i + chunkSize] # 按照分块大小进行分块
_ = content[i:i + chunkSize] # 获取当前分块
if encoding == "hex": if encoding == "hex":
_ = "0x%s" % _ # 如果编码方式为 "hex"
_ = "0x%s" % _ # 添加十六进制前缀
elif encoding == "base64": elif encoding == "base64":
_ = "'%s'" % _ # 如果编码方式为 "base64"
_ = "'%s'" % _ # 添加单引号
retVal.append(_) retVal.append(_) # 将当前分块添加到返回列表中
if not retVal: if not retVal:
# 如果返回列表为空
if encoding == "hex": if encoding == "hex":
content = "0x%s" % content # 如果编码方式为 "hex"
content = "0x%s" % content # 添加十六进制前缀
elif encoding == "base64": elif encoding == "base64":
content = "'%s'" % content # 如果编码方式为 "base64"
content = "'%s'" % content # 添加单引号
retVal = [content] retVal = [content] # 将编码后的内容添加到返回列表中
return retVal return retVal # 返回编码后的文件内容列表
def askCheckWrittenFile(self, localFile, remoteFile, forceCheck=False): def askCheckWrittenFile(self, localFile, remoteFile, forceCheck=False):
choice = None """
询问用户是否需要检查写入的文件
Args:
localFile (str): 本地文件路径
remoteFile (str): 远程文件路径
forceCheck (bool, optional): 是否强制检查. Defaults to False.
Returns:
bool: 如果文件写入成功返回 True如果用户选择不检查返回 True否则返回 False
"""
choice = None # 初始化用户选择
if forceCheck is not True: if forceCheck is not True:
message = "do you want confirmation that the local file '%s' " % localFile # 如果不强制检查
message += "has been successfully written on the back-end DBMS " message = "do you want confirmation that the local file '%s' " % localFile # 构造询问消息
message += "file system ('%s')? [Y/n] " % remoteFile message += "has been successfully written on the back-end DBMS " # 消息补充
choice = readInput(message, default='Y', boolean=True) message += "file system ('%s')? [Y/n] " % remoteFile # 消息补充
choice = readInput(message, default='Y', boolean=True) # 读取用户输入
if forceCheck or choice: if forceCheck or choice:
return self._checkFileLength(localFile, remoteFile) # 如果强制检查或者用户选择检查
return self._checkFileLength(localFile, remoteFile) # 调用检查文件长度函数
return True return True # 如果用户选择不检查,则返回 True
def askCheckReadFile(self, localFile, remoteFile): def askCheckReadFile(self, localFile, remoteFile):
"""
询问用户是否需要检查读取的文件
Args:
localFile (str): 本地文件路径
remoteFile (str): 远程文件路径
Returns:
bool: 如果文件读取成功返回 True如果用户选择不检查返回 None
"""
if not kb.bruteMode: if not kb.bruteMode:
message = "do you want confirmation that the remote file '%s' " % remoteFile # 如果不是爆破模式
message += "has been successfully downloaded from the back-end " message = "do you want confirmation that the remote file '%s' " % remoteFile # 构造询问消息
message += "DBMS file system? [Y/n] " message += "has been successfully downloaded from the back-end " # 消息补充
message += "DBMS file system? [Y/n] " # 消息补充
if readInput(message, default='Y', boolean=True): if readInput(message, default='Y', boolean=True):
return self._checkFileLength(localFile, remoteFile, True) # 读取用户输入
return self._checkFileLength(localFile, remoteFile, True) # 如果用户选择检查,调用检查文件长度函数
return None return None # 如果用户选择不检查,则返回 None
def nonStackedReadFile(self, remoteFile): def nonStackedReadFile(self, remoteFile):
errMsg = "'nonStackedReadFile' method must be defined " """
errMsg += "into the specific DBMS plugin" 使用非堆叠查询技术读取远程文件需要在子类中实现
raise SqlmapUndefinedMethod(errMsg)
Args:
remoteFile (str): 远程文件路径
Raises:
SqlmapUndefinedMethod: 如果没有在子类中实现该方法则抛出该异常
"""
errMsg = "'nonStackedReadFile' method must be defined " # 构造错误消息
errMsg += "into the specific DBMS plugin" # 错误消息补充
raise SqlmapUndefinedMethod(errMsg) # 抛出 SqlmapUndefinedMethod 异常
def stackedReadFile(self, remoteFile): def stackedReadFile(self, remoteFile):
errMsg = "'stackedReadFile' method must be defined " """
errMsg += "into the specific DBMS plugin" 使用堆叠查询技术读取远程文件需要在子类中实现
raise SqlmapUndefinedMethod(errMsg)
Args:
remoteFile (str): 远程文件路径
Raises:
SqlmapUndefinedMethod: 如果没有在子类中实现该方法则抛出该异常
"""
errMsg = "'stackedReadFile' method must be defined " # 构造错误消息
errMsg += "into the specific DBMS plugin" # 错误消息补充
raise SqlmapUndefinedMethod(errMsg) # 抛出 SqlmapUndefinedMethod 异常
def unionWriteFile(self, localFile, remoteFile, fileType, forceCheck=False): def unionWriteFile(self, localFile, remoteFile, fileType, forceCheck=False):
errMsg = "'unionWriteFile' method must be defined " """
errMsg += "into the specific DBMS plugin" 使用 UNION 查询技术写入文件需要在子类中实现
raise SqlmapUndefinedMethod(errMsg)
Args:
localFile (str): 本地文件路径
remoteFile (str): 远程文件路径
fileType (str): 文件类型
forceCheck (bool, optional): 是否强制检查. Defaults to False.
Raises:
SqlmapUndefinedMethod: 如果没有在子类中实现该方法则抛出该异常
"""
errMsg = "'unionWriteFile' method must be defined " # 构造错误消息
errMsg += "into the specific DBMS plugin" # 错误消息补充
raise SqlmapUndefinedMethod(errMsg) # 抛出 SqlmapUndefinedMethod 异常
def stackedWriteFile(self, localFile, remoteFile, fileType, forceCheck=False): def stackedWriteFile(self, localFile, remoteFile, fileType, forceCheck=False):
errMsg = "'stackedWriteFile' method must be defined " """
errMsg += "into the specific DBMS plugin" 使用堆叠查询技术写入文件需要在子类中实现
raise SqlmapUndefinedMethod(errMsg)
Args:
localFile (str): 本地文件路径
remoteFile (str): 远程文件路径
fileType (str): 文件类型
forceCheck (bool, optional): 是否强制检查. Defaults to False.
Raises:
SqlmapUndefinedMethod: 如果没有在子类中实现该方法则抛出该异常
"""
errMsg = "'stackedWriteFile' method must be defined " # 构造错误消息
errMsg += "into the specific DBMS plugin" # 错误消息补充
raise SqlmapUndefinedMethod(errMsg) # 抛出 SqlmapUndefinedMethod 异常
def readFile(self, remoteFile): def readFile(self, remoteFile):
localFilePaths = [] """
读取远程文件
Args:
remoteFile (str): 远程文件路径
Returns:
list: 本地文件路径列表
"""
localFilePaths = [] # 初始化本地文件路径列表
self.checkDbmsOs() self.checkDbmsOs() # 检查数据库类型和操作系统类型
for remoteFile in remoteFile.split(','): for remoteFile in remoteFile.split(','):
fileContent = None # 遍历所有远程文件路径
kb.fileReadMode = True fileContent = None # 初始化文件内容
kb.fileReadMode = True # 设置文件读取模式为 True
if conf.direct or isStackingAvailable(): if conf.direct or isStackingAvailable():
# 如果使用直接连接或支持堆叠查询
if isStackingAvailable(): if isStackingAvailable():
debugMsg = "going to try to read the file with stacked query SQL " # 如果支持堆叠查询
debugMsg += "injection technique" debugMsg = "going to try to read the file with stacked query SQL " # 构造调试消息
logger.debug(debugMsg) debugMsg += "injection technique" # 调试消息补充
logger.debug(debugMsg) # 打印调试消息
fileContent = self.stackedReadFile(remoteFile) fileContent = self.stackedReadFile(remoteFile) # 使用堆叠查询技术读取文件
elif Backend.isDbms(DBMS.MYSQL): elif Backend.isDbms(DBMS.MYSQL):
debugMsg = "going to try to read the file with non-stacked query " # 如果是 MySQL 数据库
debugMsg += "SQL injection technique" debugMsg = "going to try to read the file with non-stacked query " # 构造调试消息
logger.debug(debugMsg) debugMsg += "SQL injection technique" # 调试消息补充
logger.debug(debugMsg) # 打印调试消息
fileContent = self.nonStackedReadFile(remoteFile) fileContent = self.nonStackedReadFile(remoteFile) # 使用非堆叠查询技术读取文件
else: else:
errMsg = "none of the SQL injection techniques detected can " # 如果无法使用以上技术读取文件
errMsg += "be used to read files from the underlying file " errMsg = "none of the SQL injection techniques detected can " # 构造错误消息
errMsg += "system of the back-end %s server" % Backend.getDbms() errMsg += "be used to read files from the underlying file " # 错误消息补充
logger.error(errMsg) errMsg += "system of the back-end %s server" % Backend.getDbms() # 错误消息补充
logger.error(errMsg) # 打印错误消息
fileContent = None fileContent = None # 将文件内容设置为 None
kb.fileReadMode = False kb.fileReadMode = False # 设置文件读取模式为 False
if fileContent in (None, "") and not Backend.isDbms(DBMS.PGSQL): if fileContent in (None, "") and not Backend.isDbms(DBMS.PGSQL):
self.cleanup(onlyFileTbl=True) # 如果文件内容为空并且不是 PostgreSQL 数据库
self.cleanup(onlyFileTbl=True) # 清理文件表
elif isListLike(fileContent): elif isListLike(fileContent):
newFileContent = "" # 如果文件内容是一个列表
newFileContent = "" # 初始化新的文件内容
for chunk in fileContent: for chunk in fileContent:
# 遍历文件内容中的块
if isListLike(chunk): if isListLike(chunk):
# 如果块本身是一个列表
if len(chunk) > 0: if len(chunk) > 0:
chunk = chunk[0] # 如果块列表不为空
chunk = chunk[0] # 获取块列表的第一个元素
else: else:
chunk = "" # 如果块列表为空
chunk = "" # 将块设置为空字符串
if chunk: if chunk:
newFileContent += chunk # 如果块不为空
newFileContent += chunk # 将块添加到新的文件内容中
fileContent = newFileContent fileContent = newFileContent # 将新的文件内容赋值给 fileContent
if fileContent is not None: if fileContent is not None:
fileContent = decodeDbmsHexValue(fileContent, True) # 如果文件内容不为空
fileContent = decodeDbmsHexValue(fileContent, True) # 解码文件内容
if fileContent.strip(): if fileContent.strip():
localFilePath = dataToOutFile(remoteFile, fileContent) # 如果文件内容不为空
localFilePath = dataToOutFile(remoteFile, fileContent) # 将文件内容写入本地文件
if not Backend.isDbms(DBMS.PGSQL): if not Backend.isDbms(DBMS.PGSQL):
self.cleanup(onlyFileTbl=True) # 如果不是 PostgreSQL 数据库
self.cleanup(onlyFileTbl=True) # 清理文件表
sameFile = self.askCheckReadFile(localFilePath, remoteFile) sameFile = self.askCheckReadFile(localFilePath, remoteFile) # 询问用户是否需要检查读取的文件
if sameFile is True: if sameFile is True:
localFilePath += " (same file)" # 如果文件相同
localFilePath += " (same file)" # 添加 (same file) 后缀
elif sameFile is False: elif sameFile is False:
localFilePath += " (size differs from remote file)" # 如果文件大小不同
localFilePath += " (size differs from remote file)" # 添加 (size differs from remote file) 后缀
localFilePaths.append(localFilePath) localFilePaths.append(localFilePath) # 将本地文件路径添加到列表中
elif not kb.bruteMode: elif not kb.bruteMode:
errMsg = "no data retrieved" # 如果文件内容为空并且不是爆破模式
logger.error(errMsg) errMsg = "no data retrieved" # 构造错误消息
logger.error(errMsg) # 打印错误消息
return localFilePaths return localFilePaths # 返回本地文件路径列表
def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False):
written = False """
写入本地文件到远程服务器
Args:
localFile (str): 本地文件路径
remoteFile (str): 远程文件路径
fileType (str, optional): 文件类型. Defaults to None.
forceCheck (bool, optional): 是否强制检查文件长度. Defaults to False.
Returns:
bool: 如果文件写入成功则返回 True否则返回 False
"""
written = False # 初始化写入状态
checkFile(localFile) checkFile(localFile) # 检查本地文件是否存在
self.checkDbmsOs() self.checkDbmsOs() # 检查数据库类型和操作系统类型
if localFile.endswith('_'): if localFile.endswith('_'):
localFile = getUnicode(decloakToTemp(localFile)) # 如果本地文件名以 '_' 结尾
localFile = getUnicode(decloakToTemp(localFile)) # 将伪装的文件名转换为临时文件路径
if conf.direct or isStackingAvailable(): if conf.direct or isStackingAvailable():
# 如果使用直接连接或者支持堆叠查询技术
if isStackingAvailable(): if isStackingAvailable():
debugMsg = "going to upload the file '%s' with " % fileType # 如果支持堆叠查询技术
debugMsg += "stacked query technique" debugMsg = "going to upload the file '%s' with " % fileType # 构造调试消息
logger.debug(debugMsg) debugMsg += "stacked query technique" # 调试消息补充
logger.debug(debugMsg) # 打印调试信息
written = self.stackedWriteFile(localFile, remoteFile, fileType, forceCheck) written = self.stackedWriteFile(localFile, remoteFile, fileType, forceCheck) # 使用堆叠查询技术写入文件
self.cleanup(onlyFileTbl=True) self.cleanup(onlyFileTbl=True) # 清理临时表
elif isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) and Backend.isDbms(DBMS.MYSQL): elif isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) and Backend.isDbms(DBMS.MYSQL):
debugMsg = "going to upload the file '%s' with " % fileType # 如果支持 UNION 查询技术并且是 MySQL 数据库
debugMsg += "UNION query technique" debugMsg = "going to upload the file '%s' with " % fileType # 构造调试信息
logger.debug(debugMsg) debugMsg += "UNION query technique" # 调试消息补充
logger.debug(debugMsg) # 打印调试信息
written = self.unionWriteFile(localFile, remoteFile, fileType, forceCheck) written = self.unionWriteFile(localFile, remoteFile, fileType, forceCheck) # 使用 UNION 查询技术写入文件
elif Backend.isDbms(DBMS.MYSQL): elif Backend.isDbms(DBMS.MYSQL):
debugMsg = "going to upload the file '%s' with " % fileType # 如果是 MySQL 数据库
debugMsg += "LINES TERMINATED BY technique" debugMsg = "going to upload the file '%s' with " % fileType # 构造调试信息
logger.debug(debugMsg) debugMsg += "LINES TERMINATED BY technique" # 调试消息补充
logger.debug(debugMsg) # 打印调试信息
written = self.linesTerminatedWriteFile(localFile, remoteFile, fileType, forceCheck) written = self.linesTerminatedWriteFile(localFile, remoteFile, fileType, forceCheck) # 使用 LINES TERMINATED BY 技术写入文件
else: else:
errMsg = "none of the SQL injection techniques detected can " # 如果以上技术都无法使用
errMsg += "be used to write files to the underlying file " errMsg = "none of the SQL injection techniques detected can " # 构造错误消息
errMsg += "system of the back-end %s server" % Backend.getDbms() errMsg += "be used to write files to the underlying file " # 错误消息补充
logger.error(errMsg) errMsg += "system of the back-end %s server" % Backend.getDbms() # 错误消息补充
logger.error(errMsg) # 打印错误消息
return None return None # 返回 None
return written return written # 返回写入状态
Loading…
Cancel
Save