title: D-CTF Qualifiers 2016 - Web300 like a dipsh\*t author: depierre published: 2016-09-27 categories: CTF, Security keywords: quals, dctf, ctf, write-up, web, 300, challenge, security, pupute First of all, wow it's been a while and a lot of things happened since last time I wrote something on my little spot of the Internet... Last weekend I played the D-CTF Qualifiers 2016 with some friends of mine ([@xarkes](https://twitter.com/xarkes_), [jfrankowski](http://jody.io) and a new friend of mine). I was told that it is not a good CTF due to multiple problems in previous version with the challenges, where for instance some exploit challenge would not be solvable at all. And apparently it was the same this year (see the [comments](https://ctftime.org/event/356)) and it was frustrating at some point... However, it didn't stop us from having quite a blast. One in particular, the Super Secure Company LLC web challenge, worth 300 points. We used our best tool in French history, called *puputerie*, or how to gain 300 points like a dipsh\*t! If you're interested, keep reading and I hope you'll forgive me. # Super Secure Company LLC For this challenge, you could access the source code of some pages. For instance, you could read the PHP source of the `index.php` file: :::php query('INSERT INTO `urls` (url, view) VALUES ("'.$db->real_escape_string($_POST['url-bad']).'",0)'); } /* [. . .] */ ?> Nothing really got our attention. Reading the `contact` case, we thought that it could be a cross-site request forgery/half baked social engineering kind of challenge. But there was nothing much to see in `index.php`. With some tests, we found that the `begin_with` function was checking that the URL was starting with **http://10.13.37.13/** (which is not totally true, but more on that later). After a while, we found that there was an `admin.php` file as well: :::php query('SELECT * FROM urls WHERE view=0'); while($row = $rows->fetch_array()) { if(parse_url($row['url'], PHP_URL_HOST) != parse_url($config['url'], PHP_URL_HOST)) continue; //todo update link below $content .= '
Report '.$row['id'].'Hide
'; } break; /* [. . .] */ ?> The `logs` case could be interesting. Combining the `contact` and the `logs` cases, we would have our CSRF attack: + Report a broken link to an admin API endpoint (we have yet to find one) + Admin sees the link in the logs page and clicks on it (simulated by a bot of some sort for the challenge we guess) + Admin automatically executes the action and does something interesting for us (yet to be defined) Next in the `admin.php` file, we find that the `upload` feature is vulnerable to arbitrary PHP file upload: :::php 500000) { $content.= "Sorry, your file is too large."; $uploadOk = 0; } $extension = @explode('.', $_FILES['file']['name']); $extension = @end($extension); if($extension == '' || $extension == 'php' || $extension == 'htaccess' || $extension == 'pl' || $extension == 'py' || $extension == 'c' || $extension == 'cpp' || $extension == 'ini' || $extension == 'html') { $content.= "Sorry, invalid extension."; $uploadOk = 0; } if($uploadOk) { if (move_uploaded_file($_FILES["file"]["tmp_name"], $target_file)) { $content.= "The file ". htmlentities(basename( $_FILES["file"]["name"]), ENT_QUOTES). " has been uploaded."; } else { $content.= "Sorry, there was an error uploading your file."; } } } ?> We can upload a PHP file containing our backdoor using an extension like `.php5` for instance. However, sending a POST request is not possible with only a "the admin clicks on my link"-attack like the potential one in `logs`. Except if the link the admin clicked on contains malicious JavaScript (with a XSS for instance), which we started to look for. # Getting bored Some times later, we couldn't find any XSS so I decided to fire up `dirbuster` and maybe find the PHP file implementating `getContentFromUrl` used in `index.php` for instance. ![DirBuster Results](/static/images/dctf2016/dirbuster.png) So we have another PHP file `functions.php` but we didn't find anything interesting. Accessing the Apache `server-status` page was of course forbidden: :::http GET /server-status/index.php HTTP/1.1 Host: 10.13.37.13 Response from the server: :::http HTTP/1.1 403 Forbidden Date: Sun, 25 Sep 2016 00:14:02 GMT Server: Apache/2.4.18 (Ubuntu) Content-Length: 309 Connection: close Content-Type: text/html; charset=iso-8859-1 403 Forbidden

Forbidden

You don't have permission to access /server-status/index.php on this server.


Apache/2.4.18 (Ubuntu) Server at 10.13.37.13 Port 80
But what if... What if we could access it after all? We could try abusing the `print` feature in `index.php` so that the function `file_get_contents` in `getContentFromUrl` (defined in `functions.php`) would be called. Would that bypass the access restriction on the server status page? :::http GET /?page=print&load_template=1&url=aHR0cDovLzEwLjEzLjM3LjEzL3NlcnZlci1zdGF0dXMvaW5kZXgucGhw HTTP/1.1 Host: 10.13.37.13 (Where the `url` parameter is base64 of `http://10.13.37.13/server-status/index.php`) ![Apache Server Status](/static/images/dctf2016/server-status.png) Yes, yes it would! Now it's time to start thinking like a dipsh\*t. We know that the very last stage of the challenge is to use the PHP backdoor you previously uploaded in the directory `/uploads/`. So... we could wait for another team to solve the challenge... and use theirs! I wrote a quick python script that would request the server status page every second and print the requests from the `Request` column. Then I would grep the logs for `/uploads/`. # Getting excited again Waiting for a match, we moved to another challenge but I kept a close eye on the terminal and a couple of hours later: ![Dirty Monitor](/static/images/dctf2016/uploads_log.png) I felt excited like during my first phishing campaign! It's not everyday that you can solve a CTF challenge like a *pupute* :) So we used the first PHP file that was working, ironically named `supershell_you_will_never_find_2.php5` (the first version didn't seem to work apparently haha), and injected our own commands: :::http GET /uploads/supershell_you_will_never_find_2.php5?1=cat+../../../../flag HTTP/1.1 Host: 10.13.37.13 And lo and behold: :::http hl_lines="8" HTTP/1.1 200 OK Date: Sun, 25 Sep 2016 01:35:14 GMT Server: Apache/2.4.18 (Ubuntu) Content-Length: 39 Connection: close Content-Type: text/html; charset=UTF-8 DCTF{5a42e723159e537443b99ba7f95fbe04} And there you have it: 300 points for all of *our* hard work! As honest dipsh\*ts, we reported the server status to the CTF admins, who removed it so nobody would take advantage of it. Because it would be a shame if someone were to abuse it... wouldn't it? # Quick word When reading the very good and concise [write up from Los Fuzzys](https://losfuzzys.github.io/writeup/2016/09/25/dctfquals2016-super-secure-company-llc/), we realized that our error was to assume that the `$config['url']` was `http://10.13.37.13/` instead of `http://10.13.37.13`, which closed the door for the attack vector explained in the write-up. Before leaving you for maybe another year, I apologize to the team who uploaded `supershell_you_will_never_find_2.php5` for taking advantage of their hard work :/ It tasted sweet when validating the flag at that time, but it was a d\*ck move nonetheless.