gyctf-2020也就是ichunqiu举办的一次公益ctf,在比赛中做出了一些题目,也有部分没做出来,通过赛后复盘学习一下姿势。
gyctf_2020_force
这道题当时是做出来的,听名字force就知道肯定是House_of_force了。主要涉及到force,malloc_hook,realloc_hook。
思路
1 | unsigned __int64 add() |
两个子函数add和func_puts只有add是有用的。
整体思路
修改top_chunk_size,malloc到realloc_hook,修改realloc_hook为one_gadget,修改malloc_hook为realloc+offset。
offset根据one_gadget的具体条件调节。
House of force
House of force的条件
1.能够修改到top_chunk的size。
2.能够分配任意大小的chunk。
在本题里面两个条件都满足。
泄露libc基址
当申请比较大的值(通常0x20000以上)时,会mmap出一块地址,与libc基址有固定偏移,调试的时候计算一下偏移即可。
malloc_size的计算
当我们修改完size为0xffffffffffffffff之后,就可以分配任意大小的chunk了。
* The evil_size is calulcated as (nb is the number of bytes requested + space for metadata):
* new_top = old_top + nb
* nb = new_top - old_top
* req + 2sizeof(long) = new_top - old_top
* req = new_top - old_top - 2sizeof(long)
* req = dest - 2sizeof(long) - old_top - 2sizeof(long)
* req = dest - old_top - 4*sizeof(long)
计算如上所示,nb是新分配的chunk的size。通过计算得出的req就是需要申请的大小。
Exploit
1 | #!/usr/bin/python2 |
gyctf_2020_some_thing_exceting
思路
1 | unsigned __int64 flag_init() |
程序开始时把flag读到了bss中,并且在该地址上面有个地方存了值0x60。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24unsigned __int64 delete()
{
int v1; // [rsp+4h] [rbp-Ch]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
puts("#######################");
puts("# Delete Banana #");
puts("#---------------------#");
printf("> Banana ID : ");
_isoc99_scanf("%d", &v1);
if ( v1 < 0 || v1 > 10 || !ptr[v1] )
{
puts("Emmmmmm!Maybe you want Fool me!");
myexit();
}
free(*(void **)ptr[v1]); // free之后没有设为0
free(*((void **)ptr[v1] + 1));
free(ptr[v1]);
puts("#---------------------#");
puts("# ALL Down! #");
puts("#######################");
return __readfsqword(0x28u) ^ v2;
}
其中delete函数在free完相关地址后并没有把ptr[v1]置空,导致可以double 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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51unsigned __int64 create()
{
void *v0; // rsi
size_t nbytes; // [rsp+Ch] [rbp-24h]
int i; // [rsp+14h] [rbp-1Ch]
void *buf; // [rsp+18h] [rbp-18h]
void *v5; // [rsp+20h] [rbp-10h]
unsigned __int64 v6; // [rsp+28h] [rbp-8h]
v6 = __readfsqword(0x28u);
puts("#######################");
puts("# Create Banana #");
puts("#---------------------#");
for ( i = 0; i <= 9 && ptr[i]; ++i ) // 最多10个
{
if ( i == 9 )
{
puts("# so much banana! #");
puts("#######################");
return __readfsqword(0x28u) ^ v6;
}
}
ptr[i] = malloc(0x10uLL); // 创建结构体
printf("> ba's length : ");
_isoc99_scanf("%d", &nbytes);
if ( (signed int)nbytes <= 0 || (signed int)nbytes > 0x70 )// 只能创建fastbin
{
puts("Emmmmmm!Maybe you want Fool me!");
myexit();
}
buf = malloc((signed int)nbytes);
printf("> ba : ", &nbytes);
v0 = buf;
read(0, buf, (unsigned int)nbytes);
printf("> na's length : ", v0);
_isoc99_scanf("%d", (char *)&nbytes + 4);
if ( SHIDWORD(nbytes) <= 0 || SHIDWORD(nbytes) > 0x70 )
{
puts("Emmmmmm!Maybe you want Fool me!");
myexit();
}
printf("> na : ", (char *)&nbytes + 4);
v5 = malloc(SHIDWORD(nbytes));
read(0, v5, HIDWORD(nbytes));
*(_QWORD *)ptr[i] = buf; // ba的地址
*((_QWORD *)ptr[i] + 1) = v5; // na的地址
puts("#---------------------#");
puts("# ALL Down! #");
puts("#######################");
return __readfsqword(0x28u) ^ v6;
}
在create函数中可以清晰的看出ba和na的内容地址分别存在ptr[i]和ptr[i]+1中。
后面view函数打印的就是这两个地址的内容。所以我们要做的就是把flag那一块当做fake_chunk(size=0x60)填到某个ptr[i]中,到时候view(i)就可以打印出flag。
Exploit
1 | #!/usr/bin/python2 |