Talos Vulnerability Report TALOS-2019-0777 Sqlite3 Window Function Remote Code Execution Vulnerability May 9, 2019 CVE Number CVE-2019-5018 Summary An exploitable use after free vulnerability exists in the window function functionality of Sqlite3 3.26.0. A specially crafted SQL command can cause a use after free vulnerability, potentially resulting in remote code execution. An attacker can send a malicious SQL command to trigger this vulnerability. Tested Versions SQLite 3.26.0, 3.27.0 Product URLs https://sqlite.org/download.html CVSSv3 Score 8.1 - CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H CWE CWE-416: Use After Free Details SQLite is a popular library implementing a SQL database engine. It is used extensively in mobile devices, browsers, hardware devices, and user applications. It is a frequent choice for a small, fast, and reliable database solution. SQLite implements the Window Functions feature of SQL which allows queries over a subset, or "window", of rows. After parsing a SELECT statement that contains a window function, the SELECT statement is transformed using the sqlite3WindowRewrite function. src/select.c:5643 sqlite3SelectPrep(pParse, p, 0); ... #ifndef SQLITE_OMIT_WINDOWFUNC if( sqlite3WindowRewrite(pParse, p) ){ goto select_end; } During this function, the expression-list held by the SELECT object is rewritten if an aggregate function (COUNT, MAX, MIN, AVG, SUM) was used [0]. src/window.c:747 int sqlite3WindowRewrite(Parse *pParse, Select *p){ int rc = SQLITE_OK; if( p->pWin && p->pPrior==0 ){ ... Window *pMWin = p->pWin; /* Master window object */ Window *pWin; /* Window object iterator */ ... selectWindowRewriteEList(pParse, pMWin /* window */, pSrc, p->pEList, &pSublist); [0] selectWindowRewriteEList(pParse, pMWin /* window */, pSrc, p->pOrderBy, &pSublist); ... pSublist = exprListAppendList(pParse, pSublist, pMWin->pPartition); The master window object pMWin is taken from the SELECT object and is used during the rewrite [1]. This walks the expression list from the SELECT object and rewrites the window function(s) for easier processing. src/window.c:692 static void selectWindowRewriteEList( Parse *pParse, Window *pWin, SrcList *pSrc, ExprList *pEList, ExprList **ppSub ){ Walker sWalker; WindowRewrite sRewrite; memset(&sWalker, 0, sizeof(Walker)); memset(&sRewrite, 0, sizeof(WindowRewrite)); sRewrite.pSub = *ppSub; sRewrite.pWin = pWin; // [1] sRewrite.pSrc = pSrc; sWalker.pParse = pParse; sWalker.xExprCallback = selectWindowRewriteExprCb; sWalker.xSelectCallback = selectWindowRewriteSelectCb; sWalker.u.pRewrite = &sRewrite; (void)sqlite3WalkExprList(&sWalker, pEList); *ppSub = sRewrite.pSub; } Note the master window object is used in the WindowRewrite object. While processing each expression, the xExprCallback function is used as a callback for processing. When processing an aggregate function (TKAGGFUNCTION) and after appending to the expression list, the expression is deleted [2]. src/window.c:602 static int selectWindowRewriteExprCb(Walker *pWalker, Expr *pExpr){ struct WindowRewrite *p = pWalker->u.pRewrite; Parse *pParse = pWalker->pParse; ... switch( pExpr->op ){ ... /* Fall through. */ case TK_AGG_FUNCTION: case TK_COLUMN: { Expr *pDup = sqlite3ExprDup(pParse->db, pExpr, 0); p->pSub = sqlite3ExprListAppend(pParse, p->pSub, pDup); if( p->pSub ){ assert( ExprHasProperty(pExpr, EP_Static)==0 ); ExprSetProperty(pExpr, EP_Static); sqlite3ExprDelete(pParse->db, pExpr); [2] ExprClearProperty(pExpr, EP_Static); memset(pExpr, 0, sizeof(Expr)); pExpr->op = TK_COLUMN; pExpr->iColumn = p->pSub->nExpr-1; pExpr->iTable = p->pWin->iEphCsr; } ... } During the deletion of the expression, if the expression is marked as a Window Function, the associated Window object is deleted as well. src/window.c:1051 static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){ ... if( !ExprHasProperty(p, (EP_TokenOnly|EP_Leaf)) ){ ... if( ExprHasProperty(p, EP_WinFunc) ){ assert( p->op==TK_FUNCTION ); sqlite3WindowDelete(db, p->y.pWin); } } During the deletion of the Window, the assocated partition for the Window is deleted. src/window.c:851 void sqlite3WindowDelete(sqlite3 *db, Window *p){ if( p ){ sqlite3ExprDelete(db, p->pFilter); sqlite3ExprListDelete(db, p->pPartition); sqlite3ExprListDelete(db, p->pOrderBy); sqlite3ExprDelete(db, p->pEnd); sqlite3ExprDelete(db, p->pStart); sqlite3DbFree(db, p->zName); sqlite3DbFree(db, p); } } Looking back at the original sqlite3WindowRewrite function, this deleted partition is reused after the rewrite of the expression list [4]. src/window.c:785 selectWindowRewriteEList(pParse, pMWin, pSrc, p->pEList, &pSublist); [4] selectWindowRewriteEList(pParse, pMWin, pSrc, p->pOrderBy, &pSublist); pMWin->nBufferCol = (pSublist ? pSublist->nExpr : 0); ... pSublist = exprListAppendList(pParse, pSublist, pMWin->pPartition); [5] src/window.c:723 static ExprList *exprListAppendList( Parse *pParse, ExprList *pList, ExprList *pAppend [5] ){ if( pAppend ){ int i; int nInit = pList ? pList->nExpr : 0; for(i=0; inExpr; i++){ Expr *pDup = sqlite3ExprDup(pParse->db, pAppend->a[i].pExpr, 0); pList = sqlite3ExprListAppend(pParse, pList, pDup); if( pList ) pList->a[nInit+i].sortOrder = pAppend->a[i].sortOrder; } } return pList; } After this partition is deleted, it is then reused in exprListAppendList [5], causing a use after free vulnerability, resulting in a denial of service. If an attacker can control this memory after the free, there is an opportunity to corrupt more data, potentially leading to code execution. Crash Information Using the debug version of sqlite3 to trash contents of freed buffer helps demonstrate this vulnerability [5]. Watching for a crash around 0xfafafafafafafafa would mean a freed buffer is being accessed again. src/malloc.c:341 void sqlite3DbFreeNN(sqlite3 *db, void *p){ assert( db==0 || sqlite3_mutex_held(db->mutex) ); assert( p!=0 ); if( db ){ ... if( isLookaside(db, p) ){ LookasideSlot *pBuf = (LookasideSlot*)p; /* Trash all content in the buffer being freed */ memset(p, 0xfa, db->lookaside.sz); [5] pBuf->pNext = db->lookaside.pFree; db->lookaside.pFree = pBuf; return; } Running this slight modification through gdb sqlite3 with the proof of concept: [─────────────────────REGISTERS──────────────────────] *RAX 0xfafafafafafafafa RBX 0x0 *RCX 0x7fffffd0 RDX 0x0 *RDI 0x7fffffffc3a0 —▸ 0x7ffff79c7340 (funlockfile) ◂— mov rdx, qword ptr [rdi + 0x88] RSI 0x0 R8 0x0 *R9 0x30 R10 0x0 *R11 0x246 *R12 0x401a20 (_start) ◂— xor ebp, ebp *R13 0x7fffffffe000 ◂— 0x2 R14 0x0 R15 0x0 *RBP 0x7fffffffc900 —▸ 0x7fffffffc990 —▸ 0x7fffffffcc10 —▸ 0x7fffffffce90 ◂— ... *RSP 0x7fffffffc8d0 —▸ 0x4db4f5 (selectWindowRewriteSelectCb) ◂— push rbp *RIP 0x4db723 (exprListAppendList+240) ◂— mov eax, dword ptr [rax] [───────────────────────DISASM───────────────────────] ► 0x4db723 mov eax, dword ptr [rax] 0x4db725 cmp eax, dword ptr [rbp - 0x10] 0x4db728 jg exprListAppendList+94 <0x4db691> ↓ 0x4db691 mov rax, qword ptr [rbp - 0x28] 0x4db695 mov edx, dword ptr [rbp - 0x10] 0x4db698 movsxd rdx, edx 0x4db69b shl rdx, 5 0x4db69f add rax, rdx 0x4db6a2 add rax, 8 0x4db6a6 mov rcx, qword ptr [rax] 0x4db6a9 mov rax, qword ptr [rbp - 0x18] [───────────────────────SOURCE───────────────────────] 145380 ){ 145381 if( pAppend ){ 145382 int i; 145383 int nInit = pList ? pList->nExpr : 0; 145384 printf("pAppend: [%p] -> %p\n", &pAppend, pAppend); 145385 for(i=0; inExpr; i++){ // BUG-USE 0 145386 Expr *pDup = sqlite3ExprDup(pParse->db, pAppend->a[i].pExpr, 0); 145387 pList = sqlite3ExprListAppend(pParse, pList, pDup); 145388 if( pList ) pList->a[nInit+i].sortOrder = pAppend->a[i].sortOrder; 145389 } [───────────────────────STACK────────────────────────] 00:0000│ rsp 0x7fffffffc8d0 —▸ 0x4db4f5 (selectWindowRewriteSelectCb) ◂— push rbp 01:0008│ 0x7fffffffc8d8 ◂— 0xfafafafafafafafa 02:0010│ 0x7fffffffc8e0 —▸ 0x746d58 ◂— 0x1 03:0018│ 0x7fffffffc8e8 —▸ 0x7fffffffdb30 —▸ 0x73b348 —▸ 0x736c60 (aVfs.13750) ◂— ... 04:0020│ 0x7fffffffc8f0 ◂— 0x100000000 05:0028│ 0x7fffffffc8f8 ◂— 0xce1ae95b8dd44700 06:0030│ rbp 0x7fffffffc900 —▸ 0x7fffffffc990 —▸ 0x7fffffffcc10 —▸ 0x7fffffffce90 ◂— ... 07:0038│ 0x7fffffffc908 —▸ 0x4db994 (sqlite3WindowRewrite+608) ◂— mov qword ptr [rbp - 0x68], rax [─────────────────────BACKTRACE──────────────────────] ► f 0 4db723 exprListAppendList+240 f 1 4db994 sqlite3WindowRewrite+608 Exploit Proof of Concept Run the proof of concept with the sqlite3 shell: ./sqlite3 -init poc Timeline 2019-02-05 - Vendor Disclosure 2019-03-07 - 30 day follow up with vendor; awaiting moderator approval 2019-03-28 - Vendor patched 2019-05-09 - Public Release Credit Discovered by Cory Duplantis of Cisco Talos.