Overview
return to libc是属于缓冲区溢出的一种攻击
前提:此时堆栈不可执行,所以不能够将恶意代码注入到堆栈当中来进行执行
方案:将返回地址修改为现有的代码,例如
libc
库里面的system()
函数,该函数已经加载到内存空间
Task 1: Finding out the Addresses of libc Functions
既然说我们需要调用system()
函数来帮助我们获得root shell,那么首先我们就需要来获得system()
函数的位置,通过运行gdb来查看
以及通过batch模式下来查看
可以看到system()函数的地址是0xf7e12420,exit()函数的地址是0xf7e04f80
Task 2: Putting the shell string in the memory
这一步其实就是传参,将/bin/sh放入内存,需要的时候进行
方法:
将/bin/sh
通过环境变量的方式来放入到内存当中,参考blog:shell总结1:Shell全局变量、局部变量和环境变量,之后的子shell中已经将该环境变量加入到内存当中了,通过运行prtenv和retlib来进行验证
可以看到此时/bin/sh作为环境变量在内存当中的地址是0xffffd40f
Task 3: Launching the Attack
在这一步当中,我们构造badfile的payload,根据注释以及前面的信息,可以得知我们现在已经获取了所要调用的函数的地址以及参数在地址当中的位置,那么现在对于我们来说就需要知道将这些信息放在payload的什么位置/在栈中的位置
- 首先我们的目标是调用
system()
函数,那么就需要修改bof函数的return address为system()
的起始位置0xf7e12420
,此时我们需要将它填到A+0x4当中 - 之后我们要确定
/bin/sh
所要放到的位置,通过分析执行完bof()函数之后调用system()
函数时栈的地址,会发现此时参数/bin/sh应该放在ebp + 0xc的位置上 - 之后为了在/bin/sh运行之后正常退出,所以我们需要在调用完system()函数之后调用exit()函数,将system()的返回地址填充为exit()的地址
可以看到buffer的起始位置和ebp之间的距离,即A和B之间的距离为0x18,所以system()相对于buffer的偏移量就是0x18 + 0x4 = 0x1c,而exit的偏移量就在0x18 + 0x8 = 0x20,/bin/sh所在位置就是0x18 + 0xc = 0x24()
因为在执行完bof函数之后,最后肯定是pop出来system()的地址并且进行跳转,此时esp所执行想的就应该是原来的ebp + 8的位置,而当我们执行system()函数的时候,第一步就是push ebp,所以相比之前的ebp,会往上移4位,然后ebp需要在return address之下,而return address需要在传入参数的下面,所以位置分别为0x18 + 0x4,0x18 + 0x8,0x18+0xc,需要深入并细致的分析汇编以及寄存器内容
Attack variation 1
在badfile中去除exit函数,在这里我们修改跳转地址就好,改为0xAAAAAAAA
之后在进行运行,会发现仍能够获取权限,但是当我们执行exit来退出的时候,会由于跳转地址而发生segmentation fault
Attack variation 2
其实原理就是当我们改变名称的时候,会改变环境变量的地址,就只是会打印出消息,而不会获取root shell
Task 4: Defeat Shell’s countermeasure
这个task就是在针对于shell的解决方案,因为最开始的时候我们是将/bin/sh连接到了/bin/zsh,而不是/bin/dash
,这是由于dash和bash会自动放弃特权,但是如果说我们增加一个选项 -p,那么命令就会变为/bin/bash -p
,这就不会自动放弃特权,而根据提示,execv()
可以帮助我们来执行这条命令
下面开始一步一步拆解解决问题
首先是要获取
execv()
函数以及exit()
函数在内存当中的地址所以
execv()
和exit()
分别位于0xf7e994b0, 0xf7e04f80把/bin/bash和-p设置为环境变量并且找到他们在内存当中的位置
之后打印出相应的地址信息
可以看到/bin/bash的地址是
0xffffd401
,-p的地址是0xffffd515
之后就是分析栈上面的内容,ipad上面有,可以分析出
execv()
要放在距离buffer首地址偏移量位0x1c的地方,exit()
要放在距离buffer首地址0x20的地方,而pastname放在0x24,argv[]数组就放在了从0x28开始的位置上,并且根据打印出来的信息,我们能够得到input[]的其实位置以及相对偏移量之后就是根据信息来构造payload
之后进行运行,成功获得root shell
Task 5 (Optional): Return-Oriented Programming
本来我们是要在调用system函数之前调用setuid(0),而实际上需要在bof结束之后调用10次foo,然后再调用execv()
首先,我们需要获得foo、execv以及exit在内存当中的位置分别为0x565562b0,0xf7e994b0,0xf7e04f80
之后结合栈里面信息进行修改
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!