SHU-CTF – lottery
Sorry about the Click-bait title.
This article is about how PHP type juggling and exploiting the Lottery challenge from SHU-CTF.
This challenge is hosted at http://120.79.191.75/web-test/lottery/index.php
and at the time of writing still online.
Finding the vulnerability
They were nice enough to provide us with the source code of the challenge.
So lets start by checking how things work and see if we can find some bugs.
in api.php there is a function that will compare the winning lottery numbers against the user picked numbers. (api.php line 80)
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]){ $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]); }
The vulnerability lays in the comparison of the following line. A loose comparison == is used instead of a strict comparison ===
if($numbers[$i] == $win_numbers[$i]){ $same_count++; }
This means that if I am able to send true instead a number it will match 1,2,3,4,5,6,7,8 and 9 the only number it won’t match is 0.
The only restrictions on the input is done on the client side. (buy.php line 7)
<input type="text" name="numbers" id="numbers" minlength="7" maxlength="7" pattern="\d{7}" required placeholder="7 numbers">
and the json is build up and ajax request send in buy.js (line 1)
function buy(){
$('#wait').show();
$('#result').hide();
var input = $('#numbers')[0];
if(input.validity.valid){
var numbers = input.value;
$.ajax({
method: "POST",
url: "api.php",
dataType: "json",
contentType: "application/json",
data: JSON.stringify({ action: "buy", numbers: numbers })
}).done(function(resp){
if(resp.status == 'ok'){
show_result(resp);
} else {
alert(resp.msg);
}
})
} else {
alert('invalid');
}
$('#wait').hide();
}
exploiting the vulnerability
I could just boot up burp suit and intercept and modify the requests.
or craft some curl requests. But for the sake of simplicity I’ll use my browsers debugger.
First get our self a session going by registering at http://120.79.191.75/web-test/lottery/register.php
After registering it will redirect you to the buy.php page.
Hit ‘f12’ to open up the debugging console in your browser.
just enter any 7 numbers into the box next to the buy button on the page.
and go to the sources tab in the debugger and open ‘js/buy.js’
now change line 12 from:data: JSON.stringify({ action: "buy", numbers: numbers })
to:data: JSON.stringify({ action: "buy", numbers: [true,true,true,true,true,true,true] })
press CTRL+S to activate the modified script.
And click the buy button until you have enough money to buy me a beer.