CVE-2010-3974-Microsoft Windows 传真封面编辑器双重释放漏洞

环境

Windows 7 32位(原始镜像安装完就行)
windbg
ida

漏洞分析

打开软件:在命令行输入fxscover

windbg附加,打开poc,点击确定

windbg断下

1
2
3
4
5
6
7
8
9
10
0:005> g
(a30.b00): Unknown exception - code 000006ba (first chance)
(a30.9a8): C++ EH exception - code e06d7363 (first chance)
(a30.9a8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=01de1ad8 ebx=0032ebb0 ecx=01dd78d8 edx=01dd7ea0 esi=01dd78d8 edi=01db71f8
eip=01dd7ea0 esp=0014f65c ebp=0014f660 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
01dd7ea0 b800de01d8 mov eax,0D801DE00h

堆栈调用显示如下:

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
0:000> kv
ChildEBP RetAddr Args to Child
WARNING: Frame IP not in any known module. Following frames may be wrong.
0014f658 68b2b7b3 0014f67c 005faadd 006164f4 0x1dd7ea0
0014f660 005faadd 006164f4 01db71f8 0032ebb0 MFC42u!CObject::IsKindOf+0xb (FPO: [Non-Fpo])
0014f67c 005fb1d4 00000000 0032ebb0 68b38515 FXSCOVER!CDrawDoc::Remove+0x67 (FPO: [Non-Fpo])
0014f688 68b38515 0032ebb0 68b384df 0032ebb0 FXSCOVER!CDrawDoc::DeleteContents+0xc (FPO: [0,0,4])
0014f690 68b384df 0032ebb0 0032ebb0 0014f6d4 MFC42u!CDocument::OnNewDocument+0x15 (FPO: [0,0,4])
0014f6a0 005fa812 0032ebb0 68b383e8 e409f953 MFC42u!COleDocument::OnNewDocument+0xe (FPO: [Non-Fpo])
0014f6a8 68b383e8 e409f953 01db71f8 01db7274 FXSCOVER!CDrawDoc::OnNewDocument+0xa (FPO: [0,0,4])
0014f6d4 68b38598 00000000 00000001 e409f8e3 MFC42u!CSingleDocTemplate::OpenDocumentFile+0x103 (FPO: [Non-Fpo])
0014f764 005f80fb 0032ebb0 00616000 005f94f3 MFC42u!CDocManager::OnFileNew+0xaa (FPO: [Non-Fpo])
0014f770 005f94f3 e409385d 00000000 00618798 FXSCOVER!CDrawApp::OnFileNew+0xb (FPO: [0,0,0])
0014f7b8 005f8f62 01dd3410 e4093801 00000000 FXSCOVER!CDrawApp::OpenDocumentFile+0x57a (FPO: [Non-Fpo])
0014f7e4 68b46572 00000000 005f1de0 0014f828 FXSCOVER!CDrawApp::OnFileOpen+0x4a (FPO: [Non-Fpo])
0014f7f4 68b2a879 00618798 0000e101 00000000 MFC42u!_AfxDispatchCmdMsg+0x49 (FPO: [Non-Fpo])
0014f828 68b2b957 0000e101 00000000 00000000 MFC42u!CCmdTarget::OnCmdMsg+0x13c (FPO: [Non-Fpo])
0014f864 68b2c42b 0000e101 00000000 00000000 MFC42u!CFrameWnd::OnCmdMsg+0xb6 (FPO: [Non-Fpo])
0014f8b4 68b46d1d 0000e101 00000000 0032ed08 MFC42u!CWnd::OnCommand+0x99 (FPO: [Non-Fpo])
0014f8d0 68b2c3b5 0000e101 00000000 e409f6df MFC42u!CFrameWnd::OnCommand+0x79 (FPO: [Non-Fpo])
0014f958 68b26657 00000111 0000e101 00000000 MFC42u!CWnd::OnWndMsg+0x51 (FPO: [Non-Fpo])
0014f980 68b2887a 00000111 0000e101 00000000 MFC42u!CWnd::WindowProc+0x2e (FPO: [Non-Fpo])
0014f9e8 68b287d2 0032ed08 00000000 00000111 MFC42u!AfxCallWndProc+0xb5 (FPO: [Non-Fpo])
0014fa0c 68b28909 000d0266 00000111 0000e101 MFC42u!AfxWndProc+0x3e (FPO: [Non-Fpo])
0014fa40 766886ef 000d0266 00000111 0000e101 MFC42u!AfxWndProcBase+0x57 (FPO: [Non-Fpo])
0014fa6c 76688876 68b288b2 000d0266 00000111 USER32!InternalCallWinProc+0x23
0014fae4 766889b5 00000000 68b288b2 000d0266 USER32!UserCallWinProcCheckWow+0x14b (FPO: [Non-Fpo])
0014fb44 76688e9c 68b288b2 00000000 0014fb7c USER32!DispatchMessageWorker+0x35e (FPO: [Non-Fpo])
0014fb54 68b2c48f 006187cc 00000000 00618798 USER32!DispatchMessageW+0xf (FPO: [Non-Fpo])
0014fb64 68b2c4e9 00618798 00618798 ffffffff MFC42u!CWinThread::PumpMessage+0x42 (FPO: [0,0,0])
0014fb7c 68b2f85d 00619298 00000001 00000000 MFC42u!CWinThread::Run+0x4e (FPO: [Non-Fpo])
0014fb90 0060d63a 005f0000 00000000 00151b80 MFC42u!AfxWinMain+0x84 (FPO: [Non-Fpo])
0014fc24 75ed1174 7ffd9000 0014fc70 775cb3f5 FXSCOVER!_initterm_e+0x1b1 (FPO: [Non-Fpo])
0014fc30 775cb3f5 7ffd9000 83b468be 00000000 kernel32!BaseThreadInitThunk+0xe (FPO: [Non-Fpo])
0014fc70 775cb3c8 0060d792 7ffd9000 00000000 ntdll!__RtlUserThreadStart+0x70 (FPO: [Non-Fpo])
0014fc88 00000000 0060d792 7ffd9000 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo])

