何为exit_hook
hook即修改地址,exit_hook即修改exit函数内的地址达到控制执行流的目的
hook rtld_lock_(un)lock_recursive
由fini_array这篇文章知道了exit)()
在执行时会先调用__run_exit_handlers
,而在这个函数中又会调用_dl_fini
_dl_fini
函数源码如下:
1 | #ifdef SHARED |
关键函数
1 | __rtld_lock_lock_recursive (GL(dl_load_lock)); |
__rtld_lock_lock_recursive()
的定义:
1 | # define __rtld_lock_lock_recursive(NAME) \ |
宏GL
的定义:
1 | # if IS_IN (rtld) |
而_rtld_global
是一个结构体,我们可以在pwndbg中用p _rtld_global
来查看
结构体太长,这里我们这看结构体中作为上面俩函数的参数的部分
1 | _dl_load_lock = { |
函数的参数即为 _rtld_global._dl_load_lock.mutex.__size
的地址
如何利用
既然我们已经知道了关键函数及其参数的位置,那么我们直接将关键函数改为one_gadget来get shell,也可以将关键函数改为system
,再把参数地址改为/bin/sh\x00
地址来get shell
如何寻找偏移
_rtld_global
结构位于ld.so
中 ( ld.sym['_rtld_global']
),而libc_base
与ld_base
又有固定的差值,所以我们可以通过libc的基址来推出函数及其参数的地址
1 | libc2.23下偏移为0×5f0040,两个hook的偏移为3848和3850 |
由于这个函数对于libc_base的偏移是固定的,故可以使用p &_rtld_global._dl_rtld_lock_recursive
来查看函数地址,再减去libc_base得到偏移
1 | pwndbg> p &_rtld_global._dl_rtld_lock_recursive |
hook __libc_atexit
exit.c源码如下:
1 | __run_exit_handlers (int status, struct exit_function_list **listp, |
而在我们调用__run_exit_handlers
这个函数时,参数run_list_atexit
传进去的值就为真:
1 | void exit (int status) |
因此,可以直接改__libc_atexit
的值为one_gadget
,在执行exit
函数(从main
函数退出时也调用了exit()
)时,就能直接getshell
了。
这个__libc_atexit
有一个极大的优点,就是它在libc
而非ld
中,随远程环境的改变,不会有变化。缺点就是,它是无参调用的hook
,传不了/bin/sh
的参数,one_gadget
不一定都能打通。