CTF:lottery(代码审计|==比较绕过)

Posted luocodes

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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

技术图片



以上是关于CTF:lottery(代码审计|==比较绕过)的主要内容,如果未能解决你的问题,请参考以下文章

实验吧CTF练习题---WEB---因缺思汀的绕过解析

代码审计-sha()函数比较绕过

Lottery

Lottery

CTF---Web入门第六题 因缺思汀的绕过

CTF中常用PHP特性总结