《附录八:关于Windows 计划任务导致内核提权漏洞CVE-2010-3338的分析与利用》 -By nEINEI 补丁更新的文件: 对于所有受支持的基于 x64 的 Windows 7 和 Windows Server 2008 R2 版本 Schtasks.exe 6.1.7600.16699 179,712 02-Nov-2010 04:34 x86 Schtasks.exe 6.1.7600.20830 179,712 02-Nov-2010 04:24 x86 Hashcleanup.exe 6.1.7600.16699 9,728 02-Nov-2010 04:34 x86 Hashcleanup.exe 6.1.7600.20830 9,728 02-Nov-2010 04:24 x86 Taskcomp.dll 6.1.7600.16699 305,152 02-Nov-2010 04:40 x86 Taskcomp.dll 6.1.7600.20830 305,152 02-Nov-2010 04:28 x86 Taskschd.dll 6.1.7600.16699 496,128 02-Nov-2010 04:40 x86 Taskschd.dll 6.1.7600.20830 505,856 02-Nov-2010 04:28 x86 Taskeng.exe 6.1.7600.16699 192,000 02-Nov-2010 04:34 x86 Taskeng.exe 6.1.7600.20830 192,000 02-Nov-2010 04:24 x86 D61d61c8-d73a-4eee-8cdd-f6f9786b7124.xml Not Applicable 3,188 10-Jun-2009 21:37 Not Applicable Schedsvc.dll 6.1.7600.16699 749,056 02-Nov-2010 04:39 x86 Schedsvc.mof Not Applicable 2,684 10-Jun-2009 21:37 Not Applicable D61d61c8-d73a-4eee-8cdd-f6f9786b7124.xml Not Applicable 3,188 10-Jun-2009 21:37 Not Applicable Schedsvc.dll 6.1.7600.20830 749,056 02-Nov-2010 04:28 x86 Schedsvc.mof Not Applicable 2,684 10-Jun-2009 21:37 Not Applicable Wmicmiplugin.dll 6.1.7600.16699 351,232 02-Nov-2010 04:41 x86 Wmicmiplugin.dll 6.1.7600.20830 351,232 02-Nov-2010 04:28 x86 该漏洞修补后,因为schedsvc.dll 中有校验。如果修改了一个计划任务的xml描述,那么计划任务的管理器会发现文件被篡改,但任务执行失败。 schedsvc.dll: 补丁前的文件: 文件版本:6.1.7600.16385;size:743,424 byte 补丁后的文件: 文件版本:6.1.7601.17514;size :750,592 byte; schedsvc.dll 在svchost.exe -k netsvcs进程中, 在RpcServer::RetrieveTask 中 _SchRpcRetrieveTask-》RpcServer::RetrieveTask-》 RpcServer::LoadXmlForUser-》JobStore::LoadTaskXml - 》 JobStore::ComputeCRC - 》 UpdateCRC32((unsigned __int8 *)a1, 2 * wcslen(a1), 0xFFFFFFFF); 有漏洞的版本: int __stdcall JobStore::LoadTaskXml(JobMoniker *a1, BSTR bstrString, wmi::RefBase *a3, int a4, int a5) { ... lpFileName = 0; v5 = (int)a1; v37 = 0; v6 = JobStore::GetRealPath(a1, (int)&lpFileName); if ( v6 < 0 ) { operator delete[]((void *)lpFileName); result = v6; } else { v7 = lpFileName; hFile = CreateFileW(lpFileName, 0x80000000, 5u, 0, 3u, 0x8000080u, 0); LOBYTE(v37) = 1; v31 = v7; if ( WPP_GLOBAL_Control != &WPP_GLOBAL_Control && *((_BYTE *)WPP_GLOBAL_Control + 28) & 8 ) WPP_SF_Sq( *((_DWORD *)WPP_GLOBAL_Control + 4), *((_DWORD *)WPP_GLOBAL_Control + 5), 34, dword_701E4D14, v7, (char)hFile); if ( hFile == (HANDLE)-1 ) { v25 = GetLastError(); if ( (signed int)v25 > 0 ) v25 = (unsigned __int16)v25 | 0x80070000; v19 = v25; if ( WPP_GLOBAL_Control != &WPP_GLOBAL_Control && *((_BYTE *)WPP_GLOBAL_Control + 28) & 1 ) WPP_SF_Sq(*((_DWORD *)WPP_GLOBAL_Control + 4), *((_DWORD *)WPP_GLOBAL_Control + 5), 35, dword_701E4D14, v7, v25); LOBYTE(v37) = 0; tsched::JobsAutoHandle::Close((tsched::JobsAutoHandle *)&hFile); operator delete[]((void *)v7); } else { v8 = a4; v35 = JobStore::LoadFileToBuffer(hFile, a4); // 这里将计划任务的xml描述文件加载到内存 if ( v35 < 0 ) { LABEL_11: LOBYTE(v37) = 0; tsched::JobsAutoHandle::Close((tsched::JobsAutoHandle *)&hFile); operator delete[]((void *)lpFileName); return v35; } // 如果可以加载到内存成功,那么计算crc32的值,存放在v32; v32 = JobStore::ComputeCRC(*(const unsigned __int16 **)v8); // 这里判断crc32的值是否相同; // 但没有用crc32值来进行判断xml修改后是否允许继续运行; v35 = *(_DWORD *)(*((_DWORD *)a1 + 6) + 12) != v32 ? 0x80041321 : 0; if ( !a3 || !*(_DWORD *)a3 ) { LABEL_8: if ( WPP_GLOBAL_Control != &WPP_GLOBAL_Control ) { if ( *((_BYTE *)WPP_GLOBAL_Control + 28) & 4 ) { v28 = *(_DWORD *)(*(_DWORD *)(v5 + 24) + 12); v29 = JobMoniker::GetPath((JobMoniker *)v5); WPP_SF_S_guid_DD( *((_DWORD *)WPP_GLOBAL_Control + 4), *((_DWORD *)WPP_GLOBAL_Control + 5), .... } 补丁后的版本: JobStore::LoadTaskXml { ... // 这里将计划任务的xml描述文件加载到内存 v41 = JobStore::LoadFileToBuffer(hFile, a4); if ( v41 < 0 ) { LABEL_40: LOBYTE(v46) = 0; tsched::JobsAutoHandle::Close((tsched::JobsAutoHandle *)&hFile); operator delete[]((void *)lpFileName); LABEL_38: JUMPOUT(nullsub_3); } v26 = *(BYTE **)a4; v33 = 32; if ( JobStore::ComputeHash(JobStore::m_pCommonStore, v26, &v44, &v33) ) // 计算一下hash { if ( WPP_GLOBAL_Control != &WPP_GLOBAL_Control ) { if ( *((_BYTE *)WPP_GLOBAL_Control + 28) & 8 ) { v27 = JobMoniker::GetPath(v39); WPP_SF_S(*((_DWORD *)WPP_GLOBAL_Control + 4), *((_DWORD *)WPP_GLOBAL_Control + 5), 38, v25, v27); } } goto LABEL_40; } if ( a6 == 1 ) { v7 = *(const unsigned __int16 **)a4; qmemcpy(&v45, &v44, 0x20u); v8 = JobStore::ComputeCRC(v7); // 这里crc32 已经被修改; v9 = v39; if ( *(_DWORD *)(*((_DWORD *)v39 + 6) + 12) != v8 ) HashCompute::SpoilHash(&v45, 0x20u); JobMoniker::SetHash(v9, &v45); } v28 = *((_DWORD *)v39 + 15); if ( v28 ) v29 = (char *)v39 + 28; else v29 = 0; if ( !v29 ) goto LABEL_40; if ( v28 ) v30 = (char *)v39 + 28; else v30 = 0; if ( memcmp(&v44, v30, 0x20u) ) // 这里失败 { *(_QWORD *)(*((_DWORD *)v39 + 6) + 24) = Triggers::Trigulator::GetSpoiledVersion(); goto LABEL_40; } } 综上,如果能通过修改xml启动进程的权限等级,同时对修改后的文件进行crc32的碰撞即可完成整个攻击过。