CVE-2016-10021

Hello world,hello blog!

Posted by 吴柚 on February 17, 2020

资料链接

  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和转换。

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

POC

使用在论坛上找到的pdf来进行触发的,这是由别人精心制作所得。通过邮件诱发受害者打开此PDF,然后即可使用DOS攻击。

以下是POC的下载地址。

POC to trigger stack overflow

补丁

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,所以此环节待补充。