首页
社区
课程
招聘
[求助]微软bug?天杀的sprintf_s函数!!!
发表于: 2009-2-21 16:56 17538

[求助]微软bug?天杀的sprintf_s函数!!!

2009-2-21 16:56
17538
刚初步学习完毕seh,开始写些代码实践一下:

#include "windows.h"
#include "stdio.h"

int WinMain(__in HINSTANCE hInstance, __in_opt HINSTANCE hPrevInstance, __in_opt LPSTR lpCmdLine, __in int nShowCmd)
{
        __try
        {
                //LARGE_INTEGER li_temp01,li_temp02;

                //QueryPerformanceCounter(&li_temp01);
                //QueryPerformanceCounter(&li_temp02);
                //QueryPerformanceCounter(&li_temp02);
                //QueryPerformanceCounter(&li_temp02);

                char char_temp01[1024];
                sprintf_s(char_temp01,sizeof(char_temp01),"%I64",
                        0);//li_temp02.QuadPart - li_temp01.QuadPart);
                MessageBox(0,char_temp01,0,0);

                //__asm jmp [li_temp01.LowPart];
        }
        __except(EXCEPTION_EXECUTE_HANDLER)
        {
                MessageBox(0,0,0,0);
        }

        return 1;
}

多么简单的代码阿! -_-b

//__asm jmp [li_temp01.LowPart]; 专门触发seh的代码都屏蔽了……
因为sprintf或者wsprintf不是被vs2005说不安全嘛,我们用它推荐的sprintf_s函数,注意后面的format参数“"%I64"”,我特意少些一个“d”,理论上不是messagebox弹出一个乱码的对话框,就是seh被触发。

各位可以用vs2005编译一下,项目设置都是默认的,(/EHsc)开关也是默认的,能够正常触发__try的。

实际的结果是进程直接被卡擦:弹出标准windows异常窗口。

好的,我们OllDbg……

00401040    6A 00                          push 0
00401042    68 60A14000                    push SEH.0040A160 ; ASCII "%I64"
00401047    68 00040000                    push 400
0040104C    8D85 E4FBFFFF                  lea eax,dword ptr ss:[ebp-41C]
00401052    50                             push eax
00401053    E8 51000000                    call SEH.004010A9;sprintf_s函数调用。
00401058    83C4 10                        add esp,10
================= 悲惨的分割线 =================
进入call SEH.004010A9后多次call,来到了:
00404C51    8A13                           mov dl,byte ptr ds:[ebx]; 开始检查格式化字符串。
00404C53    84D2                           test dl,dl
.
.
.
.
.
.
.
00404DF0    8A03                           mov al,byte ptr ds:[ebx]
00404DF2    3C 36                          cmp al,36
00404DF4    75 17                          jnz short SEH.00404E0D
00404DF6    807B 01 34                     cmp byte ptr ds:[ebx+1],34
00404DFA    75 11                          jnz short SEH.00404E0D
00404DFC    43                             inc ebx ; 检查通过,已经是%I64字符串。
00404DFD    43                             inc ebx ; SEH.0040A162
00404DFE    814D E8 00800000               or dword ptr ss:[ebp-18],8000
00404E05    895D B4                        mov dword ptr ss:[ebp-4C],ebx ; SEH.0040A162
00404E08    E9 28060000                    jmp SEH.00405435

%I64都已经检查完毕了,就差检查“d”了,如果有d的话都一切正常…… 悲剧阿~~~

00405435    8B5D B4                        mov ebx,dword ptr ss:[ebp-4C]                ; SEH.0040A164
00405438    8A03                           mov al,byte ptr ds:[ebx]
0040543A    84C0                           test al,al
0040543C    8845 E7                        mov byte ptr ss:[ebp-19],al
0040543F    74 21                          je short SEH.00405462
00405441    8B7D D4                        mov edi,dword ptr ss:[ebp-2C]
00405444    8AD0                           mov dl,al
00405446  ^ E9 1FF8FFFF                    jmp SEH.00404C6A

上面的代码就是检查“d”的,如果有d,红色的代码不会触发,会继续运行,然后jmp SEH.00404C6A,一切正常…… 悲剧阿~~~
我们通过je short SEH.00405462来到了下面的代码:

================= 悲惨的分割线 =================
00404B6B    E8 9CCCFFFF                    call SEH.0040180C
00404B70    56                             push esi
00404B71    56                             push esi
00404B72    56                             push esi
00404B73    56                             push esi
00404B74    C700 16000000                  mov dword ptr ds:[eax],16
00404B7A    56                             push esi
00404B7B    E8 2DCCFFFF                    call SEH.004017AD
00404B80    83C4 14                        add esp,14

红色的代码call SEH.004017AD是关键,我们进入看看。

