Peeling Back the Layers on Powershell Empire

This blog post is the second in the series on Powershell Empire. If you missed the first post, you can find it here.

Multi/launcher: How does it work?

In the previous post, we used the multi/launcher Powershell stager to create an Empire agent. We also disabled our antivirus, Windows Defender, for testing purposes. This time we will leave it on and try to bypass it.

Empire's multi/launcher Powershell one-liner payload
Empire's `multi/launcher` Powershell one-liner payload

 As expected, an unmodified multi/launcher payload is detected and blocked by Defender.

Defender blocking our malicious `multi/launcher` oneliner
Defender blocking our malicious `multi/launcher` one-liner

What actually happens when we paste this into the Windows command prompt? Let's analyze this one-liner to figure out what it does step by step. The first step to find out is to disable the base64 encoding on the generated Powershell one-liner. To do this, simply set Base64 False.

The same oneliner as above without the Base64 encoding
The same one-liner as above without the Base64 encoding

As in other languages like Bash or Python, Powershell uses the semicolon character to stack lines of code on the same line. The above code really runs multiple Powershell commands one after the other. We can remove the semicolons and place each command on its own line for readability.

The same Powershell code as above with each command placed on its own line
The same Powershell code as above with each command placed on its own line

This is the first stage of the Empire agent, called Stage0 by the Empire Project.

A few things to notice here:

  • Since Powershell is not case sensitive, the stager uses random case wherever possible to throw off simple string detections. This obfuscation isn't very effective these days.
  • The stager creates a new WebClient object and sets the domain and port ($ser), user agent ($u),  and page ($t) to connect back to the Empire server. The stager uses this WebClient to download the next stage, Stage1, from the Empire server.
  • Empire generates a random key ($k) for the stager that can decrypt the downloaded data into Stage1 Powershell code.
  • Lastly, the downloaded Stage1 script is run in memory via Invoke-Expression, which in this case is abbreviated to IEX.

If we remove the IEX at the end, we can see the downloaded and decrypted data instead of executing it.

Viewing the downloaded data after removing `IEX` from the end of the Stage0 oneliner
Viewing the downloaded data after removing `IEX` from the end of the Stage0 oneliner

Again, the Empire stagers are deliberately meant to be hard to read, so we need to clean up this code before we can analyze it.

Below is the prettified Stage1 code:

Stage1 code largely does the same thing as Stage0 code. It performs a key exchange with the server to set up AES encryption. It uses the previous WebClient to download the final Stage2 Empire agent code. It then decrypts and runs it.

To download (instead of run) the Stage2 Empire code, we can replace the Invoke-Expression with a Write-Output.

Replaced `IEX` with `Write-Output` and viewing the beginning of Stage2 code 
Replaced `IEX` with `Write-Output` and viewing the beginning of Stage2 code 

Finally the Stage2 downloaded code is Empire's actual agent code.

Defending ourselves from the Defenders.

Powershell has matured from the early days of its release, and it is now easier than ever for defenders to gain visibility into the Powershell run on their computers. For this reason, it's a good assumption that Antivirus/other security software is looking every line of Powershell you run.

We can use Event Viewer to view Powershell execution logs at the following path: Event Viewer > Applications and Services Logs > Microsoft > Windows > Powershell > Operational.

A Scriptblock Log showing Empire's obviously evil Powershell code
A Scriptblock Log showing Empire's obviously evil Powershell code

The good news is no human being can look at all of these Powershell logs. The bad news is security software will be. When modifying Empire, we only need to bypass automated detection.

As a co-worker of mine once said, "Antivirus is just fancy Grep." Antivirus simply scans for suspicious strings in our Powershell code. The solution: remove all of the "dirty words" that Antivirus would potentially be looking for. Function names are a good place to start. Here are a list of function names taken from Stage1 and Stage2 that could give us away:

  • Start-Negotiate
  • ConvertTo-Rc4ByteStream
  • Get-HexString
  • Set-Delay
  • Get-Delay
  • Set-LostLimit
  • Get-LostLimit
  • Set-Killdate
  • Get-Killdate
  • Set-WorkingHours
  • Get-WorkingHours
  • Get-Sysinfo
  • Invoke-ShellCommand
  • Start-AgentJob
  • Get-AgentJobCompleted
  • Receive-AgentJob
  • Stop-AgentJob
  • Update-Profile
  • Get-FilePart
  • Encrypt-Bytes
  • Decrypt-Bytes
  • New-RoutingPacket
  • Decode-RoutingPacket
  • Encode-Packet
  • Decode-Packet
  • Process-Tasking
  • Process-TaskingPackets

Empire already does function name aliasing for the two worst offenders, Invoke-Empire and Invoke-Mimikatz, which means these two don't make the list.

There are probably better ways to change these function names in Empire, but a little bit of sed will do the job.

Run this script from the root of the Empire directory to replace all instances of these function names. When we look at the event log, we can see these function names are replaced with random names.

Replaced function names in the Empire agent core
Viewing the replaced function names in Event Viewer

This replacement by itself isn't quite enough to bypass Defender. The replacements above only affect Stage1 and Stage2, but do not modify the multi/launcher Stage0. We need to change our Stage0 launcher code a little bit to bypass Defender. Luckily, Empire comes with some obfuscation functionality built in (but only for Stage0).

We can simple set Obfuscation True in the multi/launcher menu to turn obfuscation on.

Generating an agent with obfuscation turned on
Generating an agent with obfuscation turned on

When we run the above one-liner, Defender lets it right through.

Running our obfuscated one-liner with Defender turned on
Running our obfuscated one-liner with Defender turned on

We saw it only takes a little bit of time and tinkering to get Empire past Defender.

Typical experience with Antivirus software
Typical user experience with Antivirus software

This article was updated on July 11, 2022