《围观2017黑帽大会,剖析sqlite内存破坏漏洞》要点:
本文介绍了围观2017黑帽大会,剖析sqlite内存破坏漏洞,希望对您有用。如果有疑问,可以联系我们。
0x00 从黑帽年夜会说起
前不久,一年一度的黑帽大会于在美国拉斯维加斯举行,平安圈内各位大咖、全球顶级平安企业都使出浑身解数,展示他们最新的研究成果.相信大多数小伙伴都和小编一样,虽然有心想参会,但无奈囊中羞涩.
错过大会怎么办?没关系,我们还可以一起来学习观摩大牛的视频和文档.这次与大家一个国内平安厂商的议题《一石多鸟:利用单个sqlite漏洞破解多个软件》.
0x01 什么是内存破坏漏洞
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,并提供给开发者自行添加分词器的接口.
0x02 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",(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 <sqlite3Fts3InitTokenizer+312>eflags 0x10297 [ CF PF AF SF IF RF ]cs 0x33 51ss 0x2b 43ds 0x0 0[-------------------------------------code-------------------------------------]0x00005555555a2173 <+307>: mov %r12,%rsi0x00005555555a2176 <+310>: mov %ebx,%edi=> 0x00005555555a2178 <+312>: callq *0x8(%rax)0x00005555555a217b <+315>: test %eax,%eax0x00005555555a217d <+317>: mov %eax,%ebp0x00005555555a217f <+319>: jne 0x5555555a21d8 <sqlite3Fts3InitTokenizer+408>0x00005555555a2181 <+321>: mov (%rsp),%rax0x00005555555a2185 <+325>: mov 0x18(%rsp),%rdx0x00005555555a218a <+330>: mov (%rax),%rax0x00005555555a218d <+333>: mov %rdx,(%rax)0x00005555555a2190 <+336>: mov %r12,%rdi
rax 注册时提交的指针参数,cast将blob类型数据转换为指针,sqlite 完全没有对指针做任何有效性检查,直接进行了回调的挪用.其对应源代码位于 ext/fts3/fts3_tokenizer.c 的 sqlite3Fts3InitTokenizer 函数:
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,ppTok); assert( rc!=sqlITE_OK || *ppTok ); if( rc!=sqlITE_OK ){ sqlite3Fts3ErrMsg(pzErr,"unkNown tokenizer"); }else{
整理一下思路,整个攻击流程应该是这样的:
首先利用fts3_tokenizer的第一种方式,查询到sqlite基地址,注意结果是大端序.根据 select sqlite_version() 函数泄漏的版本信息调整偏移量,执行 PRAGMA soft_heap_limit 语句布置必要 call 的目标指令地址,向一个已知内存地址写入一个函数指针,然后这个地址转换为blob类型,作为fts3_tokenizer 函数的第二个参数,进而注册了一个“分词器”,最后通过创建虚拟表,触发 xCreate 回调函数,导致eip劫持,允许远程攻击者执行任意代码.
0x03亡羊补牢犹未晚
虽然这不是 sqlite 的漏洞,但滥用这一特性可能导致应用程序发生攻击面.
禁用这一特性可以起到缓解的效果.比如Andriod甚至是sqlite本身都在3.11版本就采用了直接禁用这种方式.
重写函数也不为是一个不错的方法,十分流行的WebKit也曾提供选择禁用Web sql Database作为本地数据库,它采用的语言就是sqlite,但已经被W3C标准移除了.现在WebKit也重写了函数.
但是只是简单的白名单过滤并不是一个优秀的处理方式,Safari浏览器采用sqlite3_set_authorizer()用来授权SQL语句行为,并通过白名单控制了可以执行的SQL语句,但CVE-2015-3659中明确说了如何绕过白名单,同样执行任意代码,或者导致拒绝服务,系统瓦解.
总结一下修复和处置的方式:
如果用不到全文检索,可通过封闭 sqlITE_ENABLE_FTS3 / sqlITE_ENABLE_FTS4 / sqlITE_ENABLE_FTS5 选项禁用之,或者使用 Amalgamation 版本编译;
如果必要使用 MATCH 检索,但不必要支持多国语言(即内置分词器可以满足要求),找到 ext/fts3/fts3.c 中注释掉如下一行代码关闭此函数:
&& sqlITE_OK==(rc = sqlite3Fts3InitHashtable(db,pHash,“fts3_tokenizer”))
使用 sqlite3 的 Authorization Callbacks 设置拜访控制
0x04参考链接
[1] 特性还是漏洞?滥用 sqlite 分词器
[2]《一石多鸟:利用单一的sqlITE 漏洞攻击年夜量软件》
[3] sqlite
[4] Offset2lib进击测试:看我如何全面绕过64位linux的内核防护
内存溢出PHP培训学院每天发布《围观2017黑帽大会,剖析sqlite内存破坏漏洞》等实战技能,PHP、MysqL、liNUX、APP、Js,CSS全面培养人才。
总结以上是内存溢出为你收集整理的围观2017黑帽大会,剖析SQLite内存破坏漏洞全部内容,希望文章能够帮你解决围观2017黑帽大会,剖析SQLite内存破坏漏洞所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)