CVE-2018-19134:通过Ghostscript中的类型混淆来远程执行代码

栏目: C · 发布时间: 5年前

内容简介:在这篇文章中,我将展示如何利用我们先回顾一下这个漏洞,首先,PostScript对象的类型为结构体

CVE-2018-19134:通过Ghostscript中的类型混淆来远程执行代码

一、前言

在这篇文章中,我将展示如何利用 CVE-2018-19134 漏洞远程执行任意代码。该漏洞是由类型混淆引起的。2018年11月,该漏洞被发现。如果你想了解更多关于QL技术相关文章,可以翻看我以前的 博客

二、漏洞

我们先回顾一下这个漏洞,首先,PostScript对象的类型为结构体 ref_s (或者为ref,ref是ref_s的别名),如下图所示:

CVE-2018-19134:通过Ghostscript中的类型混淆来远程执行代码

该结构大小为16字节,其中tas_s类型占据前8个字节,包含的字段为类型信息以及数组,字符串,字典的大小。如下图所示:

CVE-2018-19134:通过Ghostscript中的类型混淆来远程执行代码

我发现该漏洞触发的原因是 zsetcolor 函数中缺少类型检查:在将其解释为gs_pattern_instance_t之前未进行pPatInst类型检查。如下图所示:

CVE-2018-19134:通过Ghostscript中的类型混淆来远程执行代码

如下图所示,r_ptr是一个宏,被定义在 iref.h 文件中:

CVE-2018-19134:通过Ghostscript中的类型混淆来远程执行代码

pstruct的值源自PostScript,因此由用户控制。例如,setpattern的输入将造成pPatInst.value.pstruct的值为0x41。如下所示:

CVE-2018-19134:通过Ghostscript中的类型混淆来远程执行代码

将代码放入 pattern_instance_uses_base_space 后,我看到我控制了一个指针对象,代码将其解释为gs_pattern_instance_t指针,如下所示:

CVE-2018-19134:通过Ghostscript中的类型混淆来远程执行代码

所以我可以控制许多函数指针:get_pattern,uses_base_space和pinst。

创建一个伪造的对象

PostScript类型数组非常重要,因为它的值指向ref数组开头的ref指针。并允许我创建一个缓冲区进行存储,我可以控制缓冲区内容,如下所示:

CVE-2018-19134:通过Ghostscript中的类型混淆来远程执行代码

如上图,灰色部分表示我可以控制部分数据(我无法完全控制type_attrs和pad),绿色部分是我可以完全控制的数据。其中关键点是,refs中的值和gs_pattern_instance_t中的type都是8个字节,意味着pinst-> type-> procs中的procs将是部分被我控制的底层PostScript数组。事实证明,我确实可以通过使用嵌套数组来控制函数指针get_pattern和uses_base_space:

CVE-2018-19134:通过Ghostscript中的类型混淆来远程执行代码

