An - Effective - Way - of - Bug - Hunting - Chrome 02564
An - Effective - Way - of - Bug - Hunting - Chrome 02564
#BHUSA @BlackHatEvents
About 360 Alpha Lab
memset
Data Flow memmove
1 class InstalledAppProviderImpl :
2 public blink::mojom::InstalledAppProvider
3 {
4 // ...
5 private:
6 RenderFrameHost* render_frame_host_;
7 };
An Example
Chrome Issue 1062091
1 void InstalledAppProviderImpl::Create(
2 RenderFrameHost* host,
3 mojo::PendingReceiver<blink::mojom::InstalledAppProvider>
receiver) {
4 mojo::MakeSelfOwnedReceiver(
5 std::make_unique<InstalledAppProviderImpl>(host),
6 std::move(receiver));
7 }
Finding Bug Variants
Candidates
Strore RFH as raw pointer in a member variable
Finding Bug Variants
Reduce false positives
WebContentsObserver
Class can get notified of page events by inheriting it
Give a chance to clean up when RFH is going away
FrameServiceBase
Wrapper class of WebContentsObserver
Work the same way
Finding Bug Variants
Candidates
Strore RFH as raw pointer in a member variable
Not a subclass of FrameServiceBase
Not a subclass of WebContentsObserver or the
RenderFrameDeleted method is not implemented
Finding Bug Variants
CodeQL query
Finding Bug Variants
RawClipboardHostImpl
Reported as issue 1117348
ERROR RETURN ISSUES
RenderFrameHost lifetime issue is a too common.
1 void BlobRegistryImpl::BlobUnderConstruction::TransportComplete(
2 …
3 if (context()->registry().HasEntry(uuid())) {
4 if (result == BlobStatus::DONE)
5 context()->NotifyTransportComplete(uuid()); std::map blobs_under_construction_.erase
6 else
7 context()->CancelBuildingBlob(uuid(), result);
8 } delete this
9 if (BlobStatusIsBadIPC(result)) {
10 std::move(bad_message_callback_)
11 .Run("Received invalid data while transporting blob");
12 }
13 …
Example - crbug/1065704
1 void WebSocket::ReadAndSendFromDataPipe() {
2 …
3 const size_t size_to_send =
4 std::min(static_cast<uint64_t>(readable_size), data_frame.data_length);
5 auto data_to_pass = base::MakeRefCounted<net::IOBuffer>(size_to_send);
6 const bool is_final = (size_to_send == data_frame.data_length); FailChannel
7 memcpy(data_to_pass->data(), buffer, size_to_send);
8 channel_->SendFrame(is_final, MessageTypeToOpCode(data_frame.type),
9 std::move(data_to_pass), size_to_send);
std::set connections_.erase
10
11 const MojoResult end_result = readable_->EndReadData(size_to_send);
12 DCHECK_EQ(end_result, MOJO_RESULT_OK);
13 … delete this
14 }
class A {
void A::Func(){
Root cause :
…
During the code execution of a class instance, Foo(); map.erase
1 bool X11WholeScreenMoveLoop::RunMoveLoop(
2 …
3 in_move_loop_ = true;
4 canceled_ = false;
5 base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
6 quit_closure_ = run_loop.QuitClosure();
7 run_loop.Run();
11 …
12 }
Nested message loop
Create a new message loop
1 bool X11WholeScreenMoveLoop::RunMoveLoop(
2 … Destroy instances in new loop
3 in_move_loop_ = true;
4 canceled_ = false;
5 base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); Exit the new loop
6 quit_closure_ = run_loop.QuitClosure();
7 run_loop.Run();
11 …
12 } Back to the blocking
context and continue code
execution
Save the context and create a new message loop in the current thread
1 void TabStrip::TabDragContextImpl::ContinueDrag(views::View* view, const
ui::LocatedEvent& event) {
Back to the special case 2 if (drag_controller_.get() &&
3 drag_controller_->event_source() == EventSourceFromEvent(event)) {
4 gfx::Point screen_location(event.location());
5 views::View::ConvertPointToScreen(view, &screen_location);
6 drag_controller_->Drag(screen_location);
7 }
8
9 // Note: |drag_controller| can be set to null during the drag above.
10 if (drag_controller_ && drag_controller_->group())
11 tab_strip_->UpdateTabGroupVisuals(*drag_controller_->group());
back to the context 12 }
1 bool X11WholeScreenMoveLoop::RunMoveLoop(
2 …
3 in_move_loop_ = true;
4 canceled_ = false;
5 base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
6 quit_closure_ = run_loop.QuitClosure();
CVE-2020-16004 7 run_loop.Run();
11 …
12 }
Nested message loop Results
Mojo IPC
Legacy IPC
control message
route message
Prior Knowledge
Ppapi
raw_x_ = x.get();
weakptr
sptr_x.reset();
raw_x_->DoSomething();
Stores in map |resources_|
wrapped as a weakptr
Keeped in file_system_host_
The Bug
could be passed in from the renderer-side
ResourceMap resources_;
base::WeakPtr<PepperFileSystemBrowserHost> file_system_host_
Exploit
Exploit
Need to construct the structure to meet the constraints on this path and hijack the control flow.
32GB limited!
[*] https://googleprojectzero.blogspot.com/2019/04/virtually-unlimited-memory-escaping.html
Exploit
1 void PepperFileIOHost::SendFileOpenReply(
2 …
3 if (pp_error == PP_OK) {
4 state_manager_.SetOpenSucceed();
5 // A non-zero resource id signals the plugin side to check quota.
6 if (check_quota_)
Reply 4 bytes to renderer process 7 quota_file_system = file_system_host_->pp_resource();
8 }
9 reply_context.params.set_result(pp_error);
10 host()->SendReply(
11 reply_context,
12 PpapiPluginMsg_FileIO_OpenReply(quota_file_system, max_written_offset_));
13 state_manager_.SetOperationFinished();
14 }
Exploit
Demo Video
CONCLUSION
Some background
WeakPtr Optimization
The exploit
THANKS!