Use-After-FLEE was a web challenge allowing you to upload and run arbitrary PHP scripts.
Uploading a script that does phpinfo()
, we see that the PHP script is
running with and open_basedir
of /var/www/html/:/tmp/
as well as the
following functinos in disable_functions
:
exec, passthru, shell_exec, system, proc_open, popen, curl_exec,
curl_multi_exec, parse_ini_file, symlink, chgrp, chmod, chown, dl, mail,
imap_mail, apache_child_terminate, posix_kill, proc_terminate,
proc_get_status, syslog, openlog, ini_alter, ini_set, ini_restore,
putenv, apache_setenv, pcntl_alarm, pcntl_fork, pcntl_waitpid,
pcntl_wait, pcntl_wtermsig, pcntl_wstopsig, pcntl_signal,
pcntl_signal_dispatch, pcntl_sigtimedwait, pcntl_sigprocmask,
pcntl_sigwaitinfo, pcntl_exec, pcntl_setpriority, link, readlink
This list looks pretty reasonable, so based on the title of the problem,
we searched for publicly known use-after-free bugs in the running
version of PHP (5.5.9-1ubuntu4.12
). It turned out the server is
vulnerable to
CVE-2015-6834.
Luckily, the service is using standard Ubuntu packages, so we can develop on an environment with the exact same php/libc versions.
As described in Taoguang's writeup, the bug is a use-after-free in
unserializing SplDoublyLinkedList
objects. PHP's object serialization
allows a value to reference a previously unserialized object (see
here
for some more details). When an deserialized object's __wakeup
method
is called, it can free one of its member objects by reassigning the
member. With this bug, when the member is a SplDoublyLinkedList
, the
object is freed, but references to its elements are still accessible
from the deserialized object. Taoguang's PoC does this, then overlaps a
string with the freed object so that fake PHP objects (zvals) can be
constructed.
Here's an approximate descripton of what is going on in his PoC:
class obj {
var $ryat;
function __wakeup() {
$this->ryat = 1;
}
}
$inner = 'i:1234;:i:1;';
$exploit = 'a:5:{';
$exploit .= 'i:0;i:1;';
# SplDoublyLinkedList object
$exploit .= 'i:1;C:19:"SplDoublyLinkedList":'.strlen($inner).':{'.$inner.'}';
# obj that references the SplDoublyLinkedList. When __wakeup is called,
# the SplDoublyLinkedList is freed.
$exploit .= 'i:2;O:3:"obj":1:{s:4:"ryat";R:3;}';
# references element in SplDoublyLinkedList
$exploit .= 'i:3;a:1:{i:0;R:5;}';
# overlaps the freed element
$exploit .= 'i:4;s:'.strlen($fakezval).':"'.$fakezval.'";';
$exploit .= '}';
The techniques for exploiting this are mostly taken from Stefan Esser's SyScan 2012 talk.
At this point, we can construct arbitrary zval
s. In PHP 5.5.9, the relevant structures are:
struct _zval_struct {
zvalue_value value;
zend_uint refcount__gc;
zend_uchar type;
zend_uchar is_ref__gc;
};
// zval types
#define IS_LONG 1
#define IS_OBJECT 5
#define IS_STRING 6
typedef union _zvalue_value {
long lval; // type = IS_LONG
...
struct {
char *val;
int len;
} str; // type = IS_STRING
...
zend_object_value obj; // type = IS_OBJECT
} zvalue_value;
typedef struct _zend_object_value {
zend_object_handle handle;
const zend_object_handlers *handlers; // a table of function pointers
} zend_object_value;
Since we can construct arbitrary zval
s (_zval_struct
), the plan will
be to leak addresses, then construct a zval
of type IS_OBJECT
whose
handler function table points to memory that we control.
We can already read arbitrary memory by making a string with any val
and len
of our choosing. Unfortunately, the apache2 process is PIE, so
there are no known addresses to leak from. Stefan's slides give a nice
solution to this: We can construct a long, then free it using the same
__wakeup
trick. PHP's memory cache will then write a heap address over
lval
, leaving the type
field intact. We can then read the long to
get a heap address.
Now that we have a heap address, we scan through it for libphp5
addresses
using the method suggested in the slides. We spray a bunch of objects with, and
scan the heap looking for a pattern that looks like a handlers
address
followed by a refcount of 1 and a type of IS_OBJECT
. The handlers
for the
object should live in libphp5
's data, and from there, we can compute
libphp5
's base address. We can then learn libc
addresses using GOT entries
in libphp5
.
Now that we have the address of system
, we spray system
on the heap,
and scan through to find the location of one of them, system_addr
.
We'll use system_addr - 8
as the handlers
table for our fake object
so that system(fake_zval)
will be executed when del_ref
is called on
the object. This gives 8 bytes (the handle
field) to place a shell
command. Luckily, we are given write access to /tmp
, so we can write a
command in /tmp/a
and run it with exactly 8 bytes: sh /*/a;
.
See exploit.php for the full exploit.
Flag: hitcon{beapentester,itisnecessarytolearnmemory-basedattack}