0x04 flag
通过反汇编工具IDA PRO打开
文件打开入口看起来很奇怪,东西很少,可能是加壳过的,在linux环境下用upx –d flag命令脱壳,脱壳后文件大小从300+k到800+k,此时再用IDA打开,通过View->Open subview->String查看字符串得到flag或者跟进flag找到字符串
0x05 passcode
#include <stdio.h> #include <stdlib.h> void login(){ int passcode1; int passcode2; printf("enter passcode1 : "); scanf("%d", passcode1); 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"); //两个函数挨着,用的同一个栈底(ebp) welcome(); login(); // something after login... printf("Now I can safely trust you that you have credential :)\n"); return 0; }
思路:
1. 在login函数中在调用scanf函数输入passcode变量的时候,没有加&符号,后果是在调用scanf的时候,程序会从栈上去4个字节作为passcode变量的地址,将我们输入的整形放入到这个地址中,也就是说我们实际输入的数据就不一定保存在栈上了,但是这两个passcode的地址一定在栈上。
2.再看welcome函数输入100个字节,看似没有什么漏洞可以利用,但观察主函数里,login函数和welcome函数的调用紧挨着且welcome函数在前,这就导致这两个函数的函数栈帧是同用一个栈底(ebp)的,所以调用welcome函数输入100个字节后,残留下来的数据可以发挥作用。
3.我们用gdb调试,来查看passcode1和passcode2到底是用的栈上哪个地方的数据做地址
如图,这是第一次调用scanf,因为passcode1没加&符号所以用的是(ebp-0x10)这个地址上存放的4个字节的数据作为地址
如图,这是第二次调用scanf,passcode2也没加符号&,所以用的是(ebp-0xc)这个地址上存放的4个字节数据作为地址。
再看welcome函数里数组name的首地址为(ebp-0x70)
所以调用login函数时,栈上的数据分布如下:(灰色为上个栈帧的残留数据)
其实正常来说根据函数中定义变量的先后顺序,应该是passcode2在相对较低的字节(先定义先分配栈空间),也就是说原来这两个变量的值是保存在栈上的,而后面的if()语句正是用这两个栈空间内的值来判定,scanf没加&只是导致我们把内容输入到别的地址去了。所以能不能直接覆盖passcode1和passcode2在栈上的内容,结果是不行的。原因有两点:
1) passcode1和passcode2定义了之后就没有其他动作了(比如赋值或者其他操作)这样我们在gdb反汇编它就没办法确定它们的位置,上图的两个passcode地址是由于scanf()的使用不当引起的把栈上数据内容当地址的行为,那两个地址并不是login函数一开始定义的两个变量的地址,后面if的判断也不是用的图上两个地址的数据
2) 其次即使能确定地址,welcome中的name数组的输入是没有任何漏洞,所以如果需要覆盖的数据超出了100字节的返回,在数组外的区域没有办法覆盖,比如上图,虽然两个变量是紧挨着的但有一个在数组外,想靠数组覆盖无能为力。
所以我们采用got表覆写的方式:
1.用一个got中的函数地址去覆盖scanf取的地址也就是上图passcode1的地方
2.再用执行system(‘/bin/sh’)这条指令的地址通过scanf写入到1.中的地址去
下一次执行到这个got表中函数时,正常情况是跳到got表地址读取指令,而此时指令以及被我们覆盖,就执行system(‘/bin/sh’)了
注意不论是name数组的构造,还是scanf这个漏洞导致覆写got表的行为,本质都是在构造,真正触发漏洞的是这个got表函数在程序里第一次加载的时候,调到got表的这个行为。
之所以有两个\n是因为对应两次输入的回车代表输入结束。