自2007年以来,亚马逊已售出数千万台Kindle,但这也意味着数以千万计的人可能会因为这些 Kindle 中的软件漏洞而被黑客攻击。他们的设备可能会变成木马,或者他们的私人本地网络会受到攻击,甚至他们的账单帐户中的信息也可能会被盗。
远程访问用户 Kindle 的最简单方法就是对电子书发起攻击。含有恶意程序的电子书可以通过“自助出版”服务发布并在任何虚拟图书馆(包括 Kindle 商店)中免费访问,或通过亚马逊“发送到 Kindle”服务直接发送到最终用户设备。
为了验证这种攻击,研究人员成功制作了一本含有恶意程序的书。如果你在 Kindle 设备上打开这本书,它可能会导致一段隐藏的代码以 root 权限执行。此时,用户就已经失去了对电子阅读器的控制。
该问题已于 2021 年 2 月报告给亚马逊,并于 2021 年 4 月在 5.13.5 版本的 Kindle 固件中修复,修补后的固件将自动安装在连接到互联网的设备上。
Kindle Touch 架构
基本上,Kindle OS 是一个 Linux 内核,其中包含主要由 busybox 提供的一组本机程序、用于进程间通信的 LIPC 子系统以及用于用户界面 (UI) 和服务的 Java 和 Webkit 子系统。
Kindle Touch 架构
LIPC 是一个基于 D-Bus 的 IPC 库及其将所有 Kindle 组件链接在一起的环境。 Kindle 进程可以使用此库来启动应用程序、公开应用程序属性/设置、监听或触发事件。例如,用 HTML 和 Javascript 编写的 Webkit 应用程序可以使用 LIPC 与 Java 服务或本机应用程序交互。
大多数 UI 是用 Java 编写的,Java 子系统(框架)为服务和 UI(所谓的Booklets)提供 LIPC 处理程序。比如 Kindle 主页 UI 窗口就是框架管理的 com.lab126.booklet.home Booklets。
Webkit 子系统(HTML5 和 Javascript)是另一种创建 UI 元素的方法。内置的实验浏览器是 Webkit 子系统的一部分。 Pillow 是一个允许从 Javascript 访问 LIPC 的库。
Kindle组件的解析
Kindle 电子阅读器固件的最新版本(5.13.4)已在亚马逊官方网站公开下载,源代码也部分可用。但是源代码对研究人员的研究没有帮助,因为它主要由第三方开源项目组成,包括 Linux 内核,并在亚马逊上进行了小幅调整,负责解析和呈现没有源代码的电子书组件。
研究人员的第一个目标是发现电子书解析框架中的漏洞,研究人员有足够的固件文件,不需要真正的 Kindle 设备。
当你在Kindle设备上下载一本新书时,/mnt/us/documents是常规的电子书目录,谁先处理文件?/usr/bin/scanner 服务会定期扫描文档目录中的新文件,并根据文件扩展名使用“提取器”库之一从电子书中提取元数据。所有提取器都列在 /var/local/appreg.db sqlite 数据库中。每种支持的 Kindle 电子书格式都有一个处理程序:
如果扫描程序与文件扩展名不匹配或出现解析错误,则不会向用户显示电子书。
研究人员没有深入研究扫描过程,因为提取元数据的操作过于简单,无法提示解析错误。
扫描程序完成工作后,主屏幕上会显示新书的缩略图。此时,Java 框架负责在用户点击时打开这本书。可以在 /opt/amazon/ebook/lib 固件目录中找到实现打开和呈现电子书逻辑的 Java 存档 (JAR) 文件。主要是 MobiReader-impl.jar、YJReader-impl.jar、PDFReader-impl.jar、HTMLReader-impl.jar 和 TopazReader-impl.jar 文件。
为了进一步研究,研究人员决定将注意力集中在 PDF 文件格式上,因为它是最常见但同时也是复杂的格式之一。
让我们看看PDFReader-impl.jar(com.amazon.ebook.booklet.pdfreader.impl.PDFModel类)中PDF书籍打开函数的实现:
此函数只是 nativeOpenPDFDocument 本机函数的包装程序,其主体位于 /usr/java/lib/libPDFClientJNI.so 库中。
nativeOpenPDFDocument函数启动PDF服务器/usr/bin/pdfreader,分叉(forking)进程,并通过开源HTTP客户端/服务器库/usr/lib/libsoup-2.4.so同步向其发送“openBook”消息。实际上,它会向 https://127.0.0.1:7667/command/openBook 发送 GET 请求。
pdfreader 服务器是研究人员研究的主要目标,最后,研究人员将在此过程的上下文中运行有效载荷。
在启动时,pdfreader 服务器通过 setuid 调用将自身降低到“framework”用户(uid 9000)的权限。然后它启动一个监听端口 7667 的soup服务器,为高级 PDF 操作定义了数十个处理程序,包括研究人员感兴趣的“openBook”和“startRendering”处理程序。
由 Amazon 编写的 /usr/lib/libFoxitWrapper.so 库提供了一个用于处理 PDF 文件的 API。 pdfreader 在它的soup处理程序中使用这个库。例如,“openBook”处理程序如下所示:
请注意 libFoxitWrapper.so 库的以下重要函数:
openPDFDocumentFromLibrary(char *file, char* password, uint32_t* handle) ——打开 PDF 文档。
getCurrentPage(uint32_t handle, uint32_t page, uint32_t flag)——将 PDF 页面解析为内部结构。
renderPageFromLibrary(uint32_t handle, uint32_t page, uint32_t width, uint32_t height, float scale, uint8_t Landscape, uint8_t* out) ——渲染 PDF 页面并将其转换为图像,当被调用时, stream filter开始被解析。
这些函数是对 PDF 树结构进行模糊测试的良好切入点。
顾名思义,libFoxitWrapper.so 是由 /usr/lib/libfpdfemb.so 库在 Kindle 设备上提供的流行 Foxit PDF SDK 的包装程序。 libfpdfemb.so 是 Foxit Software Inc. 专有的闭源库。 Foxit Embedded PDF SDK 手册可以在网上找到。
模糊 PDF 过滤器
研究人员试图从 libFoxitWrapper.so 库中对上述函数进行模糊测试,但这种方法没有带来任何结果,除了一组空指针异常。一种更有前景的 PDF 格式方法是选择一个特定对象或 stream filter作为测试目标。因此,研究人员决定对 libfpdfemb.so 库进行模糊测试。
但首先,让研究人员来看看经典的模糊测试模型。
模糊任何闭源库的最简单方法是编写一个可执行文件,将库加载到内存中并调用目标函数。这个加载程序将一个带有排列数据的文件作为命令行参数,将其读入,并将数据传递给测试中的函数。接下来,加载程序被检测或在模拟器上运行,以收集每个测试用例的代码覆盖矩阵,一个第三方的fuzzers/permutors被用来基于覆盖矩阵生成新的测试用例。
为了对 libfpdfemb.so 库进行模糊测试,研究人员选择了 American Fuzzy Lop (AFL) 和 Quick emulator (Qemu) 的组合,主机是Ubuntu。
模糊测试方案
研究人员还需要注意一件事,Kindle 设备基于 ARM 处理器。因此,研究人员的加载程序是使用 arm-linux-gnueabi-g++ 编译的,Qemu 很容易在 x86 上模拟 ARM。
在 libfpdfemb.so 库中对单词“CPDF”和“Codec”的简单搜索使研究人员能够找到所有可能的 stream filter/编解码器:Predictor、Decrypt、Flate、Fax、Lzw、AsciiHex、RunLen、Ascii85、Jpeg、Jbig2和Jpx。让我们看看其中一个例子。
带有 jbig2 过滤器的 PDF 页面片段
声明了带有 jbig2 过滤器的图像 Im1,Jbig2 是一种用于双层图像的图像压缩标准。 jbig2 编码器将输入页面分割为多个区域:文本、半色调图像、细化等。这些区域保存在 JBIG2Globals 流中。渲染 PDF 页面时,libfpdfemb.so 会解析 JBIG2Globals 流并重建图像。
在 libfpdfemb.so 库中定义的 Jbig2Module 对象负责解码 jbig2 压缩图像。
Jbig2Module 对象
其 StartDecode 方法声明如下:
在其他过滤器中,研究人员使用 StartDecode 函数作为入口点对 jbig2 解码算法进行了模糊测试,并排列了图像大小(宽度和高度参数)、图像流(src_buf、src_size)和 JBIG2Globals 流(global_data、global_size)。可以在下方看到研究人员用来调用 StartDecode 的工具。基变量是 libfpdfemb.so 库在内存中的地址。
结果,研究人员在 JBIG2Globals 解码算法中发现了一个有价值的堆溢出漏洞。
CVE-2021-30354产生的堆溢出
看看以下 JBIG2Globals 流:
格式错误的JBIG2Globals流
这里定义了两个页面区域:
图像信息区域(前 0x23 字节),图像宽度为 0x80,高度为 1,步幅为 0x10。步幅计算为 ((width + 31) >> 5) < < 2。
“细化”区域(从 0x23 到 0x4D 字节),该区域包含 jbig2 编码信息以优化图像。由于只能细化图像的一部分,它还包含细化矩形的坐标。在研究人员的例子中,提供的矩形参数是:width – 0,height – 0x10,x – 0,y – 0x40000000。
这是一个格式错误的流,在细化区域中定义了一个超大的矩形。
在这种情况下会发生什么?该算法尝试将基础图像扩展到新的维度。新图像的高度重新计算为高度 + y,并为调整大小的图像分配 (height + y) * stride 堆内存。但是扩展函数中有一个错误导致堆溢出:计算新图像在内存中的大小时错过了对 INT_MAX 的检查。 32 位寄存器溢出,为图像分配 0x100 字节而不是 0x400000100。
扩展函数
这意味着通过使用细化区域,研究人员可以“细化”图像之外的数据,并获得任意写入原语。在以下示例中,第二个细化区域从堆中的图像开始处以 0x1234 * 0x10 字节的偏移量覆盖 0x10(跨步)字节。数据块(0x71到0x79字节)由jbig2算法解压缩,然后使用异或处理堆内容。
受控堆溢出
研究人员可以创建任意数量的细化区域,并覆盖彼此相距一定距离的部分内存。此外,写入是通过异或处理操作完成的这一事实允许研究人员仅修复内存的特定位,而不是整个字,并在需要时绕过 ASLR 保护。
如前所述,libfpdfemb.so 库是 pdfreader 进程的一部分。这个进程的数据段和堆段是读/写/执行的。 ASLR 内置于 Linux 内核中,由参数 /proc/sys/kernel/randomize_va_space 控制。它在 Kindle 设备上的默认值为 1,这意味着数据段的基地址紧跟在可执行代码段的末尾之后。换句话说,数据段和堆没有随机化。这两个事实使得利用发现的 jbig2 漏洞变得微不足道。
CVE-2021-30355会造成权限管理不当
研究人员现在在 pdfreader 进程的上下文中存在 RCE 漏洞,用户可以将PDF格式的书下载到他的Kindle设备上。当这本书被打开时,一个恶意的有效载荷就被启动了。
pdfreader 进程具有框架用户权限:uid=9000(framework) gid=150(javausers) groups=150(javausers)。它可以发送 LIPC 消息,访问特殊的内部文件,但它仍然是有限的。研究人员想成为 root 来重置所有限制。
因此,研究的第二阶段是找到一个允许框架用户在root用户下运行代码的LPE漏洞。
首先,研究人员越狱了研究人员的一个 Kindle,因为仅仅从固件中获取文件来搜索逻辑 LPE 是不够的。研究人员需要查看正在运行的进程和打开的端口,并能够调试 Kindle 服务。
你可以在网上找到Kindle某些版本固件的软件越狱,但最常用的方法是通过串口越狱。虽然这需要拆卸设备,但研究人员做到了。
通过串口越狱 Kindle
研究人员得到了一个越狱的设备,然后分析了拥有root权限的服务,以及它们访问的资源。最终,研究人员发现了一个逻辑错误,或者更准确地说,在Kindle的一个服务中,发现了一个不恰当的权限管理。太好了,这就无需对设备驱动程序进行模糊测试。
框架用户对 /var/tmp/framework 目录具有完全访问权限,他可以在其中创建任何可执行文件。实际上,这是用户的工作目录。例如,研究人员可以创建一个记录用户权限的 bash 脚本文件 payload.sh:
框架用户对 /var/local/appreg.db sqlite 数据库具有读/写访问权限,该数据库本质上是一个应用程序注册表。这意味着研究人员可以使用 /usr/lib/libsqlite3.so 库或简单地编辑文件来修复数据库条目,研究人员希望对属性表中的一个“命令”项进行修补。
appreg.db 中的属性表
例如,研究人员可以修补条目 com.lab126.browser:将值字段设置为 /var/tmp/framework/payload.sh 而不是 /usr/bin/mesquite。以下 SQL 请求完成工作:
该框架可以请求由 appmgrd 服务代表的应用程序管理器启动任意应用程序,研究人员可以使用 /usr/lib/liblipc.so 库发送 LIPC 消息以打开浏览器应用程序。此 shell 命令执行相同的操作:
应用程序管理器负责启动内置应用程序,为此,它会监听适当的 LIPC 事件。要启动浏览器应用程序,它会从 appreg.db 读取条目 com.lab126.browser,并执行值字段中指定的命令。当研究人员修补这个数据库条目时,研究人员的 payload.sh 脚本被启动。
appmgrd 服务具有 root 权限, “root: uid=0(root) gid=0(root)”字符串由payload.sh记录。
可以从研究人员拥有的 pdfreader 进程中轻松利用所描述的 LPE 漏洞,libsqlite3.so 和 liblipc.so 库已经加载到进程内存中。通过结合发现的两个漏洞,任何恶意载荷都可以以 root 身份运行。
总结
由于恶意软件代码是以 root 用户权限执行的,打开这样的恶意书可能会导致无法弥补的损失。攻击者可能会删除你的电子书,可能获得对你的亚马逊帐户的完全访问权限,可能将你的 Kindle 转换为木马,攻击本地网络中的其他设备,等等。
所描述的漏洞已于 2021 年 2 月报告给亚马逊,并于 2021 年 4 月在 5.13.5 版本的 Kindle 固件中被修复。
本文翻译自:https://research.checkpoint.com/2021/i-can-take-over-your-kindle/如若转载,请注明原文地址