2手机端漏洞复现
运用编写的craft 程序输出精心设计的哈夫曼编码很长的恶意文件bad.webp 会在解码时发生缓冲区溢出,何时需要 decode 解码?在将 webp 图像转为其他类型图像如 jpg、png 时需要,因为它们会调用 Google 提供的 webp 库中代码 dwebp 解码程序。
Android 其实也用的 Google 提供的 webp 库,若要将 webp 类型图片转为其他类型时,需要调用 dwebp 程序将 webp 文件解码 decode
接下来依次会在手机端软件 qq、小红书、pixlr(图像处理软件)以及安卓手机端 chrome 浏览器进行测试。以 qq 为重点分析为例。
一、QQ
手机 qq 发送 webp 图片时其实自动转为 jpg 类型,可以通过任意手机 qq 发送 webp 类型图片,然后点开图片点击右下角下载按键保存到本地,然后在本地相册查看文件类型时其实转为了jpg 类型。根据安卓手机qq 这一特点说明会调用安卓将webp 转为其他类型图片的过程,也就是一定会经历 decode 解码这一过程,那么可以体现这一漏洞
实验用的是最新版的手机QQ(V 8.9.83.12605)
打开 logcat 设置过滤器:
发送正常的 test.webp:
手机端很快发送成功,无肉眼可见的延迟
时间戳显示用时 0.1s,out.isDecodeSuccess=true 解码成功,输入输出流完整且结束时也标志成功
发送错误的 bad.webp:
手机端发送图像一直处于加载状态且始终加载进度为 0,经多次实验,平均经历 1 分半至 2分钟后图像才会停止加载上传并显示红色感叹号表征发送失败。
查看 Android 日志信息,会发现有两段报错内容第一段:
首先由调试信息获知解码 decode 失败,发现文件头部信息大小 headersize 值异常,44397字节很大,远远超出正常范围
日志的错误信息显示了在尝试解码一个可能受 DRM 保护的图片时遇到的问题。具体的错误是 java.io.IOException: Resetting to invalid mark , 这 个 异 常 通 常 在 调 用 BufferedInputStream.reset() 方法时抛出,如果在调用 reset() 方法时,输入流无法回滚到最后一次 mark() 方法标记的位置。
原因是输入流的大小超过了 BufferedInputStream 的缓冲区大小。BufferedInputStream 只能回滚到其内部缓冲区所能覆盖的范围内,如果输入流的数据量超过了这个范围,那么 reset()方法就无法回滚到标记的位置。
接下来仔细分步分析这一长段错误信息:
1.缓冲区输入流重置指针失败
查看 Android 源码: https://cs\.android\.com/android/platform/superproject/\+/master:libcore/ojluni/src/main/java/jav a/io/BufferedInputStream.java;l=438?q=BufferedInputStream.reset&ss=android%2Fplatform%2F superproject&hl=zh-cn
BufferedInputStream.reset()方法用于将流重置到最后一次调用 mark()方法时的位置。如果从上次调用 mark()方法后读取的数据超过了 marklimit(在调用 mark()方法时设置),那么 markpos 将会是-1,此时调用 reset()方法会抛出 IOException 报错。
日志显示错误信息是”Resetting to invalid mark”,表明在调用 reset()方法时,markpos 是-1,即没有有效的标记位置。这是因为从上次调用 mark()方法后读取的数据超过了 marklimit。 2.转化位图失败
Android 中图片主要以 Bitmap 位图的形式存在,输入流->解码成位图->输出到文件,所以位图从输入流 inputstream 中解码。BitmapFactory.decodeStream 方法是用来从输入流中解码位图的。然后实验中解码失败的原因是上一个步骤的输入流都有错误无法解码成位图,所以位图返回为空 null https://cs\.android\.com/android/platform/superproject/\+/master:frameworks/base/graphics/java
/android/graphics/BitmapFactory.java?q=BitmapFactory.decodeStreamInternal&ss=android%2Fpl atform%2Fsuperproject&hl=zh-cn
3.转化新文件失败
紧接着第二段错误信息显示 open failed 打开失败,因为上一步位图是空所以输出流到文件为空,转化的新文件输入流为空,没有成功形成一个新的文件,所以找不到这个文件。所以这个 bad.webp 并没有成功转为一个 jpg 类型图片
二、小红书:设置过滤器:
上传 bad.wep 并点击“下一步”生成图片时,手机端显示“合成失败”,同时日志输出如下错误信息,重复输出 6 组尝试了六次。Debug 信息与 qq 输出流读取新文件的信息相同,解码失败,但是从上面 qq 的日志信息可以看出其实从输入流到解码位图的过程就已经失败了。
三、Pixlr(图像处理软件):设置过滤器:
上传 bad.wep 并点击“下一步”生成图片时,手机端显示“无法打开图像”,同时日志输出如下错误信息,只输出 1 次说明只尝试了 1 次。Debug 信息与之前在其他手机端软件调试信息相同,解码失败,但是从最后一条输出信息证实确实无法从输入流中解码位图
Pixlr 闪退:(详情可见 demo1 视频)视频 0:00~0:26 bad.webp:
用 chrome 浏览器打开 bad.webp 文件,点击分享(用其他应用打开)到应用 pixlr。测试显示 pixlr 闪退回 chrome 浏览器,并显示“Pixlr:无法打开图像”。尝试 3 次发现结果不变依旧闪退,说明闪退现象不是偶然。
对比:
视频 0:27~0:49 test.webp:
打开相同 webp 类型的正常 test.webp 文件,同样操作(分享到 Pixlr 打开),可见反应迅速无异常现象。
视频 0:50~1:13 other.png:
打开其他类型图像如 other.png 文件,同样操作(分享到 Pixlr 打开),同样无异常现象。
原因:
Pixlr 可打开多种类型图像但都转为了 jpg 类型进行编辑。这一点在我们测试时打开任意正常图像编辑后,点击保存键,发现都是 jpg 类型。说明 Pixlr 在打开 webp 类型图像时有解码转换为 jpg 类型这个过程存在,那么可以利用我们精心设计的有缓冲区溢出漏洞的恶意文件 bad.webp 进行漏洞复现,发现出现应用闪退的现象。具体的程序内部报错以及原理可参考前面的文档。
四、chrome 浏览器
若在手机端 chrome 浏览器(版本 107.0.5304.91)打开 bad.webp 文件,手机端无法显示图像,查看日志输出错误信息无法解码图像。因为我们用的是一个有缓冲区溢出的恶意图像
Google len 崩溃:(详情可见 demo2 视频)
用 chorme 浏览器登陆一个可在线识图的网站 Smallseotools.com,这是一个可以显示在多个浏览器(Google、bing、yahoo)识图搜索结果的网站。
视频 0:00~0:43 bad.webp:
上传恶意的 bad.webp 文件,点击搜索,通过网站验证后开始搜索。然后网站跳转到选择识图浏览器页面,我们选择用 google 浏览器(Google len 应用)识图,发现 Google Len 崩溃无法显示,多次重新加载页面依然无效。
视频 0:44~1:42 test.webp:
上传正常的 test.webp 文件,点击搜索。同样操作选择 Google 浏览器识图。这是为了印证 Google len 支持 webp 类型文件,毕竟 webp 类型就是来自 Google 研发的。可见 Google len正常搜索显示结果,重新加载依然正常搜索无异常。(如果加载有些缓慢是网络代理的原因,不是图像本身的问题)
视频 1:43~2:50 other.png:
上传正常的 other.png 文件,点击搜索。发现 Google len 仍能正常识图并显示结果。(如果加载有些缓慢是网络代理的原因,不是图像本身的问题)
原因:
这里用的 Google 浏览器不是最新版的,也就是 Google 还调用的是存在代码漏洞的 webp 库,所以上传恶意 webp 文件进行识图解析的解码过程中会出现崩溃的异常现象。