0x00
Protostar涉及栈溢出、堆溢出、格式化字符串漏洞、网络编程、及综合性漏洞。
本文将介绍stack部分。
关于环境准备,在官网https://exploit-exercises.lains.space/protostar/下载即可。下载后得到iso镜像,使用vmware安装。然后使用user/user即可登录
查看ip
这题很简单,就是在if里判断modified变量是否被修改,而题目可以让我们合法输入的只有buffer的64字节,不过由于使用的是gets,可以通过溢出64字节的缓冲区,比如65字节,这样64个字节在buffer中,1个字节在modified中,只要其值不是0,就可以了。
测试如下
先输入64个字节,可以会try again,输入65字节,则成功修改了modified的初始值0
如果输入更多,可能还会溢出到其他空间如返回地址等,所以容易造成程序崩溃,比如输入200个
从源码中看出,这一关在上一关基础上,不仅要求modified被修改,而且明确了要修改为0x61626364。这一关虽然使用了strcpy,但是和gets一样,没有校验边界,所以还是可以造成溢出
可以先试试
输入64个a的时候是刚好把buffer填满了,输入65个a时,多出的1个a是填到了modified的空间中
这里注意到第65个a是第一个写到modified空间的(modified为int类型,共四字节)但是从打印的结果中看到这个a却是在最后的位置,这是由于该机器采用的小端序
所以既然从源码中看到它让我们把modified填为0x61626364,即abcd,所以我们在输入时的顺序应该是相反的,即dcba,即如下所示
这是在stack1上改进的。主要有两点区别。
第一点,这次的strcpy是将variable复制到buffer,而上一关是argv[1]复制到buffer。而variable来自环境变量。第二点,这里要求将modified修改为0x0d0a0d0a,而0x0d是回车符,0x0a是换行符,我们没办法像针对0x61一样直接输入a就可以。
第一个问题,我们手动设置环境变量就可以;第二个问题的话,对于python而言,如果我们输入为\x0d这种格式的话输出也自然为相应的16进制
所以如下就可以解决该问题
这里的话,要求我们控制程序执行流,能够调用win函数,很明显这就要求我们覆写eip指针。
首先借助objdump找到win函数在内存中的地址
这里的-x选项是让objdump列出所有函数的地址,配合grep,只列出win函数的地址
这里可以看到地址为0x08048424
接下来我们就需要用fp指向该地址即可,由于小端序的原因,所以如下所示
这一次没有任何变量可以让我们覆写去调用win函数,那么我们这次要想调用win函数的话,就需要覆写main函数的返回地址,eip。这个地址存在栈帧的最开始的地方,接下来就需要知道溢出buffer多少字节才能覆写eip。
这就需要一次次尝试
在传入76字节的时候报错了,可知76字节之后的区域就是用于覆写eip的
接下来还是同样的方法找到win函数的地址
这里报错的原因是因为原返回地址被我们覆写了,导致程序不能正常退出。
这次要求我们直接拿到shell,直接用现成的shellcode:https://www.exploit-db.com/exploits/13357,作用相当于execve /bin/sh
基本的布局还是和之前的一样,还是需要76字节+4字节,这4字节是要覆写eip的内容
先gdb载入
可见,buffer从esp+0x10开始
先确定是否能控制eip,gdb调试,输入76个a,4个b
可以看到确实可以控制
这里有一个问题,就是我们不知道esp+0x10的确切地址,直接使用gdb给出的地址是不准确的
从之前的汇编中我们看到,在执行过程中,我们要跳转的内存地址实际被载入了eax寄存器中,所以如果我们可以定位到jmp eax指令的位置,我们就可以将eip指向该位置以执行我们的shellcode
找这种gadget很简单,由于protyostar近乎裸机,我们用python开个简单的http服务,然后将stack5下到kali上
我们将BBBB的替换为0x080483bf,当然这里要注意小端格式。
shellcode不足76字节,所以需要补上一些字节,这里用A补上
然后进行测试
接下来看看报错原因
经过调试可知是由于shllcode中的push导致在栈空间中写入了其他内容
为此,我们可以使用add $0x10, $esp再开辟一些空间,其对应的机器码为\x83\xc4\x10,所以相应地,payload中的A少填充3个。如下所示
这里多了一个限制,在上图中可以看到这里将返回地址进行了校验,相比于上一关又增加了难度
gdb载入后反汇编getpath函数
0x080484aa处的gets用于接收我们的输入
0x080484b8到0x080484c2的指令就是用于校验返回地址
如果不等,则跳到0x084884e4,走到0x080484f3,打印出got path…
如果相等则继续走到0x080484d3,打印出bzzzt…
这里来看看这个与的操作
假设跳到一个地址0xbfffff01
相与之后:
可以看到只要地址以bf开始,经过与操作后,都会与0xbf000000相等,继而就会打印出bzzzt…
这就是这道题目给的限制
也就是说无法使用在栈的地址(因为都是以0xbf开头)针对这道题目,我们这里使用ret2libc技术(后面可以看到是以0xb7开头)
首先要找到偏移
0x45,44分别是E和D
所以可知偏移总共为70+”BBBBCCCCDD”=80
这样就能控制eip了
接下来要找到libc地址
gdb查看内存分布
这里的-a是扫描整个文件,-t x是打印出16进制形式的偏移
所以/bin/sh地址为0xb7e97000 + 0x11f3bf
所以exp如下
和stack6类似,这次是限制我们使用0xb开始的地址作为返回地址,也就是说ret2libc的方法也失效了。
如下所示
这次我们绕过的方法是ret2.text
既然我们不能跳到栈上和libc地址上,那么这次我们跳到程序的.text段上,此处放着程序的汇编代码,并且利用特殊的gadget实现目的
首先还是要确定偏移以控制eip,这一步和stack6一样,偏移还是为80字节。
接下来就是找gadget,还是同样的办法,先把程序传输到kali上
在protostar上 :
使用哪一条都可以,本质上都是一样的,即pop,pop,ret
所以现在我们知道了
padding为A*80
EIP为0x080485c7
POP随意
POP随意
RET=我们希望返回的地址
这里还是与stack6一样,用system调用/bin/sh,相关的地址还是同样的做法就可以找到,这里不再重复,源码如下
0x09
参考:
1.https://exploit-exercises.lains.space/protostar/
2.https://medium.com/bugbounty/
3.https://medium.com/@airman604/
4.https://medium.com/@coturnix97/exploit-exercises-protostar/