二进制堆的练习题
2026-03-02 01:17:58 # CTF

fmtstr_uaf

可执行文件为:echo2,尝试执行。

image-20260228171054793

检查文件的安全检测。发现什么安全防护都没开。

image-20260228171134019

反编译

使用IDA Pro进行反编译。

image-20260228171501856

按 “\” 键,可以消除变量的类型,更简洁。

image-20260228171600371

对于反编译的代码中,注意到有(_QWORD *),这是什么?表示指针,除此之外,还有_BYTE表示1字节,_WORD表示两字节,_DWORD,表示Double WORD,两倍的WORD,就是4字节。那么_QWORD,就是Quad WORD,四倍的WORD,就是8字节。小知识点,简单记一下。

image-20260228172705493

这里的,greetingsbyebye是函数对应地址,点击即可跳转。

image-20260228172856618

*(o + 3) = greetings;,相当于是把greetings函数的地址,赋值给 o + 3 * 8 (byte) 地址处的值。

image-20260228173332838

漏洞点

下面分别看一下echo1echo2echo3

echo1如下:

1
2
3
4
int echo1()
{
return puts("not supported");
}

echo2如下:

1
2
3
4
5
6
7
8
9
10
__int64 echo2()
{
char format[32]; // [rsp+0h] [rbp-20h] BYREF

(*(o + 3))(o);
get_input(format, 32);
printf(format);
(*(o + 4))(o);
return 0;
}

echo3如下:

1
2
3
4
5
6
7
8
9
10
11
12
__int64 echo3()
{
char *s; // [rsp+8h] [rbp-8h]

(*(o + 3))(o);
s = malloc(040u);
get_input(s, 32);
puts(s);
free(s);
(*(o + 4))(o);
return 0;
}

很明显,在echo2中,直接使用了 printf(format),这里存在格式化字符串漏洞。

echo2中,先是s = malloc(040u),然后进行了free(s),但是没有把s,置空,这里存在UAF漏洞。

然后再回到我们的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
42
43
44
45
46
47
48
49
50
int __fastcall main(int argc, const char **argv, const char **envp)
{
_QWORD *v3; // rax
unsigned int i; // [rsp+Ch] [rbp-24h] BYREF
_QWORD v6[4]; // [rsp+10h] [rbp-20h] BYREF

setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 1, 0);
o = malloc(40u);
*(o + 3) = greetings;
*(o + 4) = byebye;
printf("hey, what's your name? : ");
__isoc99_scanf("%24s", v6);
v3 = o;
*o = v6[0];
v3[1] = v6[1];
v3[2] = v6[2];
id = v6[0];
getchar();
func[0] = echo1;
func_1_ = echo2;
func_2_ = echo3;
for ( i = 0; i != 121; i = getchar() )
{
while ( 1 )
{
while ( 1 )
{
puts("\n- select echo type -");
puts("- 1. : BOF echo");
puts("- 2. : FSB echo");
puts("- 3. : UAF echo");
puts("- 4. : exit");
printf("> ");
__isoc99_scanf("%d", &i);
getchar();
if ( i > 3 )
break;
(func[i - 1])();
}
if ( i == 4 )
break;
puts("invalid menu");
}
cleanup(); //漏洞点,double free
printf("Are you sure you want to exit? (y/n)");
}
puts("bye");
return 0;
}

注意到main函数中存在一个cleanup()函数,函数内容如下:

1
2
3
4
void cleanup()
{
free(o);
}

问题就来了,先执行了cleanup函数,然后再进行判断printf("Are you sure you want to exit? (y/n)");,是否进行退出。如果不退出呢?不退出已经free(o)一次,到下一轮,会再free(o)一次,这就造成了double free漏洞。

尝试复现,使程序崩溃,如下:

image-20260302010909009

这里就是成功触发了double free,造成了程序的崩溃。

如何去将格式化字符串漏洞、UAFdouble free这三个漏洞串起来,实现获取shell呢?下面就慢慢分析。

漏洞复现