House of spirit attack,题目名字说明了一切。
题目描述
32位程序,基本没开啥保护。
1 | Arch: i386-32-little |
就一个评论功能:
1 | int survey() |
依次输入name
, age
, reason
, comment
然后会依次输出,并且会把字符串”%d comment so far. We will review them as soon as we can”放在v1里面,如果继续评论的话,buf
会先free
然后再重新malloc
。
相关知识点
House of spirit 攻击原理
House of spirit是fastbin attack中的一种,主要是free出一个目标地址的fastbin出来,然后再malloc从而达到任意地址写的目的。
需要绕过的一些检测:
fake chunk
的ISMMAP
位(size的第2个bit)不能为1,因为free时,如果是mmap
的chunk,会单独处理。1
2
3
4
5
6
7else if (!chunk_is_mmapped(p)) {
...
}
else {
munmap_chunk (p);
}fake chunk
地址需要对齐MALLOC_ALIGN_MASK
1
2
3
4
5
6
7
8
9
10if (__builtin_expect ((uintptr_t) p > (uintptr_t) -size, 0)
|| __builtin_expect (misaligned_chunk (p), 0))
{
errstr = "free(): invalid pointer";
errout:
if (!have_lock && locked)
(void) mutex_unlock (&av->mutex);
malloc_printerr (check_action, errstr, chunk2mem (p), av);
return;
}fake chunk
的size
大小需要满足对应的fastbin的需求,同时也得对齐。1
2
3
4
5
6
7
8
9
10
11if (__glibc_unlikely (size < MINSIZE || !aligned_OK (size)))
{
errstr = "free(): invalid size";
goto errout;
}
if (have_lock && old != NULL && __builtin_expect (old_idx != idx, 0))
{
errstr = "invalid fastbin entry (free)";
goto errout;
}fake chunk
的next chunk
的大小不能小于2 * SIZE_SZ
,同时也不能大于av->system_mem
。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23if (__builtin_expect (chunk_at_offset (p, size)->size <= 2 * SIZE_SZ, 0)
|| __builtin_expect (chunksize (chunk_at_offset (p, size)) >= av->system_mem, 0))
{
/* We might not have a lock at this point and concurrent modifications
of system_mem might have let to a false positive. Redo the test
after getting the lock. */
if (have_lock ||
({
assert (locked == 0);
mutex_lock(&av->mutex);
locked = 1;
chunk_at_offset (p, size)->size <= 2 * SIZE_SZ || chunksize (chunk_at_offset (p, size)) >= av->system_mem;
}))
{
errstr = "free(): invalid next size (fast)";
goto errout;
}
if (! have_lock)
{
(void)mutex_unlock(&av->mutex);
locked = 0;
}
}fake chunk
对应的fastbin链表头部不能是该fake chunk
,即不能构成double free的情况。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17do
{
/* Check that the top of the bin is not the record we are going to add
(i.e., double free). */
if (__builtin_expect (old == p, 0))
{
errstr = "double free or corruption (fasttop)";
goto errout;
}
/* Check that size of fastbin chunk at the top is the same as
size of the chunk that we are adding. We can dereference OLD
only if we have the lock, otherwise it might have already been
deallocated. See use of OLD_IDX below for the actual check. */
if (have_lock && old != NULL)
old_idx = fastbin_index(chunksize(old));
p->fd = old2 = old;
} while ((old = catomic_compare_and_exchange_val_rel (fb, p, old2)) != old2);
利用思路
- 乍一眼看过去好像没什么洞,发现问题出在
sprintf
中,v1
分配的大小是0x38,当cnt
是两位数的时候,字符串长度正好是0x38,导致nbytes
被覆盖为0,那么只要cnt为三位数,就可以将cnt
覆盖为ord('n')=0x6e
:1
2
3
4
5
6
7
8
9
10
11char v1; // [sp+10h] [bp-E8h]@2
size_t nbytes; // [sp+48h] [bp-B0h]@1
size_t v3; // [sp+4Ch] [bp-ACh]@1
char s; // [sp+50h] [bp-A8h]@2
int v5; // [sp+A0h] [bp-58h]@2
void *buf; // [sp+A4h] [bp-54h]@2
int v7; // [sp+A8h] [bp-50h]@2
...
sprintf(&v1, "%d comment so far. We will review them as soon as we can", cnt); - 那么对应的对
s
的输入可以溢出到v5
和buf
,这里可以将栈上的libc,stack以及heap address全部都leak出来:1
2
3
4
5read(0, &s, nbytes);
...
printf("Comment: %s\n\n", &s); - 地址leak出来之后,就可以在栈上
v7
的区域伪造出fake fastbin以及next chunk size,之后它会被free
到fastbin中 - 下一次的malloc就能分配到栈上的地址空间,由于这个时候
nbytes=0x68
,并且这个fake chunk距离栈上的return address的offset只有0x4c,所以这里我采用的是写rop的方法,构造system("/bin/sh");
,从而在从survey
返回地时候触发来getshell。
exp
1 | context.log_level = "debug" |
小结
- 很基本的House of spirit attack,主要是了解一下原理以及一些check的pass
- 我能想到的最简单的就是rop了,不知道还有没有其他姿势
参考资料
都是House of spirit的原理分析: