Doubly Dangerous
扔进IDA,F5查看伪代码,我们看到main函数里有两个变量,一个数组和一个单精度浮点数,一个gets函数。
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
分析代码易知当v5等于11.28125时,就会调用give_flag函数,然后得到flag。于是我们的思路就是通过gets函数,使数组s的值覆盖掉v5的值,使v5等于11.28125。
那么通过IDA远程调试,断点就设置在main函数即可,然后单步调试,观察栈中的情况,我们发现v5最开始被赋值为零,记录下v5在栈中的位置:
继续执行,找到数组s在栈中的位置:
我们要做的,就是在s中填充适当字符,覆盖掉v5为我们想要的。于是这两个栈中的地址相减,得到两者相距64个字节,那么就需要64个字节的字符填充,然后后4字节覆盖v5成11.28125。由计算机组成原理的知识,32位系统,小端,float型数据,得到11.28125的机器码为0x41348000,于是构造payload:64*'a'+'\x00\x80\x34\x41'
通过pwntools最终得到flag:
Quals-Warmup
扔进IDA,查看伪代码
1 | __int64 __fastcall main(__int64 a1, char **a2, char **a3) |
查看sub_40060D
1 | int sub_40060D() |
分析代码可知,这个程序其实运行的时候,就已经把得到flag的函数地址打印出来了,所以要做的就是栈溢出,使返回值变为sub_40060D。然后查看v5在栈中的位置,使RBP-40H,所以保存的返回地址的起始在栈中的地址为RBP-40H-8H(RBP旧值)-8H,那么payload构造为payload = 72*'a'+p64(0x40060d)即可。通过pwntools得到flag:
sCTF 2016 q1-pwn1
扔进IDA,分析伪代码,发现main函数很简单,只调用了一个vuln函数,进入这个函数分析:
1 | int vuln() |
分析代码加上运行这个程序,可以知道是返回输入,然后看代码中v5是you,v7是I,然后有个replace函数,猜测是将输入中的I全部替换为you,测试一下,发现确实是。然后看输入的s,距离ebp有3C,然而fgets允许输入的最长字符串为32,所以不可能通过直接输入导致栈溢出。我们看到代码的最后,有个strcpy函数,分析可知,是将替换后的字符串给了v1,然后将v1拷贝到s中,于是乎,我们的目标就是通过替换后的字符串,使栈溢出。所以我们需要输入21个I,然后再任意输入一个字符作为payload的占位部分,然后加上get_flag的地址。exp如下:
1 | from pwntools import * |
just_do_it
扔入IDA中,分析代码:
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
发现代码是让你输入密码,查看success_message,发现就是一个输入成功的字符串,而不是flag。从代码中可以看到,代码使用fgets读取flag,保存到全局变量flag中。我们输入是在s,但是s距离ebp有20H,而fgets只允许有32个字符的输入,明显不能直接栈溢出。于是继续分析。我们发现最终输出了v6,v6距离s只有20个字符,于是我们可以覆盖v6为flag的地址,通过puts打印出flag。exp如下:
1 | from pwntools import * |