KindleDrip:从邮件地址到信用卡盗刷的严重漏洞,值$1.8万奖金
编译:奇安信代码卫士团队
前一段时间,我们发现亚马逊 Kindle 上新了一个名为 “Send to Kindle” 的功能,可使用户将电子书以邮件附件的形式发送到自己的设备。我们马上想到了这个功能可能存在的安全问题:如果我们能够将恶意电子书发送给毫不之情的用户会怎样?
于是,我开始研究这些潜在问题,结果发现组合利用三个漏洞可导致攻击者在 Kindle 上远程执行代码。该代码以 root 身份运行,攻击者仅需要知道分配给受害者设备的邮件地址即可。
攻击者可以访问设备凭据并使用受害者的信用卡在 Kindle 商店中购买商品。攻击者可以在商店中出售电子书并将钱转到自己的账户中。不过确认邮件可让受害者知晓购买行为。
第一个漏洞可导致攻击者将电子书发送到受害者的 Kindle 设备。在弱用户的上下文中解析电子书时,可利用第二个漏洞运行任意代码。第三个漏洞可导致攻击者提权并以root 身份运行代码。
亚马逊已发布软件更新修复该漏洞并将自动更新用户设备,用户无需任何操作。
那么,Send to Kindle 到底是什么功能?如之前所述,它可使Kindle 用户将 MOBI 电子书发送到自己的设备。为此,亚马逊为每个用户都生成了一个专门用于该功能的特殊的 kindle.com 电子邮件地址。用户可以以邮件附件的形式通过预先获准的邮件地址列表将电子书发送到该邮件地址,书籍就会出现在用户设备上。
什么是预先获准的邮件地址列表?问题出在哪里?实际上,邮件认证的使用范围并没有想象的那样广泛。由于很多邮件服务器并不支持认证,因此出现亚马逊不会验证发件人真实性的假设也并非不合理。
为验证这一点,我使用了一款邮件欺骗服务,欺骗邮件信息并将电子书发送到自己的设备。令人惊喜的是,电子书出现在了设备上!更糟糕的是,并没有线索表明该电子书是从电子信息中接收到的。Kindle 的主页上也通过所选封面图像的形式展示了出来,这使得钓鱼攻击变得更加容易。
亚马逊确实通过目标邮件地址 (destination email address) 为这个功能提供了安全性。攻击者必须能够预测到这个邮件地址。然而,实际上它就是 kindle.com 域名下的用户常用邮件地址(例如 name@gmail.com 转换为 name@kindle.com)。虽然某些用户的名字后可能附加了一个随机的字符串 (name_<random_string>@kindle.com),但多数地址上的熵值看似非常低,且可被暴力破解。最新用户地址的最大熵值为32位,但依然不够好。
找到向任意 Kindle 设备发送电子书的方法后,我需要找到从电子书执行代码的方式。为此,我逆向了Kindle 固件版本 5.13.2(当时是最新版)上的书籍解析代码。KindleTool 用户从固件更新文件中提取文件系统。我还有一个老旧的越狱版 Kindle PaperWhite 3(还有一个新的 Kindle 10)。这样我就可以通过固件中的 gdb 二进制调试任意进程(多么令人愉悦的惊喜),并通过Ghidra 逆向了 arm32 二进制。
在查看出现在固件上的多个库时,一个库引起了我的注意:libjpegXR.so,它似乎是用于解析名为 jpegXR 的晦涩难懂的图像格式,这真是一个完美的攻击面。
JPEG XR 是最初由微软开发的图像格式标准。当解析最新的 Kindle 文件格式 KFX 时,Kindle 支持该格式。由于使用之前的漏洞仅可发送 MOBI 文件,因此它看似无关。我试图查找支持 JPEG XR 的其它代码,发现集成的 Web 浏览器也支持它。Web 浏览器正好,因为 MOBI 文件支持 Web 链接(甚至从视觉上和 TOC 链接难以区分),因此点击书籍链接将打开浏览器。
在阅读 JPEG XR 标准的官方参照代码时,我在 jxr_priv.h 中定义的 jxr_image 结构中发现了:
unsigned char dc_quant_ch[MAX_CHANNELS];
unsigned char lp_quant_ch[MAX_CHANNELS][MAX_LP_QPS];
unsigned char hp_quant_ch[MAX_CHANNELS][MAX_HP_QPS];
以上即是“量化参数“。r_parse.c 中执行的函数 _jxr_r_TILE_HEADER_HIGHPASS 通过图像文件中的数据填充 hp_quant_ch。拷贝数据的长度由 image->num_channels 定义,它也出现在图像文件中。缓冲区的大小是常数 (MAX_CHANNELS=16),因此看起来似乎存在一个缓冲区溢出漏洞。
参考代码通过检查 num_channels 小于 MAX_CHANNELS 的断言就可阻止该缓冲区溢出漏洞。但或许 Kindle 的开发员忘记了也有可能。我在 Ghidra 中弹出 libjpegXR.so来查找开发人员是如何实现的。我立即看到虽然严重依赖于参考代码,但断言并不存在!可能开发人员在多次编辑中将其删除了,或者在生产环境中编译了断言。不管是何种原因,现在的结果是在 Kindle 中解析 JPEG XR 时存在一个缓冲区溢出漏洞,JPEG XR 图像文件中的字节受控。
在 jxr_image 结构中,紧跟溢出的缓冲区之后的是指针 struct jxr_tile_qp *tile_quant。函数 _jxr_r_TILE_HEADER_HIGHPASS 将 hp_quant_ch 拷贝到如下指针中:
memcpy(image->tile_quant[ty*(image->tile_columns) + tx].hp_quant_ch, image->hp_quant_ch, MAX_CHANNELS*MAX_HP_QPS);
因此,利用该溢出,title_quant 本可被覆盖,获取一个绝对写原语(将受攻击者控制的数据写入受攻击者控制的地址)。但情况要好得多:_jxr_r_TILE_HEADER_HIGHPASS 被调用的次数由图像文件中提供的 image->tile_columns 和 image->tile_row 定义。如此,绝对写原语可被单一图像文件使用多次。
现在,我们看下 Web 浏览器进程的内存映射 mesquite:
00008000–000a0000 r-xp 00000000 b3:01 1939 /usr/bin/mesquite
000a0000–000a2000 rwxp 00098000 b3:01 1939 /usr/bin/mesquite
000a2000–002b8000 rwxp 00000000 00:00 0 [heap]
…
该二进制被加载到一个常量地址,且具有一个可执行文件和可写部分。结合绝对写原语,利用较为容易。使用绝对写原语,可将 shellcode 写入可执行部分。接着,该原语可被再次用于“喷射“具有shellcode 地址的全局偏移表 (GOT)。Mesquite 进程是多线程的,因此其它线程之一不可避免地会从 GOT 调用要给函数,从而导致shellcode 被执行。
Mesquite 进程在 chroot 下以弱用户 framework 身份运行。因此此前的甚至无法用于重启设备。我们需要提权。
于是我看实查看监听本地套接字的root 进程:
[root@kindle root]# netstat -ntpl | tail -n +3 | awk '{ print $7 }' | awk -F / '{ print $1 }' | xargs -I {} ps -o uname=,cmd= -p {}
9000 /app/bin/AaMenuApplication
9000 webreader
root stackdumpd
9000 kfxreader -l
9000 /usr/java/bin/cvm -Xmx49m -Xms49m ...
root fastmetrics
9000 kfxview
9000 /usr/java/bin/cvm -Xmx49m -Xms49m ...
Stackdumpd 看似不错,因此通过 Ghidra 打开后查看它到底是如何工作的。从我所知的理解来看,该进程负责生成崩溃进程的栈转储。它接收崩溃进程 id 和线程 id,并将其传递给 /usr/bin/dump-stack。这是通过 gdb (这就是它出现在固件中的原因所在)连接到崩溃进程的 shell 脚本,如名称所示,它负责转储栈。Shell 脚本如下:
${GDB} --quiet \
--se "${PROCESS_EXE}" \
--pid ${CURRENT_TID} \
--batch \
-ex "bt" \
-ex detach
从中我们注意到 CURRENT_TID 并未被引用,因此或许能够将参数注入 gdb。由于 gdb 能够运行 command 参数中给定的任意命令,因此它本可被用于以 root 身份运行任意代码。
这种情况更加复杂。开发人员似乎意识到了这个问题,并试图通过确保 CURRENT_TID 是number而阻止该问题。第一次检查发生在 dumpstackd 中,内容是检查atoi 不会返回0:
第二次检查位于 dump-stack 中,它使用的正则表达式如下:
“$(echo “$CURRENT_TID” | grep ‘^[0–9]*$’)” != “”
只要字符串以数字开头,即可绕过 atoi 检查。在这些数字后增加一个换行符即可绕过正则表达式检查。因此简单的字符串如 “1\nsome string” 即可绕过这两种检查。因此,我们就发现了一个可在root 用户上下文中执行任意代码的漏洞。再结合我们之前发现的两个漏洞,就造成在 Kindle 上的 root RCE 后果。
如下短视频演示了在 Kindle 10 固件版本 5.13.2 上的完整利用链。
2020年10月17日:向亚马逊漏洞研究计划提交漏洞报告
2020年10月19日:亚马逊证实漏洞存在
2020年11月4日:获得1.2万美元的奖励
2020年11月11日:获得额外6000美元的假日推广福利奖励
2020年12月10日:固件 5.13.4发布,内含对 JPEG XR 溢出问题和 PE 问题的修复。
2020年12月29日:协助亚马逊审计了所部署的修复方案
2021年1月19日:文章发布获批准
书籍注入
如亚马逊无法验证发件人的邮件地址,现在会向获准的地址发送验证链接:
JPEG XR 溢出
亚马逊通过简单检查 channel 数量未超过 MAX_CHANNELS 的方式,修复了溢出漏洞。
提权
Dumpstackd 中的代码变更为:
从中可见,亚马逊用一个新函数替换了 atoi 调用。该新函数检查字符串中仅包含数字。
至于 /usr/bin/dump-stack,固件之间的文件偏移即说明问题。正则表达式检查现在更加强壮:
< PROCESS_PID=$1
< PROCESS=$2
< CURRENT_TID=$3
---
> PROCESS_PID=$(echo $1 | head -n 1 | grep '^[[:digit:]]*$')
> PROCESS=$(echo $2 | head -n 1 | grep '^[[:alnum:]_]*$')
> CURRENT_TID=$(echo $3 | head -n 1 | grep '^[[:digit:]]*$')
使用三个不同的漏洞,在仅向设备分配了邮件地址的情况下,我设法实现以 root 用户身份在亚马逊 Kindle 上执行任意代码的目标。这些漏洞本可导致攻击者访问设备凭据并在 Kindle 商店中购买商品,也本可用于越狱最新的 Kindle 设备。我提交的漏洞报告得到了认真对待,且在合理期限内将漏洞修复。
谷歌会怎么修?黑客正在利用Analytics 服务窃取电商网站的信用卡数据
我俩也组了个队,找到一个苹果RCE 0day,获 $5 万奖金
看我如何黑掉 Facebook 并获奖金 $7500
https://medium.com/realmodelabs/kindledrip-from-your-kindles-email-address-to-using-your-credit-card-bb93dbfb2a08
题图:Pixabay License
本文由奇安信代码卫士编译,不代表奇安信观点。转载请注明“转自奇安信代码卫士 https://codesafe.qianxin.com”。
奇安信代码卫士 (codesafe)
国内首个专注于软件开发安全的
产品线。