This document provides an introduction to shellcode development. It defines different types of shellcode like staged and egg hunt shellcodes. It discusses limitations of shellcodes and techniques to avoid them. It also provides an overview of assembly language programming, Linux and Windows system calls, and how to write basic Linux and Windows shellcodes to execute commands. The document aims to teach penetration testers and security researchers how to develop their own machine code payloads.
This document provides an introduction to shellcode development. It defines different types of shellcode like staged and egg hunt shellcodes. It discusses limitations of shellcodes and techniques to avoid them. It also provides an overview of assembly language programming, Linux and Windows system calls, and how to write basic Linux and Windows shellcodes to execute commands. The document aims to teach penetration testers and security researchers how to develop their own machine code payloads.
This document provides an introduction to shellcode development. It defines different types of shellcode like staged and egg hunt shellcodes. It discusses limitations of shellcodes and techniques to avoid them. It also provides an overview of assembly language programming, Linux and Windows system calls, and how to write basic Linux and Windows shellcodes to execute commands. The document aims to teach penetration testers and security researchers how to develop their own machine code payloads.
This document provides an introduction to shellcode development. It defines different types of shellcode like staged and egg hunt shellcodes. It discusses limitations of shellcodes and techniques to avoid them. It also provides an overview of assembly language programming, Linux and Windows system calls, and how to write basic Linux and Windows shellcodes to execute commands. The document aims to teach penetration testers and security researchers how to develop their own machine code payloads.
Download as PPTX, PDF, TXT or read online from Scribd
Download as pptx, pdf, or txt
You are on page 1of 42
Ionut Popescu
Penetration Tester @ KPMG Romania
http://www.kpmg.com/ro/en/Pages/default.aspx Administrator @ Romanian Security Team https://www.rstforums.com Introduction to Shellcode Development Contents 1. Introduction 2. C/C++ compiling 3. Running shellcodes (do not) 4. Simple BOF example 5. Shellcode limitations 6. ASM introduction 7. Linux syscalls 8. Linux shellcode example 9. Windows shellcodes 10. Disassemble shellcode 11. Find kernel32 12. Find GetProcAddress 13. Find LoadLibrary 14. Load a DLL 15. Call functions from DLL 16. Download and execute 17. More about shellcodes 18. Contact Introduction Shellcodes: In computer security, a shellcode is a small piece of code used as the payload in the exploitation of a software vulnerability. It is called "shellcode" because it typically starts a command shell from which the attacker can control the compromised machine, but any piece of code that performs a similar task can be called shellcode. Shellcode is commonly written in machine code.
Staged: When the amount of data that an attacker can inject into the target process is too limited to execute useful shellcode directly, it may be possible to execute it in stages. First, a small piece of shellcode (stage 1) is executed. This code then downloads a larger piece of shellcode (stage 2) into the process's memory and executes it.
Egg hunt: This is another form of staged shellcode, which is used if an attacker can inject a larger shellcode into the process but cannot determine where in the process it will end up. Small egg-hunt shellcode is injected into the process at a predictable location and executed. This code then searches the process's address space for the larger shellcode (the egg) and executes it.
Omlette: This type of shellcode is similar to egg-hunt shellcode, but looks for multiple small blocks of data (eggs) and recombines them into one larger block (the omelet) that is subsequently executed. This is used when an attacker can only inject a number of small blocks of data into the process C/C++ compiling Shellcode machine code Running shellcodes (DO NOT) DO NOT RUN on your machine! Use a testing purposes virtual machine! It can contain: download and execute code, rm rf ... Simple BOF example C program does not check for parameter length before copying data into c variable (it is a local variable so it is pushed on the stack).
So it is possible to corrupt the stack and modify the Return Address in order to execute custom code. This code takes an argument from the command line and copies it to a local stack variable c. This works fine for command line arguments smaller than 12 characters. Any arguments larger than 11 characters long will result in corruption of the stack. Shellcode limitations Limitations:
- NULL free (may not contain a NULL character most common) - Small size (may have a limited space to run) - Alphanumeric (may need to be alphanumeric) - Detection (may be detected by antivirus or IDS/IPS) - Difficult (may really complicated to write your own shellcode)
What to do:
- Avoid \x00 instructions - Egg hunter/omlette - Encode shellcode (msfencode) Assembly introduction Processor understands only machine language instructions which are strings of 1s and 0s. However machine language is too obscure and complex for using in software development. So the low level assembly language is designed for a specific family of processors that represents various instructions in symbolic code and a more understandable form.
It requires less memory and execution time; It allows hardware-specific complex jobs in an easier way; It is suitable for time-critical jobs; It is most suitable for writing interrupt service routines and other memory resident programs.
Processor registers Pointer & Index registers Pointer Registers The pointer registers are 32-bit EIP, ESP and EBP registers and corresponding 16-bit right portions. IP, SP and BP. There are three categories of pointer registers: Instruction Pointer (IP) - the 16-bit IP register stores the offset address of the next instruction to be executed. IP in association with the CS register (as CS:IP) gives the complete address of the current instruction in the code segment. Stack Pointer (SP) - the 16-bit SP register provides the offset value within the program stack. SP in association with the SS register (SS:SP) refers to be current position of data or address within the program stack. Base Pointer (BP) - the 16-bit BP register mainly helps in referencing the parameter variables passed to a subroutine. The address in SS register is combined with the offset in BP to get the location of the parameter. BP can also be combined with DI and SI as base register for special addressing.
Index Registers The 32-bit index registers ESI and EDI and their 16-bit rightmost portions SI and DI are used for indexed addressing and sometimes used in addition and subtraction. There are two sets of index pointers: Source Index (SI) - it is used as source index for string operations Destination Index (DI) - it is used as destination index for string operations.
Processor instructions mov dest, src ; The data specified by src is copied to dest. One restriction is that both operands may not be memory operands.
mov eax, 3 ; Store 3 into EAX register (3 is immediate operand) mov bx, ax ; Store the value of AX into the BX register
The ADD instruction is used to add integers.
add eax, 4 ; eax = eax + 4 add al, ah ; al = al + ah
The SUB instruction subtracts integers.
sub bx, 10 ; bx = bx - 10 sub ebx, edi ; ebx = ebx edi
The INC and DEC instructions increment or decrement values by one. Since the one is an implicit operand, the machine code for INC and DEC is smaller than for the equivalent ADD and SUB instructions.
inc ecx ; ecx++ dec dl ; dl-- Processor instructions
- ADD - Sum - SUB - Substraction - INC - Increment - DEC Decrement - CALL Call function - CMP Compare operands - DIV Devide - JMP Jump - MOV Move - NOP No operation - MUL Multiply - POP Pop data from stack - PUSH Push data onto stack - RET Return from procedure - XOR Exclusive OR - LODSD Load DWORD at address ESI into EAX - XCHG Exchange data - LEA Load Effective address Stack push 99 push 88 push 77 call foobar -------- push EBP mov EBP, ESP sub esp, 16
; Put parameters on the stack ; CALL will put next EIP on the stack
; Create a new stackframe ; Save EBP and replace it with ESP ; Stack space for local variables Stack ; 99 ; 88 ; 77
; Added by CALL
; Preserved EBP (start of frame)
; xx, yy, zz, sum ; Local parameters of function
; ESP (end of frame) Linux syscalls int 0x80 is the assembly language instruction that is used to invoke system calls in Linux on x86 (i.e., Intel-compatible) processors.
Each process starts out in user mode. When a process makes a system call, it causes the CPU to switch temporarily into kernel mode, which has root (i.e., administrative) privileges, including access to any memory space or other resources on the system. When the kernel has satisfied the process's request, it restores the process to user mode.
When a system call is made, the calling of the int 0x80 instruction is preceded by the storing in the process register (i.e., a very small amount of high-speed memory built into the processor) of the system call number (i.e., the integer assigned to each system call) for that system call and any arguments (i.e., input data) for it. Linux syscalls Syscall Kernel API (interface between usermode and kernelmode) Linux shellcode example jmp short ender
starter:
xor eax, eax ;clean up the registers xor ebx, ebx xor edx, edx xor ecx, ecx
mov al, 4 ;syscall write mov bl, 1 ;stdout is 1 pop ecx ;get the address of the string from the stack mov dl, 5 ;length of the string int 0x80
xor eax, eax mov al, 1 ;exit the shellcode xor ebx,ebx int 0x80
ender: call starter ;put the address of the string on the stack db 'hello' Windows shellcodes 1. Find kernel32.dll 2. Find GetProcAddress 3. Find LoadLibrary 4. Load DLLs 5. Call random functions
PE File Format The Portable Executable (PE) format is a file format for executables, object code, DLLs, and others used in 32-bit and 64-bit versions of Windows operating systems. The PE format is a data structure that encapsulates the information necessary for the Windows OS loader to manage the wrapped executable code. This includes dynamic library references for linking, API export and import tables, resource management data and thread-local storage (TLS) data. On NT operating systems, the PE format is used for EXE, DLL, SYS (device driver), and other file types. General PE File Structure MS-DOS Header MS-DOS header only, opened in a hex editor. Notable strings: it starts with MZ and it contains the following text: This program cannot be run in DOS mode. MS-DOS Header typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header WORD e_magic; // Magic number WORD e_cblp; // Bytes on last page of file WORD e_cp; // Pages in file WORD e_crlc; // Relocations WORD e_cparhdr; // Size of header in paragraphs WORD e_minalloc; // Minimum extra paragraphs needed WORD e_maxalloc; // Maximum extra paragraphs needed WORD e_ss; // Initial (relative) SS value WORD e_sp; // Initial SP value WORD e_csum; // Checksum WORD e_ip; // Initial IP value WORD e_cs; // Initial (relative) CS value WORD e_lfarlc; // File address of relocation table WORD e_ovno; // Overlay number WORD e_res[4]; // Reserved words WORD e_oemid; // OEM identifier (for e_oeminfo) WORD e_oeminfo; // OEM information; e_oemid specific WORD e_res2[10]; // Reserved words LONG e_lfanew; // File address of new exe header } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
BYTE 8 bits (1 byte), unsigned char CHAR 8 bits (1 byte), char DWORD 4 bytes (32 bits) unsigned long LONG 4 bytes (32 bits) long ULONGLONG 8 bytes (64 bits) unsigned long long WORD 2 bytes (16 bits) unsigned short PE Header MS-DOS header specifies (e_lfanew) the start of PE header. PE Header structures typedef struct _IMAGE_NT_HEADERS { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER32 OptionalHeader; } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32; typedef struct _IMAGE_FILE_HEADER { WORD Machine; WORD NumberOfSections; DWORD TimeDateStamp; DWORD PointerToSymbolTable; DWORD NumberOfSymbols; WORD SizeOfOptionalHeader; WORD Characteristics; } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; typedef struct _IMAGE_OPTIONAL_HEADER { WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseOfCode; DWORD BaseOfData; DWORD ImageBase; DWORD SectionAlignment; DWORD FileAlignment; WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion;
WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; DWORD Win32VersionValue; DWORD SizeOfImage; DWORD SizeOfHeaders; DWORD CheckSum; WORD Subsystem; WORD DllCharacteristics; DWORD SizeOfStackReserve; DWORD SizeOfStackCommit; DWORD SizeOfHeapReserve; DWORD SizeOfHeapCommit; DWORD LoaderFlags; DWORD NumberOfRvaAndSizes; IMAGE_DATA_DIRECTORY DataDirectory[16]; } Data Directory Image section table #define IMAGE_SIZEOF_SHORT_NAME 8 typedef struct _IMAGE_SECTION_HEADER { BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; union { DWORD PhysicalAddress; DWORD VirtualSize; } Misc; DWORD VirtualAddress; DWORD SizeOfRawData; DWORD PointerToRawData; DWORD PointerToRelocations; DWORD PointerToLinenumbers; WORD NumberOfRelocations; WORD NumberOfLinenumbers; DWORD Characteristics; } #define IMAGE_SIZEOF_SECTION_HEADER 40 Executable code section, .text The .text section also contains the entry point mentioned earlier. The IAT also lives in the .text section immediately before the module entry point. Data sections, .bss, .rdata, .data
The .bss section represents uninitialized data for the application, including all variables declared as static within a function or source module. The .rdata section represents read-only data, such as literal strings, constants, and debug directory information. All other variables (except automatic variables, which appear on the stack) are stored in the .data section. Basically, these are application or module global variables.
The .rsrc section contains resource information for a module. It begins with a resource directory structure like most other sections, but this section's data is further structured into a resource tree. The IMAGE_RESOURCE_DIRECTORY, shown below, forms the root and nodes of the tree. PE exports & imports table To parse the imports table, we need to iterate through all the functions with two pointers: one for the name of the function and the other for the address of the function. Verify shellcodes Disassemble and understand shellcodes. Convert text shellcodes Step 1, text shellcode:
.................................................. NASM: http://www.nasm.us/ Process Environment Block In computing the Process Environment Block (abbreviated PEB) is a data structure in Win32. It is an opaque data structure that is used by the operating system internally, most of whose fields are not intended for use by anything other than the operating system.[1] Microsoft notes, in its MSDN Library documentation which documents only a few of the fields that the structure "may be altered in future versions of Windows".[2] The PEB contains data structures that apply across a whole process, including global context, startup parameters, data structures for the program image loader, the program image base address, and synchronization objects used to provide mutual exclusion for process-wide data structures. struct PEB *GetPEB() { __asm { mov eax ,fs:30h } } typedef struct _PEB { ... PPEB_LDR_DATA Ldr; // 0xC ... } PEB, *PPEB; typedef struct _PEB_LDR_DATA { ... LIST_ENTRY InLoadOrderModuleList; LIST_ENTRY InMemoryOrderModuleList; // 0x14 LIST_ENTRY InInitializationOrderModuleList; ... } PEB_LDR_DATA, *PPEB_LDR_DATA; Find kernel32.dll 00000000 33C9 xor ecx,ecx ; ECX = 0 00000002 648B4130 mov eax,[fs:ecx+0x30] ; EAX = PEB 00000006 8B400C mov eax,[eax+0xc] ; EAX = PEB->Ldr 00000009 8B7014 mov esi,[eax+0x14] ; ESI = PEB->Ldr.InMemOrder 0000000C AD lodsd ; EAX = Second module 0000000D 96 xchg eax,esi ; EAX = ESI, ESI = EAX 0000000E AD lodsd ; EAX = Third (kernel32) 0000000F 8B5810 mov ebx,[eax+0x10] ; EBX = Base address 00000012 8B533C mov edx,[ebx+0x3c] ; EDX = DOS->e_lfanew 00000015 03D3 add edx,ebx ; EDX = PE Header 00000017 8B5278 mov edx,[edx+0x78] ; EDX = Offset export table 0000001A 03D3 add edx,ebx ; EDX = Export table 0000001C 8B7220 mov esi,[edx+0x20] ; ESI = Offset names table 0000001F 03F3 add esi,ebx ; ESI = Names table 00000021 33C9 xor ecx,ecx ; EXC = 0 Find GetProcAddress 00000023 41 inc ecx ; Loop for each function 00000024 AD lodsd 00000025 03C3 add eax,ebx ; Loop untill function name