# UAF on JSEthereumProvider

Brave's JSEthereumProvider binds isUnlocked() to base::Unretained(provider.get()); deleting window.ethereum then calling it dereferences freed memory, crashing the renderer.

- Payout: hidden
- Severity: critical
- Program: Brave Software
- Found by: nick0ve
- Disclosed: 2023-10-11
- Write-up quality: 8/10
- Original: https://hackerone.com/reports/1977252
- Library page: https://bugbountylibrary.com/r/h1%3A1977252
- Tags: area:os domain:browser impact:dos impact:rce kind:bounty platform:hackerone sev:critical tech:chromium technique:source-review vuln:memory-corruption

**Target:** brave-core renderer JS Ethereum provider (js_ethereum_provider.cc)
**Root cause:** The isUnlocked function template is bound with base::Unretained(provider.get()) while the _metamask object is stored as a property of the provider object; JS can retain _metamask after deleting window.ethereum, leaving a dangling raw pointer.
**Steps:**
1. Open a page in Brave with the Ethereum wallet connected so window.ethereum is installed.
2. Retain `let uafObj = ethereum._metamask;`
3. `delete ethereum;` to drop the provider handle.
4. Force GC with allocation loop (`new Array(1000000)` x100) to free the JSEthereumProvider.
5. `await uafObj.isUnlocked()` dereferences the freed provider via base::Unretained -> crash/UAF.
**How found:** Code review of JSEthereumProvider::Install: noticed _metamask is a separate v8::Object property of provider_object, so it can outlive the provider, while the isUnlocked callback uses base::Unretained(provider.get()) instead of a weak/gin-managed reference.
**Exploitation:** Attacker-controlled web page retains ethereum._metamask, deletes window.ethereum, triggers GC to free the provider, then invokes isUnlocked() to use the dangling pointer; author claims potential renderer code execution but only crash PoC shown.
**Insight:** When a native object exposes a sub-object (e.g. ethereum._metamask) as a plain v8::Object property, JS can keep that sub-object alive after the parent is deleted; any callback bound with base::Unretained to the parent becomes a dangling pointer. Audit gin::CreateFunctionTemplate bindings for Unretained on objects reachable independently from JS.
