Last weekend Bitform, of exploit monday fame, setup a team of a few guys to poke around at the CSAW CTF qualification challenges. Although he and the other guys carried almost all of the workload, I did mess around with the web challenges. I found the web challenges a lot more challenging than I thought they were going to be, but in the end they were pretty straightforward.
There are a few write-ups already for the 500 point challenge, but I haven't seen any decent ones of the 400 point web challenge: Inchbinge. The challenge started by just browsing to the URL: http://csawctf.poly.edu:40003/, which presented you with a login page. The challenge description didn't provide credentials so I wrongly assumed that this was going to be SQLi-related. Strangely enough, there were almost no database related exploitation in any of the challenges.
After trying a few different usernames with common passwords, it was obvious that there was a disclosure problem related to the error message. A valid username with an invalid password returned a message of "Password incorrect," while an invalid username returned "Account not found." This could be used to easily guess a valid user (admin) and eventually a valid password (password).
"Account not found"
"Password incorrect"
After logging in with admin/password, you were presented with a message informing you that you weren't "really" logged in as admin. There were two new items that weren't visible without logging in. One was a link to a changepass.php and the other was the author of the website at the bottom.
Since we knew that we weren't logged in as an actual administrator we wanted to see if the author's name was the admin account. After logging out, we verified that it was indeed another valid username.
Next, we took a look at changepass.php. It was simply a script for changing the currently logged-in user's password.
Viewing the source of the page revealed that the simple post submission included a hidden "user" field.
By catching the post with an in-line proxy (in this case the Firefox addon Tamperdata), we can edit the parameters of the post submission.
Armed with the valid username "TheTallGuy," we changed the submission in tamperdata.
The post was accepted.
Now we logged in with the "TheTallGuy" account and the password which we just set.
I honestly believed that this was going to be the end of the challenge, but I was wrong. After logging in, you were presented with a different message: "IMPORTANT: Fix dl script (broken/data/download.php)." Naturally, we immediately browsed there to see the script that needed to be "fixed."
There wasn't much to see, but I did want to see what else was in the directory structure so I browsed to the /broken directory. Directory browsing was enabled so it allowed us to see the other directories such as /data and /data/protected. There was even an image file of a popular anime character in the /broken directory, but the /data/protected appeared to be empty.
At first glance, the download.php script appeared to be a LFI vulnerability. Local file include vulnerabilities allow an attacker to download or view files that shouldn't be accessed through a web interface. On a linux box (image above shows that we are likely looking at Apache installed on an Ubuntu server) those files could include /etc/passwd or other sensitive config files. With Apache, we are most likely looking for the ".htaccess" file, which is normally in the root of the site. In order to find out what we can download with the script, we started by trying the "changepass.php" script.
The script could not find ../../changepass.php because some logic within the script changed the "../../" to just "../" thereby thwarting the directory traversal attempt. However, we could download the ".png" image that we found through directory browsing.
We could also download the download.php script itself. We can clearly see the logic which was preventing our directory traversal attempts.
<script start>
<?php
#FIXME: People might be able to download this file!
function cleanit($v) {
$v=str_replace("\0", "", $v);
$o=$v;
do {
$v=preg_replace("|/\.*/|", "/", $v);
$v=preg_replace("|^/|", "", $v);
} while($o!=$v && $o=$v);
return $v;
}
$path=cleanit($_GET['file']);
if(!strlen($path) || !file_exists($path)) {
print 'FILE NOT FOUND: '.$path; exit;
} elseif(strpos($path,'protected')===0) {
print 'ACCESS DENIED: '.$path; exit;
}
readfile($path);
?>
#FIXME: People might be able to download this file!
function cleanit($v) {
$v=str_replace("\0", "", $v);
$o=$v;
do {
$v=preg_replace("|/\.*/|", "/", $v);
$v=preg_replace("|^/|", "", $v);
} while($o!=$v && $o=$v);
return $v;
}
$path=cleanit($_GET['file']);
if(!strlen($path) || !file_exists($path)) {
print 'FILE NOT FOUND: '.$path; exit;
} elseif(strpos($path,'protected')===0) {
print 'ACCESS DENIED: '.$path; exit;
}
readfile($path);
?>
<script end>
Unfortunately for me, it wasn't that clear cut. After a bit of googling, I came across this blog post: http://www.securitypitfalls.org/2011/03/sometimes-security-function-just-does.html. I was extremely focused on bypassing the two preg_replace lines and directory traversing to the root of the website. Fortunately, Bitform tried downloading ".htaccess" from the protected directory and ended the madness.
http://csawctf.poly.edu:40003/broken/data/download.php?file=../data/protected/.htaccess
Viewing the source revealed a file called "XDONOTOPENX" which contained "Key: LOOKMANONUMBERS" and finally the end of this challenge.
The challenge covered several interesting web security topics including information disclosure vulnerabilities, account/password problems, server-side vs. client-side checks, directory browsing, local file inclusion vulnerabilities and I added a little on directory traversal. It also served to remind me to keep an open-mind when pentesting web applications, especially ones that are created for a CTF!
-Chris