SQLite是天下上利用最多的数据库之一。然而,关于其安全方面的研究,都只涉及WebSQL和浏览器开拓方面。我们相信这只是SQLite安全的冰山一角。
在对SQLite安全性的长期研究中,我们考试测验在任何环境下都只依赖SQL语句,对SQLite进行内存破坏攻击。通过自创的查询挟制(Query Hijacking)和面向查询的编程(Query Oriented Programming),我们确实可在SQLite引擎中发起内存破坏攻击。我们将在几个实际场景中进行演示做事器上的密码盗取器,以及利用更高的权限持久掌握iOS。
我们希望通过发布我们的研究进行抛砖引玉,引起更多人对SQLite的研究。特殊是考虑到SQLite早已经内置到环球大部分操作系统、桌面或移动设备中,研究前景不可估量。此外,以下所涉及的查询语句并不仅限于SQLite,也适用于其他SQL引擎。欢迎大家一起利用熟习的构造化查询措辞进行开拓、利用、攻击。
动机
这项研究始于我和omriher创造的一些臭名昭著的密码盗取器的源代码。虽然环球存在很多密码盗取器(比如Azorult、Loki Bot和Pony),但他们的行为办法基本相同:
传染受害者打算机后,恶意软件要么在用户输入凭据时捕获数据,要么主动网络其他软件存储在打算机上的所有凭据。
而将凭据存储在SQLite中是非常常见的。
恶意软件在网络SQLite存储文件后,将它们发送到攻击者做事器中,利用PHP解析它们,终极得到帐户名和密码。
通过研究这些密码盗取器,我们开始更进一步的推测。
我们能否利用数据库查询来攻击数据库?
如果能行,考虑到SQLite的支配范围,这无疑会造成巨大的影响。就让我们开始吧!
你很可能正在利用SQLite但却毫无察觉。
引用作者的话:
SQLite是一个用C措辞库实现的小型、快速、高可靠性、功能完好的SQL数据库引擎。SQLite同时也是天下上利用最多的数据库引擎。SQLite内置在所有手机和大多数打算机中,并和人们每天利用的无数运用进行合营。
与大多数其他SQL数据库不同,SQLite没有单独的做事进程。SQLite直接读写磁盘文件,包含多个表、索引、触发器和视图的完全SQL数据库常日位于单个磁盘文件中。
攻击点
下面的代码片段是密码盗取器的一个通用示例。
基于我们已掌握了数据库这一背景,因此可将攻击点分为两方面:数据库的加载和初步解析,以及实行SELECT查询。
sqlite3_open完成的初始加载实际上涉及的点非常有限;它基本上便是指用于打开数据库的大量设置和配置代码。我们的紧张关注点是header解析,基本是用AFL(American Fuzzy Lop)进行模糊测试。
另一方面,当我们开始查询数据库时,事情变得更加有趣。
用SQLite作者的话:
SELECT语句是SQL措辞中最繁芜的命令。
只管我们无法掌握查询流程本身(由于它是硬编码在目标上),但是仔细研究SELECT流程对我们的渗透测试是有益的。
由于SQLite3是一个虚拟机,以是每个SQL语句必须首先利用sqlite3_prepare例程将其编译成字节码程序。此外,prepare函数会遍历并扩展所有SELECT子查询。这个过程一部分是验证所有干系工具(如表或视图)是否确实存在,并将它们在主模式(master schema)中进行定位。
sqlite_master和DDL每个SQLite数据库都有一个sqlite_master表,它定义了数据库及其所有工具(如表、视图、索引等)的模式。
sqlite_master表的定义语句为:
我们特殊感兴趣的部分是sql这一列。
这个字段是用来描述工具的DDL(数据定义措辞)。
在某种意义上,DDL命令类似于C语句中的头文件。DDL命令用于定义数据库中数据容器的构造、名称和类型,就像头文件常日定义类型、构造、类和其他数据构造一样。
如果我们查看数据库文件,会创造这些DDL语句实际上以明文存储:
在查询的准备过程中,sqlite3LocateTable()会试图找到我们感兴趣的表的内存构造。
sqlite3LocateTable()会读取sqlite_master表中可用的模式,如果是第一次这样做,它还会对每个结果进行回调,以验证DDL语句是否有效,并构建必要的内部数据构造来描述所谈论的工具。
DDL Patching理解以上这个准备过程后,有一个问题涌现了,我们是否大略地更换文件中以纯文本形式涌现的DDL?如果可以,我们就能将自己定义的SQL语句注入到文件中,造成一些毁坏。
根据上面的代码片段,我们创造DDL语句彷佛必须以“create”开头。
考虑到这个限定,以及查阅SQLite文档,我们创造这些可能是我们能创建的工具:
CREATE VIEW命令给了我们启示。大略地说,视图(VIEW)便是预打包的SELECT语句。如果我们用兼容的视图更换目标软件的目标表,就会有神奇的事发生。
挟制任何查询
设想以了局景:
原数据库有一个名为dummy的表,定义为:
目标软件利用的查询语句如下:
如果我们制作一个dummy视图,则可以挟制这个查询:
这个“陷阱”视图使我们能够挟制查询——这意味着我们天生一个完备由我们掌握的查询功能。
以上这个挟制极大扩展了我们的攻击面,我们可以考试测验修改DDL,与SQLite阐明器进行交互。
既然现在我们可以与SQLite阐明器进行交互,那接下来的问题便是SQLite内置哪些开拓措辞?能否实行系统命令,并对文件系统进行读取或写入?
由于我们并不是第一个从开拓的角度研究SQLite的人,因此让我们先回顾一下前辈的成果。
SQL注入SQLite到底支持系统命令吗?我们可以加载任意库吗?
目前看起来,最大略的操控文件的方法如下:
我们选定一个新数据库,创建一个表并插入一行文本。然后,这个新数据库就成了一个包含webshell的新文件(SQLite中数据库是一个文件)。
PHP阐明器的容错性非常高,能顺利捕获到PHP内容的开始标记<?。
但是,正如前面所述,DDL不能以“ATTACH”开头。
另一个值得把稳的是load_extension函数。虽然这个函数该当许可我们加载任意的共享工具,但默认情形下它是被禁用的。
SQLite中的内存破坏与用C措辞编写的任何其他软件一样,在评估SQLite的安全性时,内存安全绝对是评估的重点。
在Michał Zalewski的博客中,详细描述他是如何利用AFL对被SQLite进行一些fuzz,并得到了令人印象深刻的结果:短短30分钟内得到了22个漏洞。
有趣的是,SQLite已经将AFL作为其安全测试套件的一部分。
以上内存破坏漏洞都得到了修复。而从攻击者的角度来看,如果没有一个得当的框架,这些漏洞很难被利用。
当代一些通用的防御方法在履行内存破坏攻击方面造成了极大障碍,攻击者须要找到另一个更灵巧的方法。
Web SQLWeb SQL Database是一个用于往数据库中存储数据的Web页面API,它可以通过javascript利用变种SQL语句查询数据库中的数据。W3C Web运用事情组在2010年11月停滞了对该技能规范的磋商,情由是除了SQLite之外独立运用较少。
目前,谷歌Chrome、Opera和Safari仍旧支持该API。
它们都利用SQLite数据库作为这个API的后端支持。
在一些最盛行的浏览器中,任何网站都可以操控SQLite中的数据,这引起了安全社区的把稳,干系漏洞的数量也开始上升。
溘然之间,SQLite中的漏洞可被用来攻击javascript阐明器,间接影响了浏览器。
以下是几个影响较大的研究报告:
CVE-2015-7036,fts3_tokenizer()的不可信的指针引用取消Chaitin team在Blackhat 17中展示的fts3OptimizeFunc()中的类型稠浊Exodus创造的fts3SegReaderNext()的整数溢出从过去有关WebSQL研究中,我们创造一个名为“FTS”的虚拟表模块可能是我们研究的重点目标。
FTS全文搜索(Full-Text Search)是一个虚拟表模块,它可对一组文档进行文本搜索。
从SQL语句的角度来看,虚拟表工具与任何其他表或视图看起来一样。但在事理上,虚拟表上的查询调用影子表上的回调方法,而不是普通地对数据库文件进行读写。
一些虚拟表的运用,例如FTS,涉及利用真实的(非虚拟的)数据库表来存储内容。
例如,当将字符串插入FTS3虚拟表时,必须天生一些元数据,以便进行有效的文本搜索。这些元数据终极会存储在名为%_segdir和%_segments的表中,而字符串本身会存储在% _content中,个中%代表原始虚拟表的名称。
这些包含虚拟表数据的赞助表被称为影子表。
由于其本身特性,影子表之间通报数据的接口滋长了很多漏洞。例如CVE-2019-8457,一个在RTREE虚拟表模块中的一个越界读漏洞。
RTREE虚拟表,用于地理数据索引,应该以整数列开始。因此,其他RTREE接口默认RTREE中的第一列是整数。但是,如果我们创建一个表,个中第一列是一个字符串(如下图所示),并将其通报给rtreenode()接口,就会发生越界读取。
现在我们既可以挟制查询,又知道在哪里可以找到漏洞,那么就可以正式开始漏洞利用开拓了。
SQLite内部构造关于SQLite开拓,无论是解释文档,还是实际利用,都可以看到它依赖于各种外部环境,例如关于滥用SQLite标记器中涉及的PHP阐明器,以及和Web SQL有关的javascript阐明器。
但从攻击利用的角度来看,最好有一种通用的方法,别受限于外部环境。因此我们开始探索利用SQLite内部构件进行漏洞利用开拓。
我们希望能用存粹的SQL语句进行以下方面攻击:
内存泄露打包和解包64位指针指针算法在内存中创建繁芜的假工具堆喷射我们将一个接一个地实现以上利用,仅利用SQL语句实现。
为了在PHP7上实现RCE,我们将利用仍未被修复的CVE-2015-7036。
等等?为什么一个长达4年的漏洞还没被修复?由于只有赞许实行来自不可信源(Web SQL)的任意SQL语句才能触发漏洞,以是一贯未被修复。
但是,SQLite的利用是如此的变幻莫测,我们仍可在许多场景触发漏洞。
开拓操持CVE-2015-7036是一个非常好用的漏洞。
大略地说,便是fts3_tokenizer()函数在插入单个参数(如“simple”、“porter”或任何其他被注册的分词器)时会返回分词器地址。
当函数调用两个参数时,fts3_tokenizer将用第二个参数供应的地址覆盖第一个参数的分词器地址。
在某个分词器被覆盖后,fts表中任何利用该分词器的新实例都能被我们挟制程序流。
我们的目标:
分词器地址打算基址假造一个分词器,实行我们的恶意代码我们的分词器覆盖另一个分词器实例化fts3表以实行恶意代码现在回到我们的开拓上来。
面向查询的编程(QOP)我们很自满向外展示了我们研究出的基于构造化查询措辞所进行开拓的独特方法。我们希望其他研究职员能在此方面进行深入探究。
我们的终极会将以下漏洞利用语句植入sqlite_master表中,挟制目标软件所发出的查询,达到加载并查询我们布局的恶意SQLite db文件这一目的。
内存泄露 – 二进制诸如ASLR之类的防御手段无疑提高了内存毁坏的难度,而降服它的一个常见方法便是去理解我们周围的内存布局。
这便是众所周知的内存泄露。
在我们的示例中,内存泄露指的是SQLite返回一个BLOB工具。
之以是瞄准BLOB,是由于它们有时包含内存指针。
如上所示,fts3_tokenizer()函数会返回所要求的分词器的内存地址。以是我们终极得到了一些被反转内存地址。
当然,我们可以利用一些SQLite内置的函数来再次翻转。
QOP利用链
为了存储数据,我们还须要一条INSERT语句。但由于sqlite_master表会对语句进行严格过滤,以是我们不能利用“INSERT”,所有语句都必须以“CREATE”开头。而我们应对这一寻衅的方法便是将查询集中在一个视图中,将它们串联在一起。
下面的例子清楚解释了这一点:
随着我们的链条变得越来越繁芜,利用伪变量肯定更加方便。
解压缩64位指针如果你曾经做过二进制方面的挖洞寻衅,那么对指针的压缩和解压该当并不陌生。
以下语句可以很随意马虎地将十六进制值(就像我们刚刚实现的内存泄露)转换为整数。这样就可以在接下来的步骤中用这些值进行各种打算。
以上sql语句一个字符一个字符地迭代打算十六进制字符串。只需轻微调度一基于1的instr()函数即可完成。
指针算法指针算法因值已转化为整数而变得非常大略。例如,从泄露的分词器指针中提取图像库:
64位指针压缩
在得到泄露的指针并转换打算后,须要再把它们复原,这样我们就可以把它们写到某个地方。
此处应利用函数char(),不过它只在能有限的整数范围内事情。
在多次仔细阅读SQLite文档后,我们溘然有了一个奇怪的觉得:我们的exp实际上是一个数据库。
预先准备一个将整数映射到其期望值的表。
而指针压缩语句如下:
在内存中制造繁芜的虚假工具
写一个指针肯定有用,但仍旧不足。许多内存漏洞的利用场景哀求攻击者在内存中假造一些工具或构造,乃至编写一个ROP链。
现在,我们将把前面先容的几块串起来。
例如,我们自己布局的分词器,阐明如下。
我们的虚假分词器该当符合SQLite所期望的接口:
利用上面描述的方法和一个大略的连接查询,我们可以很随意马虎地假造所需工具。
通过调试器验证,我们确实创建了一个虚假的分词器工具。
堆喷射
上步所制作的虚假工具可参与堆喷射。空想情形下,这该当是后者的某种重复形式。
但不幸的是,sqlite没有像mysql那样的REPEAT()函数。
然而,我们还有一个优雅的办理方案。
zeroblob(N)函数会返回一个由n个字节组成的BLOB工具,然后我们利用replace()将这些0更换为虚假工具。
搜索这些0x41,结果也显示了完美的同等性,每0x20字节重复一次。
内存泄露——堆
我们已经知道二进制图像的位置,我们能够推断出必要的函数在哪里,并用我们的恶意分词器喷射堆。
现在,是时候用我们的分词器去覆盖另一个分词器了。然而,堆地址也是随机的,我们不知道我们的堆喷射物在哪里。
我们须要一个堆透露漏洞。
于是,我们瞄准虚拟表接口。
由于虚拟表利用底层的影子表,以是它们在不同的SQL接口之间通报原始指针是很常见的。
把稳:这类问题在sqlite 3.20中得到理解决。但PHP7是用早期版本进行编译的。如果版本不对,这里也可以利用CVE-2019-8457。
为了泄露堆地址,我们须要预师长西席成一个fts3表,并利用其MATCH接口。
正如我们在第一次内存泄露中看到的,得到的指针须要反转。幸运的是,我们依旧可以利用SUBSTR()。
现在,我们知道堆位置,可以精确地喷射,我们终于可以用自己的分词器覆盖另一个分词器!
放在一起
有了以上所有利用条件,现在是时候回到我们开始的地方:一个密码盗取器。
如上所述,我们须要建立一个“陷阱”视图。因此,我们须要研究我们的目标,准备好精确的视图。
如上面的代码片段所示,我们的目标期望数据库中有一个名为Notes的表,个中包含一个名为BodyRich的列。为了挟制这个查询,我们创建了以下视图:
在Notes被查询后,会实行3个QOP链。先剖析第一个。
堆喷射我们的第一个QOP链该当用大量恶意分词器来添补堆。
p64_simple_create,p64_simple_destroy和p64_system基本上是组成三条QOP链的根本。
例如,p64_simple_create的布局如下:
由于这些链变得非常繁芜、且重复度很高,以是我们创建了脚本。
Demo
视频地址:https://www.youtube.com/embed/cPfYoxLOi1M?feature=oembed
现在,让我们研究SQLite的另一个有趣用例。
iOS
在iOS上常日很难实现持久掌握,由于所有可实行文件必须被署名。幸运的是,SQLite数据库不包括在内。
我们将常用的数据库更换成另一个版本。在设备重新勾引并查询恶意数据库之后,我们将实当代码实行。
为了演示这个观点,我们更换了Contacts DB “AddressBook.sqlitedb”。正如在我们在PHP7中所做的,我们创建了两个额外的DDL语句。一个DDL语句覆盖默认的分词器“simple”,另一个DDL语句试图实例化被覆盖的分词器,从而触发崩溃。现在,我们所要做的便是将原始数据库的每个表重写为一个视图,该视图将挟制所实行的任何查询,并将其重定向到恶意的DDL中。
将contacts db更换为我们的恶意contacts db并重新勾引,会导致以下情形(把稳崩溃信息):
与预期一样,contacts进程在0x4141414141414149处崩溃,它希望在这里找到我们的虚假分词器的xCreate布局部分。
此外,contacts db实际被许多进程共享。联系人、Facetime、Springboard、WhatsApp、Telegram和XPCProxy仅仅只是一部分。个中一些进程比其他进程的权限更高,因此这种攻击技能还能用于提升权限。
我们已向苹果公司报告了漏洞,CVE编号如下:
CVE-2019-8600CVE-2019-8598CVE-2019-8602CVE-2019-8577本文由白帽汇整理并翻译,不代表白帽汇任何不雅观点和态度来源:https://research.checkpoint.com/select-code_execution-from-using-sqlite/