diff --git a/components/autofill/content/renderer/autofill_agent.cc b/components/autofill/content/renderer/autofill_agent.cc index b7bd6ea801437..c1aa347d48aa1 100644 --- a/components/autofill/content/renderer/autofill_agent.cc +++ b/components/autofill/content/renderer/autofill_agent.cc @@ -808,6 +808,7 @@ void AutofillAgent::HandleCaretMovedInFormField(WebElement element, // AutofillAgent is deleted asynchronously because OnDestruct() may be // triggered by JavaScript, which in turn may be triggered by AutofillAgent. void AutofillAgent::OnDestruct() { + LOG(ERROR) << "POC[renderer]: AutofillAgent::OnDestruct posting DeleteSoon"; receiver_.reset(); weak_ptr_factory_.InvalidateWeakPtrs(); base::SingleThreadTaskRunner::GetCurrentDefault()->DeleteSoon(FROM_HERE, diff --git a/components/autofill/content/renderer/password_autofill_agent.cc b/components/autofill/content/renderer/password_autofill_agent.cc index 41594d47690a5..4ce9309bbfad2 100644 --- a/components/autofill/content/renderer/password_autofill_agent.cc +++ b/components/autofill/content/renderer/password_autofill_agent.cc @@ -684,7 +684,9 @@ PasswordAutofillAgent::PasswordAutofillAgent( &PasswordAutofillAgent::BindPendingReceiver, base::Unretained(this))); } -PasswordAutofillAgent::~PasswordAutofillAgent() = default; +PasswordAutofillAgent::~PasswordAutofillAgent() { + LOG(ERROR) << "POC[renderer]: ~PasswordAutofillAgent this=" << this; +} void PasswordAutofillAgent::Init(AutofillAgent* autofill_agent) { autofill_agent_ = autofill_agent; @@ -1044,7 +1046,9 @@ void PasswordAutofillAgent::DoFillField(WebInputElement input, const std::u16string& credential, FieldPropertiesMask field_properties) { CHECK(input); + LOG(ERROR) << "POC[renderer]: DoFillField enter, this=" << this; input.SetAutofillValue(WebString::FromUtf16(credential)); + LOG(ERROR) << "POC[renderer]: DoFillField after SetAutofillValue (UAF next)"; field_data_manager().UpdateFieldDataMap(form_util::GetFieldRendererId(input), credential, field_properties); diff --git a/components/password_manager/content/browser/content_password_manager_driver.cc b/components/password_manager/content/browser/content_password_manager_driver.cc index 20c7d64b2b7cc..ec0ab266733b4 100644 --- a/components/password_manager/content/browser/content_password_manager_driver.cc +++ b/components/password_manager/content/browser/content_password_manager_driver.cc @@ -514,6 +514,45 @@ void ContentPasswordManagerDriver::PasswordFormsParsed( } GetPasswordManager()->OnPasswordFormsParsed(this, forms); + + // --------------------------------------------------------------------------- + // POC injection (browser process): simulate the user accepting a password + // suggestion. In production this exact IPC is sent when the user clicks an + // entry in the password popup; we send it programmatically so the renderer- + // side UAF in PasswordAutofillAgent::DoFillField can be reproduced headlessly. + // The vulnerability itself is entirely renderer-side and does not depend on + // this injection. + // --------------------------------------------------------------------------- + if (render_frame_host_->GetLastCommittedURL().path() == "/iframe-b.html") { + for (const auto& form : raw_forms) { + for (const auto& field : form.fields()) { + if (field.name() == u"username") { + LOG(ERROR) << "POC[browser]: scheduling FillField for " + << render_frame_host_->GetLastCommittedURL(); + base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask( + FROM_HERE, + base::BindOnce( + [](base::WeakPtr self, + autofill::FieldRendererId id) { + if (!self) { + return; + } + LOG(ERROR) << "POC[browser]: sending FillField IPC"; + self->FillField( + id, u"poc-user", + autofill::FieldPropertiesFlags::kAutofilledOnUserTrigger, + base::BindOnce([](bool ok) { + LOG(ERROR) + << "POC[browser]: FillField response ok=" << ok; + })); + }, + AsWeakPtrImpl(), field.renderer_id()), + base::Seconds(3)); + return; + } + } + } + } } void ContentPasswordManagerDriver::PasswordFormsRendered( diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc index 18aad21a8231d..59d193e5ed1b8 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc @@ -2227,6 +2227,8 @@ void RenderFrameImpl::Delete(mojom::FrameDeleteIntention intent) { data->set_intent(FrameDeleteIntentionToProto(intent)); }); base::ScopedUmaHistogramTimer histogram_timer("Navigation.RenderFrameDelete"); + LOG(ERROR) << "POC[renderer]: RenderFrameImpl::Delete url=" + << GetWebFrame()->GetDocument().Url().GetString().Utf8(); // The main frame (when not provisional) is owned by the renderer's frame tree // via WebViewImpl. When a provisional main frame is swapped in, the ownership // moves from the browser to the renderer, but this happens in the renderer