其实上面可以看DeleteContents,Remove这个函数,应该是跟释放相关

触发异常的地址是已经free掉了的,那感觉是释放后重用,这个需要后面再确认

1
2
3
4
5
0:000> !heap -p  -a 01de1ad8 
address 01de1ad8 found in
_HEAP @ 320000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
01de1ad0 0075 0000 [00] 01de1ad8 003a0 - (free)

我的gflags没有开

1
2
0:000> !gflag
Current NtGlobalFlag contents: 0x00000000

下面开启页堆,hpa(其实使用hfc【heap free check】更精准一点)

1
0:005> !gflag +hpa

重新打开poc触发下面异常

1
2
3
4
5
6
7
8
9
0:005> g
(c58.d58): C++ EH exception - code e06d7363 (first chance)
Critical error detected c0000374
(c58.d58): Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=00000000 ecx=775907ed edx=0016ed4d esi=00320000 edi=006d7a08
eip=7763280d esp=0016efa0 ebp=0016f018 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
ntdll!RtlReportCriticalFailure+0x29:
7763280d cc int 3

我们这时候再看下堆栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
0:000> kb
ChildEBP RetAddr Args to Child
0016f018 7763376b c0000374 7764cdc8 0016f05c ntdll!RtlReportCriticalFailure+0x29
0016f028 7763384b 00000002 83af535a 00320000 ntdll!RtlpReportHeapFailure+0x21
0016f05c 77633ab4 00000003 00320000 006d7a08 ntdll!RtlpLogHeapFailure+0xa1
0016f0b4 775f7ad7 00320000 006d7a08 00000000 ntdll!RtlpAnalyzeHeapFailure+0x25b
0016f1a8 775c2d68 006d7a08 006d7a10 006d7a10 ntdll!RtlpFreeHeap+0xc6
0016f1c8 765298cd 00320000 00000000 006d7a10 ntdll!RtlFreeHeap+0x142
0016f214 0086f43a 006d7a10 006d7a10 0016f240 msvcrt!free+0xcd
0016f224 0086ab0c 00000001 006b71f8 0032ebb0 FXSCOVER!CDrawRoundRect::`scalar deleting destructor'+0x1a
0016f240 0086b1d4 00000000 0032ebb0 687d8515 FXSCOVER!CDrawDoc::Remove+0x96
0016f24c 687d8515 0032ebb0 687d84df 0032ebb0 FXSCOVER!CDrawDoc::DeleteContents+0xc
0016f254 687d84df 0032ebb0 0032ebb0 0016f298 MFC42u!CDocument::OnNewDocument+0x15
0016f264 0086a812 0032ebb0 687d83e8 841d2315 MFC42u!COleDocument::OnNewDocument+0xe
0016f26c 687d83e8 841d2315 006b71f8 006b7274 FXSCOVER!CDrawDoc::OnNewDocument+0xa
0016f298 687d8598 00000000 00000001 841d22a5 MFC42u!CSingleDocTemplate::OpenDocumentFile+0x103
0016f328 008680fb 0032ebb0 00886000 008694f3 MFC42u!CDocManager::OnFileNew+0xaa
0016f334 008694f3 841d67a9 00000000 00888798 FXSCOVER!CDrawApp::OnFileNew+0xb
0016f37c 00868f62 006d3410 841d677d 00000000 FXSCOVER!CDrawApp::OpenDocumentFile+0x57a
0016f3a8 687e6572 00000000 00861de0 0016f3ec FXSCOVER!CDrawApp::OnFileOpen+0x4a
......
......

开启页堆后,我们可以看得更多, FXSCOVER!CDrawDoc::Remove之后调用析构函数,再调用msvcrt!free,再之后通过ntdll的函数真正去free

那我们尝试在msvcrt!free+0x33下断点,并输出要free的地址看看是不是double free

1
2
3
4
5
6
7
8
9
10
0:009> u msvcrt!free+0x33
msvcrt!free+0xc7:
765298c7 ff154c115276 call dword ptr [msvcrt!_imp__HeapFree (7652114c)]
765298cd 85c0 test eax,eax
765298cf 0f8478a90300 je msvcrt!free+0xd1 (7656424d)
765298d5 e8a1ffffff call msvcrt!_SEH_epilog4 (7652987b)
765298da c3 ret
765298db 90 nop
765298dc 90 nop
765298dd 90 nop

断点如下:

1
bp msvcrt!free+0x33 "r esi;!heap -p -a esi;gc"

跟着你就可以喝杯茶,回来看到最后一条记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
esi=01e47a10
address 01e47a10 found in
_HEAP @ 470000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
01e478d0 0057 0000 [00] 01e478d8 002b0 - (free)


Critical error detected c0000374
(4c8.230): Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=00000000 ecx=775907ed edx=0010f469 esi=00470000 edi=01e47a08
eip=7763280d esp=0010f6bc ebp=0010f734 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
ntdll!RtlReportCriticalFailure+0x29:
7763280d cc int 3

可以看到这时是去free一个已经free过了01e47a10,我们网上翻(其实是将记录复制下来ctrl+f),就可以看到下面这条记录

1
2
3
4
5
6
esi=01e47a10
address 01e47a10 found in
_HEAP @ 470000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
01e47a08 0027 0000 [00] 01e47a10 0012c - (busy)
FXSCOVER!CObject::`vftable'

所以非常明显,这是一个double free

补丁对比

其实这个很难调试去发现为什么会双重释放,补丁对比会发现为什么

因为是doc那个函数出的错,我们去看这个函数

增加了IsObjectAlreadySerialized的检测,如果已经序列化过了,说明已经free过了,以为序列化的时候会free

自愿打赏专区