本文仅作为技术讨论及分享,严禁用于任何非法用途。
前言
在渗透工作中我们经常能碰到一些逻辑复杂的SQL注入漏洞,并不能直接通过sqlmap工具注入拿到结果。今年网鼎杯的一道SQL注入题“张三的网站”让我久久不能忘怀,我不断思考遇到这类型的SQL注入除了手工注入然后编写脚本一点一点脱数据以外,有没有一个比较优雅的解决方案呢?
一道CTF题的思考
先来说说“张三的网站”这道题目,因为我手上没有题目源码,所以就根据记忆中的各个功能自己写了一个(很少写php,代码很烂),相关代码已经上传到GitHub,见文章底部。
该题目主要涉及3个页面:
- 登陆页面
- 注册页面
- 登陆后的主页
题目中的登陆页面、注册页面均无SQL注入漏洞,但是登陆后的主页在用户名处存在SQL注入漏洞。要利用此漏洞,需要在注册页面控制用户名,邮箱使用随机数生成的邮箱,密码随意,然后使用邮箱和注册时的密码登陆,登陆成功后跳转到主页,此时触发SQL注入漏洞。
注册名为“123”的用户:
注册名为“123’”的用户:
以下是一个Python脚本手工注入的解法:
1 | import requests |
如果对ctf不熟悉的朋友应该会很懵,因为语句中直接查询获取了flag表中的内容,而正常情况下,我们是不知道真正的flag在上面表,这样的解法我个人觉得不具备通用性,当然了在ctf比赛中是很高效的。
那么,有没有可能通过sqlmap来进行注入呢?显然,直接使用sqlmap不进行二次开发是无法检测出注入点的,因为sqlmap的注入逻辑不支持多个数据包的逻辑处理。于是我在想有无一种办法,拿到sqlmap的注入检测payload,然后我们通过Python编写相应的请求逻辑,再把响应结果返回到sqlmap呢?答案是可行的!
Flask中转sqlmap注入
代码实现的结构如下,首先创建一个flask服务,接收payload
参数的值,然后传入函数custom_fun
中,custom_fun
函数由自己编写请求逻辑,把payload
参数的值填入到存在注入点的参数中,然后发起请求,把最终响应结果return就行。最后通过sqlmap检测URL:http://127.0.0.1:5000/?payload=1
即可,可以适当调整sqlmap的注入参数,比如--level
、--risk
、--technique
等。
1 | from flask import Flask |
流程示意图如下:
完整注入过程
先来看看本例的实现代码:
1 | from flask import Flask |
从代码上可以看到,只需要把请求逻辑写到custom_fun
函数中,把最终结果的响应包return给flask,剩下的就可以交给sqlmap了,优雅!
这里说一个小技巧,可以使用Burp的拓展Copy As Python-Requests
来一键把burp的请求复制为Python requests请求:
然后使用sqlmap测试一下,因为是通过本地flask中转,我们的sqlmap的target应该是本地的flask服务端口,命令如下:
1 | sqlmap -u http://127.0.0.1:5000/?payload=1 |
检测时flask服务的输出:
成功检测到注入点:
当前数据库:
1 | sqlmap -u http://127.0.0.1:5000/?payload=1 --current-db |
跑表名:
1 | sqlmap -u http://127.0.0.1:5000/?payload=1 -D test --tables |
跑flag表数据:
1 | sqlmap -u http://127.0.0.1:5000/?payload=1 -D test -T flag --dump |