【i春秋】 Web —— 爆破-3
0x00 前言
此题出自「百度杯」CTF 比赛 2017 二月场,是第三道 「爆破」系列的 Web 题,主要考察在理解了题目 PHP 代码的逻辑后,通过网络编程与服务器交互获取 flag 的能力,难度中低,需要的基础知识有:PHP、Python、HTTP协议。
题目链接在「i春秋」的 CTF 大本营,解题链接通过创建在线靶场后得到:
0x01 理解 PHP 代码逻辑
打开链接,发现本题的 PHP 源码比前两题要复杂,做审计题首先是要理解代码逻辑:
1 |
|
- Line 3~9:开始会话,包含 flag.php 文件,为超级全局变量
$_SESSION
的三个参数初始化。 - Line 11~13:若 Session 有效期超过了两分钟,则销毁当前会话。
- Line 15~17:先通过 GET 请求或 POST 请求获取的
value
参数,再随机选择两个小写字母拼接成字符串。 - Line 19~23:若
$_SESSION['whoami']
等于$value
数组中两个元素的拼接,并且$value
的 MD5 哈希值的第 5 至 8 位等于 0,则将$_SESSION['nums']
自增,将$_SESSION['whoami']
更新为随机字符串并输出。 - Line 25~27:若
$_SESSION['nums']
大于等于 10,则输出 flag。
理解了代码逻辑后,再结合提示,基本可判断此题真的需要通过网络交互,来「爆破」获取 flag。
0x02 编写 Python 网络通信脚本
本题的难点是如何通过 GET 或 POST 请求,传送同一数组参数的不同元素值。常见方法有以下两种:
value[]=e&value[]=a
value[0]=e&value[1]=a
使用 Firefox 浏览器,针对 GET 请求进行验证。第一种方法:
第二种方法:
针对 POST 请求同样适用,请读者自行验证。
将页面显示的新值传入 value
参数后,按上述方法手动循环 10 次,即可满足输出 flag 的条件,但只仅对于次数少的情况有效,如果循环 100 次,还想继续手动操作吗?这时,使用 Python 的第三方开源库 Requests 编写自动化脚本,即可轻松解决问题,基本用法可参考:
以下是针对 GET 请求构造的 Python 解题脚本:
1 | #!/usr/bin/env python3 |
- Line 8~12:自动循环提交 10 次 GET 请求,并输出每次循环的
value
参数。 - Line 13:将最后一次响应的报文内容输出,即可看到 flag。
将第 10 行改为 payload = "?value[]={}&value[]={}".format(whoami[0], whoami[1])
同样能获得 flag。
0x03 使用 BurpSuite 抓取 Python 流量数据
针对 GET 请求直接构造字符串 payload 相对简单,如果使用 POST 请求,还需要构造表单数据的 dict
类型变量。以下是针对 POST 请求构造的 Python 解题脚本:
1 | #!/usr/bin/env python3 |
显而易见,只有第 10、11 行与 GET 请求不同,运行完上述脚本后也能获得 flag。不过,当我们依葫芦画瓢将第 10 行改为 payload = {"value[]": whoami[0], "value[]": whoami[1]}
后,却发现无法获得 flag 了:
借此机会,讲解一下如何使用 Burp Suite 抓取 Python 发送的网络请求包,相信大家看到请求包后,答案便一目了然。以下是利用代理抓取 POST 请求包的 Python 脚本:
1 | #!/usr/bin/env python3 |
- 构造 POST 请求中
proxies
代理参数的dict
类型变量,其中http
是代理访问 URL 的协议类型,127.0.0.1:8080
是 Burp Suite 的本地监听地址与端口号。
将 Burp Suite 开启 Intercept 模式,运行上述脚本即可看到:
可发现表单数据只剩 value[]=a
,原来前面 value[]=e
的值已被覆盖。那么问题来了,如何构造 POST 请求中不带索引的同一数组参数的不同值呢?
正确的构造方法是 payload = {"value[]": [whoami[0], whoami[1]]}
,再次运行脚本,即可看到表单数据正常提交了:
其实,使用 requests 模块自带的方法也能查看请求头与请求体,对应的 Python 脚本如下:
1 | #!/usr/bin/env python3 |
运行后即可在终端上看到请求头与请求体: