wdb2018_guess writeup
buu刷题记录:wdb2018_guess
静态分析elf文件
checksec一下发现开了NX和canary保护,64位程序,用ida分析看看
很明显的gets栈溢出,并且gets之后没有回显输入的内容,那这样的话意味着不能栈溢出劫持控制流了。那么看看前面,发现有一个把flag文件内容读到栈上面的动作。后面while循环调用了一个sub_400A11函数,v7=3,v6=0,,v7>=v6 break就意味着这个函数会被调用三次。而发现sub_400A11函数是调用fork函数,fork函数会创建和当前进程一模一样的进程,然后范围自身进程的ID,如果是被创建的进程,那么fork将会返回0。
简单点就是fork的子进程会返回0,如果返回0那么break执行下面的内容,父进程因为返回自己进程号不会break则继续循环,然后调用fork,也就是说这个进程一共会产生3个子进程执行while循环之后的内容。
这里有一个特性,那就是在发生栈溢出之后stack_chk_fail。在终止程序之前还会打印argv[0],这一点很合理。但是这里可以用于泄露栈上的flag,我们如果gets覆盖到argv[0],就可以任意地址泄露了,但是因为只有三个子进程,就只能泄露三次,前两次一定要把栈地址泄露出来。但是呢现在栈地址是不知道的,我们可以先通过一次泄露泄露出libc的地址,二次泄露出栈地址,最后一次泄露flag。其实这里还有一点不太理解,就是fork之后栈是共享的嘛?libc是共享的很好理解,栈共享就不是很好理解了,还是说它栈不同,只是对应偏移的地方都有flag,然后我泄露的是别的进程上面的flag,倾向于这种解释。
动态调试确定偏移
现在就需要知道这个buf到底需要多少字节能覆盖argv[0],所以需要动调一波。断点下在fork循环之后,这里我选择了0x400b0d的位置,r。为了确定buf的位置,我们选择gets读入很多个a看看它与argv[0]之间的偏移。
很容易可以从左边看出来,他们之间差了0x128个字节,那么payload就是0x128*b’a’+p64(要泄露内容的地址)。
第一次拿下libc之后第二次应该要拿__environ,这个我也是看writeup知道的,这个好像就是指向argv[0]的地址。也就是栈上的地址,这个是在libc里面的,所以拿到libc之后就可以泄露这个,然后再观察泄露的地址和flag的地址差多少,确定好偏移之后第三次就可以直接泄露flag了,这里测试之后是0x168的偏移,泄露的地址再减去0x168就是我们想要的flag了。
exp
1 | from pwn import * |
成功getflag