Analyzing & Patching a DLL Reverse Shell
About The Project
On September 18th, The twitter account Malware Hunter Team Tweeted about a DLL, batch script, and PowerShell script being publicly hosted at 103[.]68[.]109[.]31. Given that a DLL was being hosted, I thought it would be an interesting target to reverse engineer. This blog post is analyzing that DLL and ultimately patching this simple reverse shell to call back to a local virtual machine.
OSINT
Initial triage with VirusTotal reports that some vendors detect the target IP address as malicious with numerous communicating files also flagged as malicious. Excellent! We’re off to a good start to analyze something that’s actually malicious.
Shodan reveals this IP address is located in Singapore, and based on reported data appears to be a Windows machine just hosted on the internet.
At the time of this analysis, numerous ports were open on this target IP address, but notably “1337” was open. An early spoiler, during the reverse engineering process, it was identified that this is indeed the port the malware connects on.
There were not any domain names associated with the IP address at the time of this analysis. Therefore, there were further domains to pivot on and ultimately a dead end with regards to gathering more detailed network information. Now we’ll move onto analyzing the files.
Download Cradles
At the time of this blog post initial access is unclear, but two download cradles exist. A batch and PowerShell script are hosted at the IP address of interest, 103[.]68[.]109[.]31. Both the batch and PowerShell script are simple download cradles ultimately setup to execute the DLL via calling an exported ordinal. A neat thing about DLLs, is that their exported functions can be called either by name or by ordinal. An ordinal is a numerical representation of the function to be called.
Not so long time ago some malware analysis/reverse engineering tools1 would not parse exported functions based on ordinal names alone. If a malicious actor build DLLs and exported ordinals, but no names, the tool would report no functions are exported. With ordinal 1 being the target of focus, lets pop the DLL into Ghidra and begin the analysis!
Analyzing The DLL
Upon initial loading and analyzing of the DLL, UPX section headers were identified.
This means our DLL is packed. Luckily, the malware author used an off-the-shelf copy of UPX; allowing for the DLL to be unpacked via executing upx -d rev.dll
After unpacking the DLL, and re-analyzing the binary in Ghidra the exported ordinal “1” was followed and our entrypoint into analysis can start! The image below shows a simple check to see if “1” is being provided as a CLI argument. If not, exit the program or continue onto the main c2 portion of the application.
The main C2 function begins by first sleeping for a period of time and then uses Win32 APIs to try and connect to an attacker controlled IP address. If the connection cannot be established, the malware will sleep for a period of time and then try again. The annotated decompilation can be seen below.
Something of note, you’ll see a function labeled “ordinal_9_htons
”.
What’s this about?
The malware author imports functionality from WS2_32.dll
(a Windows DLL that contains networking function calls) by ordinal rather than by exported name. At first I thought this could be an attempt at some type of anti-analysis. However, no attempt is used to obscure function calls imported by Kernel32. The Imports section of this DLL shown below.
Perhaps this is just a half-baked sample that’s not quite finished? Or maybe there’s something more going on that I’m aware of.
For the curious, a list of the corresponding ordinals to WS2_32.dll
functions can be found here
Once a connection has been established, cmd.exe
is spawned via CreateProcessW
and the attacker has a shell on the victim’s machine.
Overall, this is a very straightforward reverse shell implementation via a DLL. The name alone “rev.dll” gave away the functionality after all. To make further the analysis, and verify my understanding of the capabilities of this DLL, I’ll patch the attacker’s IP address with a local virtual machine’s IP address and obtain a shell on a test virtual machine.
Patching with Radare2
Referencing the .data
section, the hardcoded IPv4 address is located at 0x180014600
.
While its possible to patch this with Ghidra, I find exporting the binary a bit cumbersome.
To patch this, I’ll open the dll with radare2 (r2 -w rev.dll
), and then seek to the IP address and patch the bytes with my own IP address.
The output below shows the patching and corresponding verification via dumping 30 bytes at 0x180014600
.
Now to verify, I’ll host the binary via a simple http server and create a netcat listener on the original port the DLL connects to (port 1337). On the Windows host, I’ll execute rundll32 rev.dll,1
to cause the DLL to connect to the netcat listener. The image below shows the process tree of rundll32.exe
spawning cmd.exe
.
Inspecting the rundll32.exe
processes’ TCP/IP connections, we see a successful connection to my Ubuntu test host. and ultimately a cmd.exe
prompt on my terminal!
Beyond The Blog
The Malware Hunter Team twitter account frequently posts links to publicly exposed malware samples. Treating these posts like mini-CTFs with the goal or “flag” is to understand what that sample does helps gamify the analysis. The associated MD5s can be found in the IoC section below if you’re interested in re-creating the analysis. If you found this blog useful please consider sharing. If I got somethign wrong, please let me know!
Reference
IoCs
103[.]68[.]109[.]31 # IP
e07812e824fe9955d8933bfa7817b783f024e20fad3ec945a746f112c32c0c15 # rev.dll (packed w/ UPX)
f617ff7db9daac649434eab0a054e6bc8169df6ed496a3a9c9ba4bb924f7f392 # stage2.ps1
7775055c940a803de65a81a6b8948b8d0bb2e362fdc241535becf00c73e6a0d4 # run.bat