[16#41 [16#51 16#52]]的结果如下所示:

CVE-2018-19134:通过Ghostscript中的类型混淆来远程执行代码

这表明我确实可以控制uses_base_space和get_pattern,下一步,如何使用任意函数指针来实现代码执行。

我决定先获取一些有效的函数指针,在Ghostscript中,内置的PostScript运算符由 t_operator 类型表示,ref的值是一个op_proc_t,它是一个 函数指针 。可以通过下面的命令获得:

CVE-2018-19134:通过Ghostscript中的类型混淆来远程执行代码

让我们尝试在我们伪造的数组中加入一些内置函数,如下所示:

CVE-2018-19134:通过Ghostscript中的类型混淆来远程执行代码

我将使用以下指令:systemdict get exch exch put。该指令可以从systemdict中获取foo,并将其存储在索引为idx的数组中。

实际上,我现在可以直接调用zget函数和zput函数来替代uses_base_space和get_pattern,如下所示:

CVE-2018-19134:通过Ghostscript中的类型混淆来远程执行代码

我现在可以调用PostScript中的函数并控制函数的参数,当从PostScript调用底层C函数时,执行上下文作为参数传递给C函数,该上下文类型为 i_ctx_t (gs_context_state_s的别名),包含许多无法通过PostScript控制的信息,其中包括重要的安全设置,如LockFilePermissions,如下所示:

CVE-2018-19134:通过Ghostscript中的类型混淆来远程执行代码

当从PostScript调用操作符时,传递的参数存储在op_stack中,通过直接调用这些函数并控制参数i_ctx_p。

我们尝试创建一个PostScript数组来伪造i_ctx_t对象,PostScript函数参数存储在i_ctx_p-> op_stack.stack.p中,这是一个指向参数的ref指针。为了使用伪造的上下文调用PostScript函数,我需要控制p。从p到i_ctx_p的偏移量实际上与op_stack的偏移量相同,即0x270。由于每个ref的大小为0x10,所以对应伪造数组的第39个元素。

CVE-2018-19134:通过Ghostscript中的类型混淆来远程执行代码

从上图可以看出,这种对齐并不理想,op_stack.stack.p对应数组的tas部分,我无法完全控制,如果只有op_stack对应的ref值,我便能成功控制。另外,tas存储了ref的元数据,即使我完全控制,也无法在不知道其地址的情况下将其设置为任意对象地址。因此,任何漏洞利用很可能只会在此时崩溃在Ghostscript中。

获取任意的读写原语

我想找到一个符合下列条件的PostScript函数:

1.引用osp(op_stack.stack.p)指针

2.使用osp指针做一些事情

3.可在SAFER模式下使用

我想到了pop运算,如下所示:

CVE-2018-19134:通过Ghostscript中的类型混淆来远程执行代码

它使用check_op检查堆栈指针的值,并将osp与指针osbot进行比较,如果大于osbot,则减小osp的值,这是一个简单的函数,并且不会取消引用osp。我们先仔细研究一下ref和op_stack的结构,如下图所示:

CVE-2018-19134:通过Ghostscript中的类型混淆来远程执行代码

回顾我们之前提到的东西,在我们伪造的对象中,op_stack是ref数组的第39个元素,如上图,字段tas对应p,而value对应osbot。type_attrs,_padd和rsize三个字段组合在一起形成op_stack中的指针p。如之前提到的,type_attrs指定ref对象的类型及其可访问性。通过pop函数,我可以修改选择对象的类型和可访问性。但问题在于,pop只有在p大于osbot时才有效,osbot是ref对象的地址。所以我篡改的对象需要是一个足够大的字符串,数组或字典。以便于rsize字段大于大多数ref对象的指针地址。我无法仅修改systemdict等内置只读对象的可访问性以获得写权限。但是,我可以做以下事情:

1.我可以使用将数组转换为字符串的方法,将内部引用数组视为字节数组,因为PostScript中的字符串不是由空字符终止,而是由rsize指定的长度。因此任何字节都可以从缓冲区读/写。值得注意的是,这样做并不会产生任何越界读/写,因为结果字符串具有与原始数组相同的长度,生成的字节缓冲区仅覆盖ref数组分配缓冲区的1/16。

2.我也可以反过来做,将一个字符串转换成一个相同长度的数组。如上所述,生成的ref数组将比原始字符串数组大16倍,这样我可以进行OOB读写,但我没有这么做。

我还需要克服另一个技术难题,伪造对象,pinst实际上调用了两个函数,如下所示,一个函数输出到另一个:

CVE-2018-19134:通过Ghostscript中的类型混淆来远程执行代码

如上所示,use_base_space的返回值是pinst-> type-> procs.get_pattern(pinst)。现在,我们把zpop(pinst)作为输入。当我使用任何内置的PostScript运算符替代uses_base_space时,可能导致产生空指针。使zpop返回0。我需要找到一个不使用上下文指针i_ctx_p的操作。

下面是我进行的查找代码:

CVE-2018-19134:通过Ghostscript中的类型混淆来远程执行代码

我的QL查询使用了一些启发式方法来识别PostScript运算符,它们的名称通常以z开头,并在psi目录中定义。它们也采用i_ctx_t 类型的参数,然后,我查找不会取消引用参数的函数,该函数不会调用自身且不包含i_ctx_t 类型的变量。

您可以在LGTM.com上的130,000+的GitHub,Bitbucket和GitLab项目上运行自己的QL查询,您可以使用在 线查询控制台 ,也可以安装 QL for Eclipse 插件并在本地快照上运行查询语句。Ghostscript不是在GitHub,Bitbucket或GitLab上开发的,因此LGTM.com尚未对其进行分析。 但是您可以在此处下载 Ghostscript代码快照

查询后返回了6个结果:

CVE-2018-19134:通过Ghostscript中的类型混淆来远程执行代码

如上图所示,ucache函数是我们所需要的。首先我们伪造pinst对象,如下所示:

CVE-2018-19134:通过Ghostscript中的类型混淆来远程执行代码

现在我们需要创建一个大型数组对象并将其存储在pinst的第39个元素中。它的元数据tas将被解释为堆栈指针地址osp。我将使用PostScript中的put运算作为第一个元素,然后使用pop将类型更改为字符串并读取zput函数的地址。

CVE-2018-19134:通过Ghostscript中的类型混淆来远程执行代码

不幸的是,数组的type_attrs值是0x4,而string的值是0x12。我必须使ushort下溢以从数组转换为字符串。所以我进行impl setpattern操作1291次。

CVE-2018-19134:通过Ghostscript中的类型混淆来远程执行代码

从上面的截图可以看出,伪造的数组被转换为字符串,我得到了zput的地址。我可以使用该方法将字节写入arr中的任何位置。

现在我可以从任意PostScript对象读取和写入任意字节。我最初的计划是覆盖LockFilePermissions参数,然后调用file,允许任意命令执行,如同 CVE-2018-19475 中的操作。然而,事实证明,我还需要伪造许多对象。这使工作变得复杂。相反,我仅仅想要调用一个简单但功能强大的函数,forceput操作能够满足我的需求。

总结一下,我需要做的事情:

1.使用forceput提供的参数创建一个伪造的操作数堆栈。

2.覆盖pinst中的地址,并将操作数堆栈指针的地址存储到我上面创建的堆栈中。

3.获取forceput的地址,并替换pinst-> type.procs.getpattern的值。

为了实现1,我只需要用我的参数创建一个数组。如下所示:

CVE-2018-19134:通过Ghostscript中的类型混淆来远程执行代码

然后我可以将它存储在arr中以检索此数组的地址。重用pinst并将其放在第31个元素中:

CVE-2018-19134:通过Ghostscript中的类型混淆来远程执行代码

使用上一节的技巧,我现在可以读取这个伪造堆栈指针的地址并将其写入pinst中的适当位置。我可以简单获取zput地址,并将其偏移量添加到zforceput以获取zforceput的地址(因为此偏移量不是随机的)。在使用commit 81f3d1e编译的调试二进制文件中,此偏移量为0x437,在9.25版本中,偏移量为0x4B0。执行此操作后,我可以将LockFilePermissions参数写入当前设备,然后运行任意 shell 命令。如下所示,是从Ghostscript中启动计算器的示意图。

CVE-2018-19134:通过Ghostscript中的类型混淆来远程执行代码

通过覆盖userparams中的其他字段,如PermitFileReading和PermitFileWriting,还可以获得任意文件访问。至此,漏洞利用结束。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Producter 让产品从0到1

Producter 让产品从0到1

周楷雯 / 人民邮电出版社 / 2016-12-25 / CNY 69.00

这是一本以App Store首页推荐的成功App为例阐述如何完成一款App产品的设计、开发和营销的书。在这本书之后,作者的《一炷香》和《字里行间》两款产品也接连被App Store首页推荐。 《Producter 让产品从0到1》从产品的设计、产品的实现、产品的迭代、产品的营销、产品的进阶等几个角度,全面讲解了产品设计的基本原则、设计的重要性、设计的感觉、实用的设计工具、简单的iOS开发、产......一起来看看 《Producter 让产品从0到1》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具