## https://sploitus.com/exploit?id=75D8AF60-1BE7-5841-A5AC-CC59A30D14EB
# CVE-2026-13036 β Use-After-Free in Blink (`WidgetBase::UpdateSurfaceAndScreenInfo`)





> A use-after-free vulnerability in Google Chrome's Blink rendering engine that
> allows a remote attacker to execute arbitrary code inside the renderer sandbox
> via a crafted HTML page. The flaw lives in the widget compositing layer
> (`WidgetBase`) and is reached through a re-entrant screen-orientation callback.
| | |
|---|---|
| **CVE** | CVE-2026-13036 |
| **CWE** | CWE-416 (Use After Free) |
| **CVSS 3.1** | 8.8 (High) β `AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H` |
| **Component** | Blink β `third_party/blink/renderer/platform/widget/widget_base.cc` |
| **Fixed in** | Chrome **149.0.7827.197** (Windows / macOS), **149.0.7827.196** (Linux) |
| **Vulnerable** | Chrome **OrientationChanged(); // DidUpdateSurfaceAndScreen(previous_original_screen_infos); // UAF
}
```
`client_->OrientationChanged()` dispatches DOM events **synchronously** on the
renderer's main thread. An attacker-controlled handler can, during that
callback, tear down the very frame that owns this `WidgetBase` β e.g. by
removing the hosting `` from the parent document, or by navigating /
closing the frame. That destroys the `WidgetBase` object.
Control then returns into `UpdateSurfaceAndScreenInfo`, which keeps using the
now-freed `this` (`screen_infos_`, `client_`, the call to
`DidUpdateSurfaceAndScreen`). Subsequent access reads/writes freed memory β
**use-after-free**.
## The Fix
The patch takes a weak pointer to `this` *before* the re-entrant callback and
bails out if the object was destroyed during it:
```cpp
// third_party/blink/renderer/platform/widget/widget_base.cc (PATCHED)
if (orientation_changed) {
auto weak_this = weak_ptr_factory_.GetWeakPtr();
client_->OrientationChanged();
if (!weak_this) {
return; // WidgetBase was destroyed β stop.
}
}
client_->DidUpdateSurfaceAndScreen(previous_original_screen_infos);
```
> Commit message: *"Add a weak pointer check after calling
> `client_->OrientationChanged()` because this call can cause the `WidgetBase`
> object to be destroyed."*
---
## How the PoC Triggers It
A renderer widget (`WidgetBase`) is created per **local root frame**. The PoC
embeds a child frame, forces an orientation change for that frame's widget, and
destroys the frame from inside the orientation event handler β exactly the
re-entrant free the patch guards against.
`poc.html` implements **three independent trigger strategies**:
1. **Orientation lock + frame removal** β the child enters fullscreen and calls
`screen.orientation.lock()` to force an `orientation_changed` state; its
`change` handler removes the `` from the parent (frees `WidgetBase`).
2. **Emulated rotation + self-navigation** β drives the orientation change via
responsive/rotation emulation and, in the handler, navigates the frame
(`location.replace('about:blank')`) to detach the widget mid-callback.
3. **`requestAnimationFrame` re-entry** β schedules the teardown on the next
animation frame so the free lands while the surface/screen update is still
on the stack, widening the race window.
Each strategy first performs **heap grooming** β allocating a batch of
same-sized objects so the freed `WidgetBase` slot is promptly reclaimed by
attacker-controlled data, making the dangling read observable.
## Usage
```bash
# Serve over HTTP (fullscreen + orientation APIs require a secure/served origin)
python -m http.server 8000
# then open http://localhost:8000/poc.html in a target Chrome build
```
For a faithful out-of-process widget (OOPIF), host `victim_frame.html` on a
**different origin** than `poc.html` (e.g. `127.0.0.1:8000` vs `localhost:8000`)
so the child frame gets its own renderer widget. Same-origin still exercises the
code path for local-root subframes.
## Delivering the Orientation Change (important on desktop)
`screen.orientation.lock()` is **mobile-only** β on desktop Chrome it throws
`NotSupportedError`, so the page cannot self-trigger `OrientationChanged()`. If
you just open `poc.html` you'll see the frame report *"lock() unsupported on
desktop β ARMED and waitingβ¦"* and **no crash** β the vulnerable callback never
ran. The orientation change must be delivered by **emulation**:
**Option A β automated (recommended):**
```bash
python run_vuln.py "C:\path\to\vulnerable\chrome.exe" # e.g. 148.0.7778.179
```
`run_vuln.py` uses **only the Python 3 standard library** (no `pip install`): it
ships a tiny built-in WebSocket client, launches the target in an isolated
profile, and flips the emulated `screenOrientation` over CDP
(`Emulation.setDeviceMetricsOverride`) while the frame is armed, then watches for
`Inspector.targetCrashed`.
**Option B β manual:** open `poc.html`, open DevTools (`F12`) β device toolbar
(`Ctrl+Shift+M`) β pick a phone β click **Run all**, then repeatedly hit the
**rotate** button. Each rotate delivers the screen-info orientation change.
## Why It Might Not Crash
| Symptom | Cause |
|---|---|
| `lock() unsupported on desktop β¦ ARMED` and nothing happens | No orientation change delivered β use Option A or B above. |
| No crash even after rotating | You're on a **patched** build (β₯ 149.0.7827.197, or a post-fix 148.0.7778.x). The weak-pointer guard returns early. |
| Crash only sometimes | UAF is a race β repeat the flips; an ASAN build makes it deterministic. |
> Note: the exact synchronous re-entrancy that frees the `WidgetBase` is detailed
> only in the restricted Chromium bug 523711130. This PoC models the documented
> `OrientationChanged()` trigger surface from the public fix; reproducing the
> free reliably may require the specific frame arrangement from the original
> report and a vulnerable, ideally ASAN, build.
## Expected Behavior
| Build | Result |
|---|---|
| Chrome **
- Chromium bug 523711130 (restricted)
- Patch: `WidgetBase::UpdateSurfaceAndScreenInfo` weak-pointer guard