简单缓冲区溢出
信息安全课的一个作业是拿到一个大概是用C写的exe
文件,然后用缓冲区溢出跳过其中的字符比较,用了OllyDbg
在虚拟机中实践了一下,感觉起来很简单。实际上也确实不难
本文在 Windows XP
虚拟机上进行。
我不确定在Windows 10 内是否依然能做到这些,然后又发现半年前VirtualBox 留了一个分布式对象实验的虚拟机,所以正好可以使用已经配置好的分辨率和共享文件夹。
我们拿到的题目是一个 Overflow.zip
的文件夹:
1 | E:\Downloads\overflow>dir /B |
bo2.exe
就是我们的目标了。bo.jpg
中给了一张图片,显示了密码,但是这个图片所显示的内容在ida
查看之后并不是完全正确的。(有一些常数并不一样)
从图片中能读到的东西是它的密码是 654N321S
。输入这段字符,程序会输出一个Serial number is correct.
。
1 | C:\Documents and Settings\Ciaran\桌面\overflow>.\bo2.exe |
在随便输入一些字符可以见到,这个程序的逻辑是直接退出了。
然后我们的目的是要跳过这个所谓Serial Number
的字符的比较,直接输出Serial number is correct.
。
确定漏洞存在
在输入一定数量的字符后,发现输出了Error! Input must be < 100 characters.
1 | C:\Documents and Settings\Ciaran\桌面\overflow>.\bo2.exe |
让我们给多更多的字符,看看会发生什么。
1 | C:\Documents and Settings\Ciaran\桌面\overflow>.\bo2.exe |
程序没有给出任何的结果,就直接退出了,由此可见,漏洞是存在的。
我们还是来想一个简单的方式来获取输入吧,完全手动输入实在有点麻烦了:
1 | E:\Downloads\overflow>python -c "print('a'*200)" | .\bo2.exe |
在虚拟机外的主机上,我们是有Python
的存在的,那么就可以通过使用python的程序生成大量的字符,然后通过管道输出给这个程序。不过因为我们是在虚拟机中进行这样的操作,所以我们是首先将python输出的内容重定向到一个文件中,然后再在虚拟机中将这个文件重定向到bo2.exe
程序的输入。
动态调试工具
首先下载一个OllyDbg
,所使用的是52pojie
提供的版本:在这里下载。
然后在虚拟机中打开bo2.exe
,可以看到这个程序的二进制代码(CPU界面)。在这界面的左下角的面板中可以看到,内存地址中就有那几句话:Serial Number is correct
。实际上如果没有,就在左下使用右键-> search -> binary string 进行搜索也能找到这段话。可以看到这段话的地址:00408030
。
然后再上面的汇编代码的面板中搜索这个地址:右键 -> Search for
-> constant
,然后搜索得到的结果就是在调用这段话的附近。实际上,因为这是用C语言编译器编译的程序,所以其主函数的代码都会在00401000
附近,直接跳到这里也就好了。
现在我们看到了整个程序的结构,如果写成C的话大概是这样的。其中N是一个常数。
1 |
|
缓冲区溢出
缓冲区溢出的原理就是简单的输入数量足够大的字符,然后覆盖程序的返回地址,也就是EIP
寄存器。然后让我们再来做一次我们刚才所做的事情,也就是输入足够多的a
,然后看看结果。
1 | python -c "print('a'*300)" > temp1.txt |
这个时候程序理所当然的崩溃了,崩溃的原因是EIP这个地址不可读,因为这个时候EIP的地址已经被我们的aaaa
给填满了,它的地址就是61616161
也就是四个a
的ascii码。现在,通过缓冲区溢出,我们已经能操控EIP
也就是程序跳转的方向了。
然后根据我们的目标,我们需要将这个EIP
指向我们想让它出现的语句,也就是push bo2.00408030
。这是在调用printf
函数之前的装载使用参数。在这之后就是调用printf
了。而这句话的地址就是00401060
,所以我们现在需要将EIP
给覆盖成00401060
即可。
这个时候当然其实本来应该是进一步通过不同的输入,找到具体是哪一个位置的aaaa
变成了EIP
的值。但是我比较蠢,懒得花费这种智力,所以就莽了过去:
因为python的print会自动以人类可见的格式输出,\x00
就真的是'\x00'
这样输出了,所以得使用sys.stdout.buffer.write()
1 | python -c "import sys; sys.stdout.buffer.write(b'\x00\x40\x10\x60'*100)" > temp2.txt |
简单说一下,因为这是四个完全不相同的字符,因为EIP只有四个字符的位置,然后我只要确定这四个字符的相对位置,补出相应的前缀,这样总能保证EIP被正确地填上。
1 | C:\Documents and Settings\Ciaran\桌面\overflow>.\bo2.exe <temp2.txt |
然后发现并没有取得相应的效果。(这就有点尴尬)
然后再次用OllyDbg
发现EIP
的值为\x60\x10\x40\x00
,确实是我们给出的字符,没有位移,但是看来顺序反了。这是端序的问题,导致结果完全不一样。不过不会有什么影响,再来一遍逆序的即可。
1 | python -c "import sys; sys.stdout.buffer.write(b'\x60\x10\x40\x00'*100)" > temp3.txt |
然后就能得到正确的结果:
1 | C:\Documents and Settings\Ciaran\桌面\overflow>.\bo2.exe <temp3.txt |
这样就直接绕过了所谓序列码直接给出了结果。