资料链接
-
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-10221
-
https://www.suse.com/security/cve/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和转换。
官网链接:https://mupdf.com
软件版本
包含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再次进入递归,并且会无限的递归下去,由于只有进栈没有出栈,所以最终会导致栈溢出。