# CVE-2021-26411: Internet Explorer MSHTML Double-Free

MSHTML removeAttributeNode double-frees an attribute value when a valueOf callback calls clearAttributes, enabling type confusion and arbitrary read/write in IE.

- Payout: Real-world hack
- Severity: high
- Program: Microsoft Internet Explorer
- Credited: Maddie Stone
- Disclosed: 2021-03-09
- CVE: CVE-2021-26411
- Write-up quality: 9/10
- Original: https://github.com/googleprojectzero/0days-in-the-wild/blob/main/0day-RCAs/2021/CVE-2021-26411.md
- Library page: https://bugbountylibrary.com/r/p0%3ACVE-2021-26411
- Tags: domain:browser impact:rce kind:incident platform:project-zero sev:high tech:windows technique:chained technique:fuzzing technique:source-review vuln:dos vuln:memory-corruption

**Target:** MSHTML attribute handling (removeAttributeNode / CAttrArray)
**Root cause:** ie9_removeAttributeNodeInternal computes attribute-array indices, then invokes a user-controlled valueOf callback before using them; clearAttributes reallocates/frees the backing store during the callback, so the stale index causes a double free of the attribute's String value.
**Steps:**
1. Create element `ele` and attribute `attr1` whose nodeValue is an object with a `valueOf` callback.
2. `ele.setAttributeNode(attr1)` then `ele.setAttribute('attr2','AAAAAAAA')` so the array holds two entries.
3. `ele.removeAttributeNode(attr1)` triggers `ie9_removeAttributeNodeInternal`, which computes indices then calls valueOf.
4. In valueOf, call `ele.clearAttributes()` to free/memmove the backing store, leaving the freed slot holding the last attribute's String.
5. Return to removeAttributeNodeInternal, which frees the stale index again — double free of the String object.
**How found:** Not stated for the original finder; PZ notes it could plausibly come from fuzzing HTML attributes (e.g. Domato) or auditing call sites of CBase::GetIntoBSTRAt/VariantChangeTypeSpecial where a callback fires.
**Exploitation:** Double free lets a Scripting.Dictionary VARIANT array (type 0x200C) and a BSTR of length 0x200C occupy the same heap block, creating type confusion. The Dictionary leaks object addresses via VBArray dereference, then a write-what-where trick (Zuckerbraun) corrupts an ArrayBuffer to base 0x00000000 length 0xFFFFFFFF for arbitrary read/write. CFG bypassed by overwriting rpcrt4.dll's `___guard_check_icall_fptr` export entry with `KiFastSystemCallRet`. Delivered via a .mht file to security researchers.
**Escalation:** Type confusion → address leak → arbitrary read/write → CFG bypass → code execution in the browser.
**Insight:** Audit any code path that computes an index or pointer, then calls a user-controlled conversion (valueOf/toString/VariantChangeType) before using it — reentrant DOM calls like clearAttributes can reallocate the backing store and invalidate the saved index.
