pwnable.kr-login
1.常规checksec一下,开了canary和NX,然后IDA打开分析漏洞。发现auth函数中可能存在栈溢出:
1 | #注释头 |
如果a1大于8h,而我们可以控制input,那么就可以造成栈溢出。再往上翻一下,发现就是将我们的输入通过一系列操作给到input,然后a1是input的长度。
实际情况是将我们的输入给s,进行Base64解码,然后给v4,长度给v6。v4又给input,v6传值到达auth函数赋值给a1。这里input是全局变量,所以auth函数中的input中的内容其实就是我们输入经过base64解码的内容。
1 | #注释头 |
▲题外话:最开始Base64搞不懂哪个是输入,哪个是输出,直接经过调试就可以判断。况且最开始的v4是0,总不能程序永远都将0进行base64解码然给到我们的输入地址中吧。但是调试的时候发现,每次输入相同的值,但是解码后得到的v4的值却是不一样的。这就纳闷了,为什么一样的输入四个AAAA得到的解码值不一样呢,难道程序还有个随机变量不成。之后再仔细调试发现这个base64decode有点不一样,虽然传入的两个参数都是地址,但是第一个参数的操作却是从该地址直接取值进行解码,然后对于第二个参数的操作却并不是将解码结果给到第二个参数,而是再开辟一块堆内存,之后将该堆内存的地址给到第二个参数。所以每次解码后第二个参数,也就是栈上的一个值,总是不一样,因为这里保存的是一个随机生成的堆地址,而不是解码后的值。同样之后观察main函数中的memcpy也可以发现:memcpy(&input, v4, v6);而memcpy的原型是:
1 | #注释头 |
前两个参数类型都应该是地址才对,而这里却直接将v4的值给传进去,那不就说明v4的值是一个地址吗。然后再跳转到汇编代码分析一波:
1 | #注释头 |
同样Base64Decode函数的两个参数也都是地址,这里是直接取栈地址给到eax,然后再将eax的值给相应esp指向的栈内存。所以可以看到Base64Decode取值应该是从栈上取两个地址才对,分别位于main函数栈的是esp+4和esp。所以如果这里有个格式化字符串那么就完全可以泄露处出栈地址,之后就完全可控,可惜没有。还是回到正轨分析吧。
2.所以经过前面分析,程序要求我们输入一个base64编码过的字符串,随后会进行解码并且复制到位于bss段的全局变量input中,最后使用auth函数进行验证,通过后进入带有后门的correct()打开shell。并且由于长度有限制:所以我们的输入base64解码后最多只能有12个字节。
1 | #注释头 |
3.汇总一下,程序存在栈溢出,但只能溢出4个字节,也就是一个地址,也就是最多只能覆盖到ebp,然后存在后门函数。由于没办法直接覆盖返回地址,所以这里就在ebp上做文章,使用栈劫持技术。之前的栈劫持可以用rop,但是这里没办法,因为无法进行返回地址覆盖。但是还有一个地方,就是我们的输入最后会被解码赋值给input,这个input是个全局变量,不受到ASLR影响,而又可以控制12个字节,如果可以把栈挪移到这个地方,那么就是可控了。
栈模型如下:


可控栈如下:

4.总体思路应该是:
①劫持auth函数的栈底到input_addr,那么auth函数在退出到main函数时,main函数栈的栈底就不会回到之前的main函数栈栈底,而是会挪移到我们input_addr,也就是payload3的值。
②开始执行auth函数中的退出操作,到leave时,执行操作leave的第一步汇编操作mov esp ebp,将栈顶指向ebp指向的内容,此时ebp已经被修改成了payload3,而payload3会被赋值成Input_addr,也就是esp会指向input_addr。
②执行leave第二步汇编指令pop ebp,将当前栈顶的值赋值给ebp,也就是ebp的值会变成payload1,(这里的payload1没什么作用,可以随便填)之后esp由于pop,esp+0x4,会往栈底移动一个地址,移动到指向我们输入的payload2处。

④之后retn执行,实际指令为pop eip,也就是将当前栈顶数据给eip,也就是eip被赋值为我们payload中的payload2。

⑤最后执行retn的第二条实际指令:jmp eip,此时eip就已经是payload2的值,所以将该payload2设置为correct函数地址或者是system(“/bin/sh”);就可以getshell。
总的来说,就是利用leave和retn两个操作来劫持eip的值,使其直接指向后门函数,一步getshell。
5.创建payload,组成应该是:
1 | #注释头 |
之后确定payload的组成:
payload = padding + eip + input_addr。
1 | #注释头 |
最后得注意发送的是base64编码之后的payload。
参考资料:
https://bbs.ichunqiu.com/forum.php?mod=collection&action=view&ctid=157