CVE-2016-10221

Hello world,hello blog!

Posted by 吴柚 on February 14, 2019

资料链接

  1. https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-10221

  2. https://www.suse.com/security/cve/CVE-2016-10221/

  3. 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再次进入递归,并且会无限的递归下去,由于只有进栈没有出栈,所以最终会导致栈溢出。