readme_revenge 复现
1 | ➜ 桌面 ./readme_revenge |
1 | readme_revenge: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=2f27d1b57237d1ab23f8d0fc3cd418994c5b443d, not stripped |
64位,statically,开了NX,有canary,Full RELRO
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
遇到了 statically 的程序(还挺少见的)
入侵思路
我的第一反应是它可能魔改了 “_isoc99_scanf” 或者 “printf”,这个题目看上去无懈可击,找不到任何的突破口
不妨开始一波逆向分析,想想题目作者把 flag 交到我们手里的手段:
- 用 system 或者 execve 获取 shell
- ORW直接读 flag
常规的就这两种,因为这是 statically 程序并且没有 system,execve,所以直接排除第一种可能,那么程序就一定在某个位置写了“flag”字符串(用于open函数)
其中这一句比较可疑:
1 | WARNING: Unsupported flag value(s) of 0x%x in DT_FLAGS_1. |
看起来像是直接告诉我们 flag 一样,但是很可惜没有发现目标(这个字符串是某个函数自带的)
常规获取 flag 的思路看来解决不了问题,当时我就在想是不是像逆向一样直接把 flag 写死在文件里?一般比赛的 flag 都以比赛名开头,所以我直接搜“34C3”:
1 | .data:00000000006B4040 public flag |
发现 flag 了,因为程序开了 canary ,可以劫持 stack_chk_fail 的打印信息来泄露 flag
1 | RDI 0x48d187 ◂— imul rbp, qword ptr [rax], 0x202e7325 /* 'Hi, %s. Bye.\n' */ |
这就需要另一种攻击技术 House Of Husk 的帮助了:利用方法就是我们这里的 printf 注册函数的调用链,伪造 __printf_arginfo_table ,将 table['s'] 改为 _stack_chk_fail_local 地址,将 __libc_argv(存放系统路径的地方) 改为输入地址,在输入开始存放 flag_addr
1 | v14 = *(const char **)_libc_argv; |
这道题可以直接覆盖 printf_arginfo_table 和 printf_function_table 不需要借用 main_arena,想比常规的 House Of Husk 更为简单
先看exp吧:
1 | from pwn import * |
这里就是把 input_start_addr (输入的起始地址) 当成 printf_arginfo_table
小结:
目前还是有点不懂该漏洞的运作原理,抛开漏洞利用不谈,我测试了正常情况的 printf_arginfo_table 和 printf_function_table ,发现它们都是NULL,所以我有点搞不懂这两个表在 printf 中的作用
不过程序可以打通是事实,我也只能认为程序会优先执行 printf_arginfo_table 里面所指向的解析函数了(虽然我在源码中没有找到依据)