house of blindness
pwn手坐大牢的wm/(ㄒoㄒ)/~~乖乖来复现了,这题比赛的时候二十几解死活做不出来就很难受
ps:真的是house太多了买不起……
其他的exit利用方法
更改.fini_array
elf文件中会存在一个.fini_array段
在exit的时候会调用__do_global_dtors_aux_fini_array_entry指向的函数__do_global_dtors_aux
所以可以更改__do_global_dtors_aux_fini_array_entry的内容为system(‘/bin/sh’),之前见过一道用这种方法进行循环执行的题(但只能循环一次)
__rtld_lock_unlock_recursive劫持
exit的时候存在调用流程
1 |
|
所以可以更改_rtld_global的_dl_rtld_unlock_recursive成员或者_dl_rtld_lock_recursive成员为system地址或者one_gadget地址
调用_rtld_global._dl_rtld_lock_recursive时的参数rdi是_rtld_global._dl_load_lock.mutex,可以改为/bin/sh
这道题也可以更改_rtld_global,但ld中的_rtld_global和libc中的system距离太远,存在一个4096的爆破,这概率对于我这种非酋来说等于0(╯‵□′)╯︵┻━┻
house of blindness
先让我们来探究一下exit的流程,比如它到底是怎么调用.fini_array的
exit调用.fini_array流程
exit函数直接调用__run_exit_handlers
1
2
3
4
5
6void
exit (int status)
{
__run_exit_handlers (status, &__exit_funcs, true, true);
}
libc_hidden_def (exit)exit传给__run_exit_handlers的__exit_funcs指向initial
接下来__run_exit_handlers会遍历initial的fns数组,当flavor==ef_cxa(4)时会解密f->func.cxa.fn指针并调用这个函数,就是_dl_fini
1
2
3
4
5
6
7
8enum
{
ef_free, /* `ef_free' MUST be zero! */
ef_us,
ef_on,
ef_at,
ef_cxa
};ps:我当时想过这里能不能利用,但发现这个加密后就放弃了
1
2
3
4
5
6
7
8
9
10case ef_cxa:
/* To avoid dlclose/exit race calling cxafct twice (BZ 22180),
we must mark this function as ef_free. */
f->flavor = ef_free;
cxafct = f->func.cxa.fn;
#ifdef PTR_DEMANGLE
PTR_DEMANGLE (cxafct);
#endif
cxafct (f->func.cxa.arg, status);
break;这个加密的数据来自fs:[0x30],和canary差不多
_dl_fini中调用_dl_rtld_lock_recursive和_dl_rtld_unlock_recursive的地方就是这里
1
2
3
4
5
6
7
8
9
10
11
12/* 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));这里还有一个地址跳转
1
2while (i-- > 0)
((fini_t) array[i]) ();这个就是调用.fini_array的地方
细看一下调用这个地址跳转的过程(link_map结构体介绍见ret2dlresolve)
1 |
|
例题:WMCTF - 2023 - blindless
利用的就是.fini_array段为空,则直接调用fini函数的分支
根据hint,我们可以申请一个大的data chunk(大于0x100000),这样就会调用mmap分配堆块,分配的堆块就在libc下方,libc和ld的相对地址是固定的,就可以通过越界写更改
我们可以更改elf的link_map(在ld里)
- 更改_rtld_global._dl_load_lock.mutex为/bin/sh
- 更改l_addr为.init和system的plt的差值
- 更改l_info[DT_FINI]为l_info[DT_INIT]的值
- 更改l_info[DT_FINI_ARRAY]的值为0
为什么要这么构造?
.fini段的地址是0x1558,backdoor和system的plt的地址都在它之下,但改l_addr只能往后改不能往前改,所以要使l_info[DT_FINI]指向一个靠前的地址,这里我选则了l_info[DT_INIT](0x1000)
使用system(‘/bin/sh’)而不是调用backdoor是因为
- system的plt和.init的差值为0xe0
- backdoor和.init的差值为0x209
如果要使用backdoor就要改两字节,涉及一个1/16的爆破,对我来说爆破能没有最好没有:)
exp
1 |
|