太菜了,MarksMan没有做出来,本地打通了结果远程打不通。后面去做SecureBox,看到Glibc 2.30以为需要了解一些新机制了可能做起来会有些吃力,结果发现还挺简单的。
题目描述
x64,保护全开:
1 | Arch: amd64-64-little |
常规菜单题:
1 | 1.Allocate |
Allocate
功能就是先malloc一块0x28的chunk,然后前0x10字节填充随机密钥,chunk[4]储存输入的size,chunk[3]储存根据输入的size来malloc的数据chunk: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
44unsigned __int64 Allocate()
{
_QWORD *v0; // rbx
unsigned int v2; // [rsp+4h] [rbp-2Ch]
int i; // [rsp+8h] [rbp-28h]
int j; // [rsp+Ch] [rbp-24h]
unsigned __int64 size; // [rsp+10h] [rbp-20h]
unsigned __int64 v6; // [rsp+18h] [rbp-18h]
v6 = __readfsqword(0x28u);
v2 = -1;
for ( i = 0; i <= 15; ++i )
{
if ( !chunk_array[i] )
{
v2 = i;
break;
}
}
if ( v2 == -1 )
{
puts("No boxes available!");
}
else
{
puts("Size: ");
size = choice();
if ( size > 0x100 && (unsigned int)size <= 0xFFF ) // size can be very large
{
chunk_array[v2] = malloc(0x28uLL);
*((_QWORD *)chunk_array[v2] + 4) = size; // very large number
v0 = chunk_array[v2];
v0[3] = malloc(size); // return 0 here
memset(chunk_array[v2], 0, 0x14uLL);
read_rand_data(chunk_array[v2]);
puts("Key: ");
for ( j = 0; j <= 15; ++j )
printf("%02x ", *((unsigned __int8 *)chunk_array[v2] + j));
printf("\nBox ID: %d\n", v2);
}
puts("Finish!");
}
return __readfsqword(0x28u) ^ v6;
}Delete
free得很彻底: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
28unsigned __int64 Delete()
{
unsigned __int64 v1; // [rsp+0h] [rbp-10h]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
puts("Box ID: ");
v1 = choice();
if ( v1 > 0xF )
{
LABEL_7:
puts("Finish!");
return __readfsqword(0x28u) ^ v2;
}
if ( chunk_array[v1] )
{
if ( *((_QWORD *)chunk_array[v1] + 3) )
{
free(*((void **)chunk_array[v1] + 3));
*((_QWORD *)chunk_array[v1] + 3) = 0LL;
}
free(chunk_array[v1]);
chunk_array[v1] = 0LL;
goto LABEL_7;
}
puts("Empty Box!");
return __readfsqword(0x28u) ^ v2;
}Enc
功能对输入的index
对应的chunk中储存的数据chunk进行编辑,逻辑为输入相应的offset
,length
,以及data
,然后在数据chunk的对应的offset
写入length
长度的加密的data
,这里的加密其实就是使用在Allocate
功能中产生的随机0x10字节的密钥进行异或。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
42unsigned __int64 Enc()
{
unsigned __int64 i; // [rsp+8h] [rbp-38h]
unsigned __int64 v2; // [rsp+10h] [rbp-30h]
unsigned __int64 v3; // [rsp+18h] [rbp-28h]
unsigned __int64 v4; // [rsp+20h] [rbp-20h]
unsigned __int64 v5; // [rsp+28h] [rbp-18h]
unsigned __int64 v6; // [rsp+30h] [rbp-10h]
unsigned __int64 v7; // [rsp+38h] [rbp-8h]
v7 = __readfsqword(0x28u);
puts("Box ID: ");
v2 = choice();
if ( v2 > 0xF )
{
LABEL_9:
puts("Finish!");
return __readfsqword(0x28u) ^ v7;
}
if ( chunk_array[v2] )
{
puts("Offset of msg: ");
v3 = choice();
if ( *((_QWORD *)chunk_array[v2] + 4) > v3 ) // *((_QWORD *)chunk_array[v2] + 4) is large here
{
puts("Len of msg: ");
v4 = *((_QWORD *)chunk_array[v2] + 4) - v3;
v5 = choice();
if ( v5 <= v4 )
{
puts("Msg: ");
read_data(*((_QWORD *)chunk_array[v2] + 3) + v3, (unsigned int)v5);
v6 = *((_QWORD *)chunk_array[v2] + 3) + v3;
for ( i = 0LL; i < v5; ++i )
*(_BYTE *)(v6 + i) ^= *((_BYTE *)chunk_array[v2] + (i & 0xF));
}
}
goto LABEL_9;
}
puts("Empty Box!");
return __readfsqword(0x28u) ^ v7;
}Show
功能打印给定index
对应chunk中的数据chunk中的数据,根据提供的offset
和length
。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
39unsigned __int64 Show()
{
unsigned __int64 v0; // ST10_8
void *dest; // ST20_8
unsigned __int64 v3; // [rsp+0h] [rbp-30h]
unsigned __int64 v4; // [rsp+8h] [rbp-28h]
unsigned __int64 n; // [rsp+18h] [rbp-18h]
unsigned __int64 v6; // [rsp+28h] [rbp-8h]
v6 = __readfsqword(0x28u);
puts("Box ID: ");
v3 = choice();
if ( v3 <= 0xF )
{
if ( chunk_array[v3] )
{
puts("Offset of msg: ");
v4 = choice();
if ( *((_QWORD *)chunk_array[v3] + 4) > v4 )
{
puts("Len of msg: ");
v0 = *((_QWORD *)chunk_array[v3] + 4) - v4;
n = choice();
if ( n <= v0 )
{
dest = calloc(n + 32, 1uLL);
memcpy(dest, (const void *)(*((_QWORD *)chunk_array[v3] + 3) + v4), n);
puts("Msg: ");
puts((const char *)dest);
}
}
}
else
{
puts("Empty Box!");
}
}
return __readfsqword(0x28u) ^ v6;
}
利用思路
- 先利用unsorted bin来leak出libc的地址。
- 之后关键利用点就在于:
Allocate
功能:其中Allocate
的对size的检查逻辑if ( size > 0x100 && (unsigned int)size <= 0xFFF )
使得size可以是形如0x00FFFFFF00000000
的很大的数,而进行相应的malloc操作时,会因为size过大而返回NULL也就是0,但是并没有对malloc的结果进行检查。Enc
功能:因为*((_QWORD *)chunk_array[v2] + 4)
可以很大(就是输入的size)且*((_QWORD *)chunk_array[v2] + 3)=0
(malloc返回的结果),加上没有对*((_QWORD *)chunk_array[v2] + 3)
的检查,所以在控制offset为任意地址的情况下,即可以实现向任意地址写任意数据。
- 根据上述漏洞,首先
Allocate
一个size为0x00FFFFFF00000000
(覆盖所有地址均可)的chunk(返回0),记录生成的key
。 Enc
前一步申请的chunk,offset
设置为__realloc_hook
的地址,length的值为0x10。- 将
(libc_realloc + 10) << 64) | one_gadget
用key
加密(逐字节异或)得到payload,将payload作为输入的data,这样可以将__malloc_hook
写入__libc_realloc+10
,__realloc_hook
写入onegadget
,目的是调整栈以满足[rsp]+0x70
的约束然后打onegadget
。(这里这么做是因为直接改__free_hook
为system
打不通,直接改__malloc_hook
也满足不了约束,所以用__libc_realloc
来调整栈。)
exp
1 | from pwn import * |
flag
flag{4b03fb45856021f3415e6451f6cf855d}