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

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

反编译
使用IDA Pro进行反编译。

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

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

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

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

漏洞点
下面分别看一下echo1、echo2和echo3。
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];
(*(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;
(*(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; unsigned int i; _QWORD v6[4];
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(); 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漏洞。
尝试复现,使程序崩溃,如下:

这里就是成功触发了double free,造成了程序的崩溃。
如何去将格式化字符串漏洞、UAF和double free这三个漏洞串起来,实现获取shell呢?下面就慢慢分析。
漏洞复现