This post documents my exploration of implementing a reverse shell in Rust, starting from a standard API approach, and gradually shifting toward a stealthier variant that leverages manual parsing of the Process Environment Block (PEB). The goal is to reduce detection by Antivirus (AV) and Endpoint Detection and Response (EDR) engines.
Initial Implementation: Traditional API Call Chain
To begin, I created a basic reverse shell using the typical Windows API calls. The logic flow is straightforward and relies on functions exposed by common system libraries like Ws2_32.dll
and kernel32.dll
.
Reverse Shell Flow
WSASocket
↓
inet_pton
↓
connect
↓
GetSystemDirectoryA
↓
Initialize STARTUPINFOA
↓
Set lpCommandLine
↓
Initialize SECURITY_ATTRIBUTES
↓
CreateProcessA
↓
Finish
- WSASocket / connect / inet_pton: Used to set up a TCP connection to the remote host.
- GetSystemDirectoryA: Retrieves the system path, used to locate cmd.exe.
- STARTUPINFOA / SECURITY_ATTRIBUTES / CreateProcessA: Used to spawn a new process (cmd.exe) with redirected input/output over the socket.
After compiling the shell, I submitted the resulting binary to VirusTotal. The community score was 28/72 — meaning 28 out of 72 AV/EDR engines flagged the executable as malicious. Most detections explicitly identified it as a reverse shell, which is expected due to the predictable API usage.
Improved Version: PEB-Based API Resolution
To evade static detection signatures, I rewrote the shell to resolve necessary APIs dynamically by walking the PEB instead of relying on the standard Windows API imports. This eliminates the need for a conventional import table and makes detection more difficult for AV engines relying on heuristic patterns or IAT analysis.
Revised Flow with Manual API Resolution
WSAStartup (resolved from PEB)
↓
WSASocket (resolved from PEB)
↓
inet_pton (resolved from PEB)
↓
connect (resolved from PEB)
↓
GetSystemDirectoryA (resolved from PEB)
↓
Initialize STARTUPINFOA
↓
Set lpCommandLine
↓
Initialize SECURITY_ATTRIBUTES
↓
CreateProcessA (resolved from PEB)
↓
Finish
- All API addresses were resolved manually by parsing the export tables of
kernel32.dll
andWs2_32.dll
, accessed through the PEB. - No functions were declared in the import table, making the binary significantly harder to analyze via static disassembly or automated AV heuristics.
The resulting binary was again uploaded to VirusTotal. This time, the detection score dropped drastically to 2/72, with only Google and IKARUS flagging the file as suspicious. This demonstrates a significant improvement in stealthiness — achieved purely through API obfuscation and avoiding direct imports.
Today, I obtained a new VirusTotal scan result — this time, the detection score dropped to 11 out of 66.
Top comments (0)