Report on the trip from fuzzing the PHP interpreter, through getting code execution, to hijacking all incoming requests sent to a web server. Thoughts on torturing interpreters, tips and tricks for exploiting vulnerabilities in the PHP core and walk-through interesting bugs found (1e-65 days included).
4. The hardest part of this talk is...
source: http://blog.codinghorror.com/
...to tell you that I’m a PHP developer.
5. The tortures - master plan
source: http://oaklandacupunctureproject.com/wp-content/uploads/2013/12/its-easy.jpg
torture
identify bugs
exploit bugs
get profit
repeat
6. Minerva Fuzzer
fuzzer released at Month of PHP Security in 2010
dedicated to uncover bugs in PHP functions by generating valid
random scripts
written in Python (around 1000 loc)
friendly Beerware license
version from 2010 is available here:
http://php-security.org/downloads/minerva-1.0.tar.bz2
at some point I’m going to release new version
short paper about it:
http://php-security.org/2010/05/11/
mops-submission-05-the-minerva-php-fuzzer/index.html
7. Minerva - 5 years later
5 years ago I talked about it at local OWASP meeting (to be
specific 4 years and 362 days ago)
we released an exploit that was capable of hijacking all requests
that were sent to the Apache server
...this time we want to do it again but in the new reality (NX,
ASLR’n’stuff turned on by default)
we improved our fuzzing process a lot during the last few years
slides in Polish: http://www.slideshare.net/logicaltrust/
201105-owasp-fuzzing-interpretera-php
8. Minerva algorithm - the idea
1. script ← ””
2. X ← Initial set of variables with their types
3. G ← Fresh variable generator
4. F ← Function database
5. for i in 1..n:
5.1 f ← GET RANDOM(F, X)
5.2 v ← G()
5.3 script ← script . v . ” = ” . f call with random arguments from X
(but with proper types)
5.4 X ← X ∪ (v, f result type)
6. return script
Erghghg... what?
9. Minerva algorithm - the idea - example
F = {x = A(), x = B(x, x), y = C(x), y = D(x, y), ...}
X = ∅
x, y - simple types
1. v1 = A(); X = {x : {v1}}
2. v2 = B(v1, v1); X = {x : {v1, v2}}
3. v3 = C(v2); X = {x : {v1, v2}, y : {v3}}
4. v4 = D(v1, v3); X = {x : {v1, v2}, y : {v3, v4}}
5. v5 = A(); X = {x : {v1, v2, v5}, y : {v3, v4}}
6. ...repeat it until crash
10. Minerva algorithm - the idea - example
F = {x = A(), x = B(x, x), y = C(x), y = D(x, y), ...}
X = ∅
x, y - simple types
1. v1 = A(); X = {x : {v1}}
2. v2 = B(v1, v1); X = {x : {v1, v2}}
3. v3 = C(v2); X = {x : {v1, v2}, y : {v3}}
4. v4 = D(v1, v3); X = {x : {v1, v2}, y : {v3, v4}}
5. v5 = A(); X = {x : {v1, v2, v5}, y : {v3, v4}}
6. ...repeat it until crash
11. Minerva algorithm - the idea - example
F = {x = A(), x = B(x, x), y = C(x), y = D(x, y), ...}
X = ∅
x, y - simple types
1. v1 = A(); X = {x : {v1}}
2. v2 = B(v1, v1); X = {x : {v1, v2}}
3. v3 = C(v2); X = {x : {v1, v2}, y : {v3}}
4. v4 = D(v1, v3); X = {x : {v1, v2}, y : {v3, v4}}
5. v5 = A(); X = {x : {v1, v2, v5}, y : {v3, v4}}
6. ...repeat it until crash
12. Minerva algorithm - the idea - example
F = {x = A(), x = B(x, x), y = C(x), y = D(x, y), ...}
X = ∅
x, y - simple types
1. v1 = A(); X = {x : {v1}}
2. v2 = B(v1, v1); X = {x : {v1, v2}}
3. v3 = C(v2); X = {x : {v1, v2}, y : {v3}}
4. v4 = D(v1, v3); X = {x : {v1, v2}, y : {v3, v4}}
5. v5 = A(); X = {x : {v1, v2, v5}, y : {v3, v4}}
6. ...repeat it until crash
13. Minerva algorithm - the idea - example
F = {x = A(), x = B(x, x), y = C(x), y = D(x, y), ...}
X = ∅
x, y - simple types
1. v1 = A(); X = {x : {v1}}
2. v2 = B(v1, v1); X = {x : {v1, v2}}
3. v3 = C(v2); X = {x : {v1, v2}, y : {v3}}
4. v4 = D(v1, v3); X = {x : {v1, v2}, y : {v3, v4}}
5. v5 = A(); X = {x : {v1, v2, v5}, y : {v3, v4}}
6. ...repeat it until crash
14. Minerva algorithm - the idea - example
F = {x = A(), x = B(x, x), y = C(x), y = D(x, y), ...}
X = ∅
x, y - simple types
1. v1 = A(); X = {x : {v1}}
2. v2 = B(v1, v1); X = {x : {v1, v2}}
3. v3 = C(v2); X = {x : {v1, v2}, y : {v3}}
4. v4 = D(v1, v3); X = {x : {v1, v2}, y : {v3, v4}}
5. v5 = A(); X = {x : {v1, v2, v5}, y : {v3, v4}}
6. ...repeat it until crash
15. Minerva algorithm - the idea - example
F = {x = A(), x = B(x, x), y = C(x), y = D(x, y), ...}
X = ∅
x, y - simple types
1. v1 = A(); X = {x : {v1}}
2. v2 = B(v1, v1); X = {x : {v1, v2}}
3. v3 = C(v2); X = {x : {v1, v2}, y : {v3}}
4. v4 = D(v1, v3); X = {x : {v1, v2}, y : {v3, v4}}
5. v5 = A(); X = {x : {v1, v2, v5}, y : {v3, v4}}
6. ...repeat it until crash
20. Our approach:
source: http://www.jtpedals.com
we use something(tm) to cluster crashes
we use Jenkins to automate things
more on our thoughts about fuzzing:
http://www.slideshare.net/slajdszer/fuzzing-challenges-alligatorcon
21. Tips & tricks
start with small scripts (crashes generated by large ones are likely
unreproducible)
do not stress SSD drives
you want to use Address Sanitizer (or other sanitizers)
USE ZEND ALLOC = 0 - use libc allocator instead of internal
one
learn to automate (dedup crashes etc.)
timelimit(1) is very useful!
22. Tips & tricks
source: http://pearlsofpromiseministries.com
OpenGrok - http://lxr.php.net/
HHVM has bug bounty run by Facebook
https://github.com/facebook/hhvm
PHP bugs are awarded by IBB bug bounty
https://hackerone.com/ibb-php
23. The results - PHP 7.x - (HEAD)
source: http://images.phpgang.com
one machine: 8 cores + 16 GB ram + SSD
5 days + 8 threads = around 4 millions executions
cost: arount 30 PLN = 7.5 USD
10-50 lines of code generated per test case
around 4150 crashes (55 were unique):
1. unknown crash - 24
2. segmentation fault - 19
3. heap use after free - 6
4. heap buffer overflow - 4
5. stack buffer overflow - 1
6. double free - 1
24. The results - HHVM (HEAD)
source: http://www.clipartbest.com
one machine: 8 cores + 16 GB ram + SSD
5 days + 4 threads = around 800 thousands executions
cost: arount 30 PLN = 7.5 USD
10-50 lines of code generated per test case
around 956 crashes (63 were unique):
1. unknown crash - 33
2. segmentation fault - 19
3. heap-use-after-free - 6
4. heap-buffer-overflow - 5
29. Exploiting bugs - PHP & HEAP related problems
heap overflows
use-after-free - unserialize() - CVE-2015-0273
double frees - imap open() - CVE-2010-4150
5.x era allocator description - http://php-security.org/2010/05/
07/mops-submission-03-sqlite˙single˙query-sqlite˙array˙
query-uninitialized-memory-usage/index.html#˙˙exploitation
FWIW, allocator is LIFO queue
usually scenario is pretty much the same: take control over
zval/array memory guts
30. Exploiting bugs - PHP & other bugs
similary like in the other software
uninitialized memory access - sqlite array query() - http://
php-security.org/2010/05/07/mops-submission-03-sqlite˙single˙
query-sqlite˙array˙query-uninitialized-memory-usage/index.html
heap is your friend (in a non-debug builds)
everything with a dtor func t is your friend e.g.:
176 struct _zend_array {
177 zend_refcounted_h gc;
[...]
195 dtor_func_t pDestructor;
196 };
http://lxr.php.net/xref/PHP˙7˙0/Zend/zend˙types.h#195
31. Example - openssl seal()
in 2011 we hijacked all connections to the webserver using buffer
overflow in socket connect() -
http://seclists.org/fulldisclosure/2011/May/472
minerva found uninitialized memory usage in openssl seal(). How
hard would it be to do the same in 2016?
we assume to operate on Ubuntu 14.04 LTS with Apache 2.4.7
and PHP 7.0.2 (compiled manually as most distros still use 5.x
branch).
32. openssl seal() - the bug - 1/2
4888 /* {{{ proto int openssl_seal(string data, &string sealdata, &
4889 Seals data */
4890 PHP_FUNCTION(openssl_seal)
4891 {
4892 zval *pubkeys, *pubkey, *sealdata, *ekeys, *iv = NULL;
[...]
4935 pkeys = safe_emalloc(nkeys, sizeof(*pkeys), 0);
[...]
4942 /* get the public keys we are using to seal this data */
4943 i = 0;
4944 ZEND_HASH_FOREACH_VAL(pubkeysht, pubkey) {
4945 pkeys[i] = php_openssl_evp_from_zval(pubkey, 1, NULL,
0, &key_resources[i]);
4946 if (pkeys[i] == NULL) {
4949 goto clean_exit;
http://lxr.php.net/xref/PHP˙7˙0/ext/openssl/openssl.c
33. openssl seal() - the bug - 2/2
[...]
5000 clean_exit:
5001 for (i=0; i<nkeys; i++) {
5002 if (key_resources[i] == NULL) {
5003 EVP_PKEY_free(pkeys[i]);
[...]
http://lxr.php.net/xref/PHP˙7˙0/ext/openssl/openssl.c
34. openssl seal() - is it exploitable?
376 void EVP_PKEY_free(EVP_PKEY *x)
377 {
[...]
380 if (x == NULL)
381 return;
383 i = CRYPTO_add(&x->references, -1, CRYPTO_LOCK_EVP_PKEY);
387 if (i > 0)
388 return;
395 EVP_PKEY_free_it(x);
[...]
401 static void EVP_PKEY_free_it(EVP_PKEY *x)
402 {
403 if (x->ameth && x->ameth->pkey_free) {
404 x->ameth->pkey_free(x);
[...]
35. openssl seal() - our plan
1. Stage 1 (pwning PHP)
1.1 control uninitialized memory
1.2 get (or guess) pointer that will act as a fake EVP PKEY structure
1.3 push that pointer as a value to EVP PKEY free()
1.4 basing on guesses (or leaks) build a ROP chain allowing us to
execute data
1.5 execute the 2nd stage shellcode
2. Stage 2 (pwning Apache)
2.1 guess/find handlers addresses
2.2 overwrite first handler with ours evil one
2.3 get back home (do not crash apache child)
41. openssl seal() - ROP
we use ROP technique to neutralise NX
we ended up using gadgets from the PHP binary
to pivot the stack we used the address of our controlled buffer,
which luckily was on the stack
then we call mprotect() and set RWX perms
exploit code is here: http://akat1.pl/?id=1
44. openssl seal() - hijacking apache2 requests
source: http://linuxconfig.net
Here’s what we want to do:
1. register memory that will survive subsequent requests
2. copy Apache handler code to the registered memory
3. register request handler that will be run really first
4. do something to clean the corrupted state and let Apache child
process happily serve subsequent requests
50. Why should I care?
source: http://www.badideatshirts.com/
apache2 + mod php is a quite popular configuration (more than
650 thousands servers according to shodan.io search)
this attack vector can be used to bypass disabled functoins
(easier methods exists, it’s just another one)
running buggy software is risky
there are other bugs...
51. What can I do?
source: http://cdn.quotesgram.com/
keep your software up2date
unload unnecessary extensions
do not rely on disabled functions
do not rely on open basedir
do not run PHP as mod php
do not trust your software
52. Future work
port Minerva to any language → Minerva$lang
code coverage improvement
for now we ignore the fact that PHP is object-oriented language
generate language constructs
variables mutation
test case minimization
use code coverage as input to fuzzer (like in AFL or autodafe)
implement type casts
implement mocks for some backends
...your ideas.
53. Credits
Large parts of this presentation were done in cooperation with
Marek Kroemeke and Filip Palian, THANKS!