0x00
Protostar涉及栈溢出、堆溢出、格式化字符串漏洞、网络编程、及综合性漏洞。
本文将介绍format部分。
关于环境准备,在官网https://exploit-exercises.lains.space/protostar/下载即可。下载后得到iso镜像,使用vmware安装。然后使用user/user即可登录
查看ip
知道ip后可以在kali中ssh连上
输入user即可
机器上所有需要分析的程序都在如下路径
0x01
第一关,fotmat0
我们的目标是控制target为0xdeadbeef,才能打印出成功的语句
源码中可见关键点在于sprintf,sprintf()将不会检查提供的输入是否大于缓冲区大小或预期的格式字符串。
Man sprintf可以看到如下提示
可见格式化字符串漏洞的威力还是很大的。
gdb调试
在0x08048416我们可以看到在比较eax与0xdeadbeef,如果不等则跳到0x08048029,如果相等则跳到0x08048330,打印成功的语句
通过利用sprintf以及分配的64字节buffer,我们可以通过缓冲区溢出控制eax,我们需要确定偏移量以覆写eax值为0xdeadbeef
使用gdb调试,下两个断点
第二个断点是下在cmp之前
然后测试
run $(python -c ‘print “A” * 60 + “BBBBCCCCDDDDEEEE”’)
然后创建gdb hook
上图的这几条命令分别用于打印寄存器信息、打印esp之后的10个16进制数据,打印eip后的三条指令
然后continue
在上图中0x41414141可以看到我们的payload了
并可以看到之后的三条指令
输入si,单步执行
可以看到eax已经被溢出了
下一条指令就是cmp了
我们可以打印出当前的eax值
可见当前eax已被覆写为我们payload中的cccc了
也就是说buffer的偏移为60(60个A)+4(4个B)=64
我们只需在64偏移后加上0xdeadbeef即可,注意是小端格式,所以应该是\xef\xbe\xad\xde
测试如下
成功了,不过仔细看,我们其实这次的攻击使用的栈溢出的技术,没有用到格式化字符串攻击。为了契合题意,可以使用格式化字符串参数%s
一次简单的格式化字符串漏洞就成功了
0x02
第二关,format1
从源码中可知,这里的漏洞出在printf,它会接收哦任何输入,而不会检查提供的输入是否为所期望的格式字符串。我们要做的就是验证是否可以通过此处的printf泄露内存地址、写数据到堆栈等。
首先使用gdb调试,根据个人喜好,可以如下所示将汇编代码显示为intel格式
然后反汇编vuln函数
从汇编中可见,在0x08048400处调用print,打印用户的输入
在0x08048405处将ds的地址移动到eax寄存器(ds即data segment)
之后就是典型的test eax,eax;je xxx;表示的是如果eax中的值为0,则跳转到xxx
放在这个题目里,如果eax为0,则跳到0x0804741a;否则继续向下,来到0x08048415,调用puts打印出成功的语句
可见,其实最关键的就是这条指令:mov eax,ds:0x8049638
该地址我们猜测就是”target”变量的地址,如下操作可以证实我们的猜测
转成小端格式就是\x38\x96\x04\x08
接下来我们就要弄清楚如何通过printf写入该地址,本题只要将target所在地址写为不是0的任意值即可
下面是首先输入8个A打印140个以16进制形式占8位的数据,我们从输出中观察A的位置借以大概确定偏移
/opt/protostar/bin/format1 python -c "print 'AAAAAAAA' +'%08x.'*140"
从上图看到,偏移大概在129左右
因为涉及到内存对齐等操作,这并不是很准确,我们带入target的地址来进行微调,直接用129
可以看到地址并没有对齐;所以在加上一个%08x
还是没有对齐,继续加一个%08x
现在对齐了
接下来我们要做的事情就是将倒数第二个%08x,也就是现在target地址所在的地方,改为%08n,这样就可以在该地址上写入数据
/opt/protostar/bin/format1 python -c "print '\x38\x96\x04\x08BB' +'%08x.'*129"
%08n.%08x
可以看到成功修改了target的值,并且打印出了表示成功的语句
0x03
第三关format2
这一关相比于前一关,是要求我们将target变量覆写为具体的值,而不是任意的值
找到target的地址
改写为小端序是这样子的:\xe4\x96\x04\x08
接下来我们需要确定偏移
可以看到偏移为4
Target地址共4字节,再写60字节。即一共写入64字节。然后通过%4KaTeX parse error: Undefined control sequence: \xe at position 77: …hon -c 'print "\̲x̲e̲4\x96\x04\x08" …n"’ | ./format2
测试如下
0x04
第四关format3
本题在上一题基础上,要求我们写入更多的特定数据。
我们需要将target变量覆写为指定的四字节,即0x01025544
先挂载gdb
反汇编vuln的结果
0x0804848a处获取用户输入,0x08048498处打印用户输入
0x0804849d处将ds(data segment)处的数据移到eax,之后通过cmp进行比较,如果不等,则跳到0x080484b7,如果相等则继续向下执行,来到0x080484b0会打印成功的语句。
我们先简单看看程序是怎么运行的
python -c ‘print “AAAA”’ | /opt/protostar/bin/format3
如果加上%08x就可以泄露内存地址
python -c ‘print “AAAA” + “|%08x” * 2’ | /opt/protostar/bin/format3
如下所示
%x的意思是说以16进制形式表示,是格式化字符串的一个参数。08表示占8位,会将%x的输出填充到8位,相当于4字节长度
接下来我们需要找到偏移,我们使用更多的%08x,看看我们输入的AAAA会在哪里
python -c ‘print “AAAA” + “|%08x” * 12’ | /opt/protostar/bin/format3
可以看到偏移为12
接下来找到target变量的地址
表示为小端序为\xf4\x96\x04\x08
0x01025544的小端序为 \x44\x55\x02\x01
我们先给出exp,然后再解释
python -c ‘print “\xf4\x96\x04\x08\xf5\x96\x04\x08\xf6\x96\x04\x08” + “%56x%12n%173x%14n是用于将0x44写到target变量的最低有效位(通过将printf()的内部指针移动12个偏移实现)
%17x则是在68字节基础上再加上17字节,共85字节,85的16进制表示为0x55,%13KaTeX parse error: Undefined control sequence: \x at position 23: …5写到偏移为13的位置。
这样\̲x̲44\x55都写好了。还剩\x…n.0x00000102的10进制为258,我们已经写了85了,所以还需写258-85=173,所以用的是%173x。
至此,exp已经解释完毕,我们来看看效果
python -c ‘print “\xf4\x96\x04\x08\xf5\x96\x04\x08\xf6\x96\x04\x08” + “%56x%12n%173x%14$n”’ | ./format3
0x05
第五关,format4
这一关要求我们控制程序执行流程,在main中只调用了vuln,但是题目要求我们执行hello()
这里的我们采用的技术为GOT覆写
GOT表中包含了各类函数、变量的地址,在程序中用到时都会来到GOT表中查询,这里我们的思路是将vuln()中的exit()地址覆写为hello()的地址,这样执行完vuln后就可以执行hello(),我们也就是控制了执行流程了。
题目提示我们可以使用objdump
从上图中看到GLIBC导出了exit(),用于在vuln()中使用,可以看到exit的地址为08049724
再使用objdump查看hello的地址
可以看到,hello的地址为0830484b4,我们现在要做的就是用080484b4覆写08049724
在这里,同样先给出exp,再解释
python -c ‘print “\x24\x97\x04\x08\x25\x97\x04\x08\x27\x97\x04\x08” + “%168x%4n%132x%6$n”’ | ./format4
exp涉及到三个地址 0x08049724, 0x08049725 和0x08049727。我们相应的在其中分别写入0xb4, 0x0484 和 0x0508,这样就可以实现080484b4覆写08049724
具体而言,是这样子的:
三个地址共12字节,再写168字节,一共180字节,16进制表示为 0x000000b4,我们将其写到了0x08049724,由于是小端序所以实际上是写入了 \xb4\x00\x00\x00
再写976字节,976+180=1156,表示为16进制为0x0484,将其写到 0x08049725,此时我们从地址0x08049724看过去就是这样的: \xb4\x84\x04\x00\x00.
再写132字节,132+976=1288,16进制为0x0508,写到0x08049727,则从0x08049727看,是这样的:0x08050000,而从0x08049724看是这样的:\xb4\x84\x04\x08\x05\0x00\x00
而\xb4\x84\x04\x08正是hello()地址的小端序表示
至此,我们就成功用hello()的地址覆写了exit()的地址,这样,当vuln()运行结束要调用exit()时,实际就来到了hello(),我们也就成功劫持了程序执行流
测试如下
0x06
参考:
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/