资料链接
-
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-10221
-
https://nvd.nist.gov/vuln/detail/CVE-2016-10221#VulnChangeHistorySection
软件介绍
Mupdf是一款轻量级的PDF阅读器,支持.xps.cbz.zip.png.jpe.tif等格式,支持透明度、加密、超链接、注释、搜索,另外在文件包中,还有一个 mupdf-v8.exe 的文件,可选支持交互式功能,如表单填写、javascript和转换。
软件版本
包含cve的软件版本为1.10a。
下载链接为:https://mupdf.com/downloads/archive/mupdf-1.10a-source.tar.gz
漏洞点分析
该漏洞由因为pdf_layer.c文件中的count_entries函数溢出导致,攻击者使用精心准备的pdf文件可以诱发dos攻击。
首先分析一下漏洞代码里面所用到的几个函数。
int pdf_array_len(fz_context *ctx, pdf_obj *obj) //ctx被定义为一段存储的空间,影响不大,故不分析。
{
RESOLVE(obj);
if (obj < PDF_OBJ__LIMIT || obj->kind != PDF_ARRAY) //PDF_OBJ__LIMIT是预先设定好的obj文件的下限,必须大于这个下限,进程才会继续进行。同时obj的格式必须是PDF类型,否则会返回0导致进程结束。
return 0;
return ARRAY(obj)->len;
}
pdf_array_len()函数的作用就是返回obj文件的长度。
pdf_obj *pdf_array_get(fz_context *ctx, pdf_obj *obj, int i)
{
RESOLVE(obj);
if (obj < PDF_OBJ__LIMIT || obj->kind != PDF_ARRAY) // 同上。
return NULL;
if (i < 0 || i >= ARRAY(obj)->len) //i的大小必须满足循环条件,否则会结束运行。
return NULL;
return ARRAY(obj)->items[i]; // 将obj数组化,并且返回obj数组的第i个元素。
}
pdf_array_get()函数可以得到obj数组中第i个元素。
int pdf_is_array(fz_context *ctx, pdf_obj *obj)
{
RESOLVE(obj);
return obj >= PDF_OBJ__LIMIT ? obj->kind == PDF_ARRAY : 0; //如果obj文件大于预定好的下限并且obj文件的种类为PDF,则返回1,否则返回0.
}
pdf_is_array就是一个判断函数,判断输入是否为满足条件的PDF文件。
下面的函数即为漏洞点函数。
count_entries(fz_context *ctx, pdf_obj *obj)
{
int len = pdf_array_len(ctx, obj); //获取obj的长度并赋予给len。
int i;
int count = 0;
for (i = 0; i < len; i++)
{
pdf_obj *o = pdf_array_get(ctx, obj, i); 将obj数组的第i个元素赋予给o。
count += (pdf_is_array(ctx, o) ? count_entries(ctx, o) : 1); //如果obj函数符合条件则进入递归。
}
return count;
}
我们可以看到,递归没有出口。如果构造的pdf文件只有一个元素,pdf_array_get()函数会将obj赋予给o,即obj=o,在接下来的语句中o再次进入递归,并且会无限的递归下去,由于只有进栈没有出栈,所以最终会导致栈溢出。
POC
使用在论坛上找到的pdf来进行触发的,这是由别人精心制作所得。通过邮件诱发受害者打开此PDF,然后即可使用DOS攻击。
以下是POC的下载地址。
补丁
for (i = 0; i < len; i++)
{
pdf_obj *o = pdf_array_get(ctx, obj, i);
count += (pdf_is_array(ctx, o) ? count_entries(ctx, o) : 1);
if (pdf_mark_obj(ctx, o))
continue;
fz_try(ctx)
count += (pdf_is_array(ctx, o) ? count_entries(ctx, o) : 1);
fz_always(ctx)
pdf_unmark_obj(ctx, o);
fz_catch(ctx)
fz_rethrow(ctx);
}
return count;
由于此CVE没有详细的参考资料,所以关于漏洞点的很多信息我都是通过观察补丁来进行推导的。补丁里面给o重新进行了限制,正是基于这一点我才推测是o导致的溢出。
fuzz出漏洞的过程
#0 0x5b6e44 in pdf_get_xref_entry source/pdf/pdf-xref.c:243
#1 0x5c3b44 in pdf_cache_object source/pdf/pdf-xref.c:1889
#2 0x5c4a43 in pdf_resolve_indirect source/pdf/pdf-xref.c:1999
#3 0x5c4ba9 in pdf_resolve_indirect_chain source/pdf/pdf-xref.c:2025
#4 0x5d84bb in pdf_array_len source/pdf/pdf-object.c:625
#5 0x585deb in count_entries source/pdf/pdf-layer.c:86
#6 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#7 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#8 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#9 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#10 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#11 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#12 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#13 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#14 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#15 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#16 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#17 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#18 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#19 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#20 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#21 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#22 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#23 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#24 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#25 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#26 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#27 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#28 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#29 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#30 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#31 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#32 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#33 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#34 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#35 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#36 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#37 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#38 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#39 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#40 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#41 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#42 0x585e42 in count_entries source/pdf/pdf-layer.c:93
#43 0x585e42 in count_entries source/pdf/pdf-layer.c:93
....
可以看到发现此漏洞的原因就是进程一直处于递归状态,最后递归过多,导致的栈溢出。
对内存的分析
由于y一直未能在Linux下编译运行出mupdf,所以此环节待补充。