================= 悲惨的分割线 =================
004017AD    55                             push ebp
004017AE    8BEC                           mov ebp,esp
004017B0    FF35 6CCF4000                  push dword ptr ds:[40CF6C]
004017B6    E8 26130000                    call SEH.00402AE1
004017BB    85C0                           test eax,eax
004017BD    59                             pop ecx                                      ; SEH.00404B80
004017BE    74 03                          je short SEH.004017C3
004017C0    5D                             pop ebp                                      ; SEH.00404B80
004017C1    FFE0                           jmp eax
004017C3    6A 02                          push 2
004017C5    E8 1B320000                    call SEH.004049E5
004017CA    59                             pop ecx                                      ; SEH.00404B80
004017CB    5D                             pop ebp                                      ; SEH.00404B80
004017CC  ^ E9 E0FEFFFF                    jmp SEH.004016B1
.
.
.
.
.
.
0040174E    C745 80 0D0000C0               mov dword ptr ss:[ebp-80],C000000D
00401755    8975 8C                        mov dword ptr ss:[ebp-74],esi                ; SEH.00404B80
00401758    8945 D4                        mov dword ptr ss:[ebp-2C],eax
0040175B    FF15 2CA04000                  call dword ptr ds:[<&KERNEL32.IsDebuggerPres>; kernel32.IsDebuggerPresent
00401761    6A 00                          push 0
00401763    8BF0                           mov esi,eax
00401765    FF15 28A04000                  call dword ptr ds:[<&KERNEL32.SetUnhandledEx>; kernel32.SetUnhandledExceptionFilter
0040176B    8D45 D0                        lea eax,dword ptr ss:[ebp-30]
0040176E    50                             push eax
0040176F    FF15 24A04000                  call dword ptr ds:[<&KERNEL32.UnhandledExcep>; kernel32.UnhandledExceptionFilter
00401775    85C0                           test eax,eax
00401777    75 0C                          jnz short SEH.00401785
00401779    85F6                           test esi,esi                                 ; SEH.00404B80
0040177B    75 08                          jnz short SEH.00401785
0040177D    6A 02                          push 2
0040177F    E8 61320000                    call SEH.004049E5
00401784    59                             pop ecx
00401785    68 0D0000C0                    push C000000D
0040178A    FF15 20A04000                  call dword ptr ds:[<&KERNEL32.GetCurrentProc>; kernel32.GetCurrentProcess
00401790    50                             push eax
00401791    FF15 1CA04000                  call dword ptr ds:[<&KERNEL32.TerminateProce>; kernel32.TerminateProcess
00401797    8B8D A4020000                  mov ecx,dword ptr ss:[ebp+2A4]
0040179D    33CD                           xor ecx,ebp
0040179F    5E                             pop esi                                      ; SEH.00404B80
004017A0    E8 20F9FFFF                    call SEH.004010C5
004017A5    81C5 A8020000                  add ebp,2A8
004017AB    C9                             leave
004017AC    C3                             retn

进入函数之后,call了2次,然后红色代码:直接jmp SEH.004016B1,接着就是一系列的函数调用:
kernel32.IsDebuggerPresent
kernel32.SetUnhandledExceptionFilter
kernel32.UnhandledExceptionFilter
kernel32.GetCurrentProcess
kernel32.TerminateProcess

我的心阿~ 拔凉拔凉的阿~~~

更换其他诸如:sprintf、wsprintf,或者只更换格式化字符串“%d”,“%s”,都正常,自己的seh能触发。

sprintf_s 内部bug?天杀的sprintf_s !!!该死的微软?

继续研究中……

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 1
支持
分享
最新回复 (6)
雪    币: 205
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
sprintf_s 内部出错,也没必要SetUnhandledExceptionFilter(NULL)呀~ 不知道微软怎么想的?

继续苦闷中……

还有什么函数能代替sprintf_s吗?
2009-2-21 17:30
0
雪    币: 205
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
刚刚用debug模式编译,可以有源码级的跟踪调试,再次确认应该就是sprintf_s 系列函数有bug。
继续整理思路,看微软的output.c代码……
2009-2-21 18:16
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
很简单的道理
我猜微软这样做是防止Exception handler导致的漏洞

比如果sprintf_s用得不对, 攻击者就可以首先填写恶意的buffer通过sprintf_s读入到stack,然后使得sprintf_s触发异常. 如果没有reset SEH的话,用户的Exception handler就会执行, 这种情况下系统就会去找stack中的handler pointer. 结果这个handler pointer就是被攻击者恶意填充的...

所以sprintf_s一定要自己放一个自己的trap在这里,防止这样的攻击.

当然,xp sp2以后的DEP, 在OS level上可以从某种情况下防止这样的攻击. 启动dep后, 非PE注册的函数是不能当成exception handler执行的.

我没有去检查对应的源代码. 上面是我的猜测

另外,为何你觉得sprintf_s里面调用了SEH的函数,就是个bug呢?  难道sprintf_s最后没有还原你的defaulter handler么?
2009-2-22 19:25
0
雪    币: 205
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
多谢回复!

昨天跟踪了源码,output.c文件。

说它是bug,主要因为sprintf 函数是不会这样的,发现非法的格式化字符串,函数正常返回。不会触发seh清空代码。

而有 _s 的函数却会直接清空seh,之后直接kernel32.TerminateProcess,关闭整个进程了。要防止Exception handler漏洞,也不用关闭整个进程吧?

更何况本身 _s 的函数就已经增加了一个入口参数,指明buffer的大小了。

从我跟踪的情况来看,就是output.c源码有bug,没能正确处理好 %I64 以及 %I32 这两个格式化符号。

具体的情况还没有时间详细跟踪源码。

过几天等有空了把output.c的源码仔细跟踪看一下,写个原创说明一下~ ^_^
2009-2-23 15:49
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
不杀死当前进程,如何防止通过Exception handler造成的攻击呢?
调用者参数传递错误, API的行为就是undefined, 何错之有呢?
如果让你来写这个函数, 你应该怎么写呢? 这种情况下, 你如果不杀死进程, 你能够怎么做呢?
2009-2-23 22:06
0
雪    币: 30
活跃值: (750)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
7
楼上精辟~
膜拜。
微软这样做有点眉毛胡子一把抓的味道
2009-2-25 12:12
0
游客
登录 | 注册 方可回帖
返回
//