Introduction
A lot of the time when working with malware or when investigating an incident, you may encounter PowerShell executing obfuscated commands which may look like gibberish. These commands are usually obfuscated to make it harder for the analyst to understand, as well as making it harder for detection solutions to detect them.
Knowing how to de-obfuscate PowerShell is a very handy and time-saving skill. In this post, we’ll take a look at PowerShell scripts from the Lemon_Duck cryptominer as an example.
When de-obfuscating PowerShell scripts, keep two key rules in mind:
- Always look for the cmdlet
IEX
, this is an alias for Invoke-Expression
which basically evaluates and executes whatever you give it as input.
IEX
is usually either at the beginning in the form of IEX (expression)
or at the end in the form of expression | IEX
.
Example: ("h.stn[me").Replace('.', 'o').Replace('[', 'a')
will produce the string “hostname”, which will be printed.
Piping the output into IEX
as follows: ("h.stn[me").Replace('.', 'o').Replace('[', 'a') | IEX
results in it being executed.

You generally want to avoid executing the IEX cmdlet in order to de-obfuscate the script without running the malware in your environment.
It is worth noting that a quite easy and primitive technique that may work sometimes is using Write-Output
to get the deobfuscated script. This works in some cases, but we won’t get into too much details on when and why it works.
Lemon_Duck CryptoMiner Handson
In one of Cyber Castle’s IR engagements, the DFIR team encountered the Lemon_Duck cryptominer which had multiple multi-stage obfuscated PowerShell scripts.
Scheduled Task: Downloader
The first script was found in a scheduled task.
This PowerShell script didn’t have many obfuscation techniques. If anything, it was quite readable from the get-go with some simple format fixing such as indentation.
It basically sends a request to http://t.pp6r1.com/a.jsp
which contains the hostname, username, UUID, and a random number as parameters.
By changing the call to a()
to Write-Host
We can get the URL instead of getting it to execute.

Sending that request, gets us a file: a.jsp. The file initially contained obfuscated PowerShell code. The whole code is included here. Password: infected.
a.jsp: Stager

The first few bytes are the signature of the script itself. Later on, we can see other scripts verifying the first 173 bytes of the script as the signature.

This seems like a script which has hex content inside of it, which is decompressed as ASCII text.
We can see the IEX
cmdlet clearly at the start, we can just separate it from the rest of the script to get the decompressed version of the included hex data.
We got a pretty long script, which still seems to be obfuscated, let’s pipe the output to a file and then take a closer look at it. I’ll name the file a_deobf1.ps1 to keep track of the De-Obfuscation stages. We can do so by adding | Out-File a_deobf1.ps1
to the end of the line, like so:

Now on checking a_deobf1.ps1, as usual, the first thing we look for is the IEX cmdlet. One thing that caught my attention was this part in the last line of the script | &( $shELLID[1]+$sHelLId[13]+'X')
. It was a pipe (|
) with three character after it, the final one being an X.
$SHELLID
is a standard PowerShell variable which contains the string “Microsoft.PowerShell”. Indices 1 and 13 are i, and e respectively. This concludes that that is where the IEX
cmdlet is.
As we did previously, we can now replace the IEX
with Out-File a_deobf2.ps1
to get the new De-Obfuscated script.

Again, and like every time, the first thing we look for is the IEX
. There is an explicit IEX
string in the script, but it is inside a string (line 418) so it won’t execute. So, let’s take a look at the start of the script. We find this part ((gEt-VARiABLe '*mdr*').NAme[3,11,2]-joIn'')
which has the rest of the script in it
If we execute that selection alone, we get IEX
. This is because (gEt-VARiABLe '*mdr*').NAme
returns the string MaximumDriveCount. and indices 3, 11, and 2 map to i, e, and x respectively.
We can separate the IEX
from the rest of the script and use Out-File
like before to get to the next stage of de-obfuscation.

And again, on opening the third script we look for the IEX
and find it at the end of the script at: |.( $env:ComSPeC[4,26,25]-joIn'')
$env:COMSPEC
has the value "C:\Windows\system32\cmd.exe"
which has iex at the specified indices. The malware author really is getting repetitive with these methods!
We’ll do our usual magic with the IEX
separation and Out-File
to get the final script.

Now we have a pretty clear PowerShell script that we can read and analyse.
The first things we notice from a quick skim are:
-
The PowerShell script is communicating with multiple domains.
- It copies the powershell.exe executable to a differently-named file in the same original powershell path.
-
It tries to disable Windows Defender.
- It also adds an exclusion to the C:\ directory, the path to the PowerShell executable, and the other copy it makes of powershell.exe
-
Detects the type of GPU running on the system in order to start mining
-
Sets a user agent of “Lemon-Duck” before sending a request to a specific domain.
On further analysis, we found out that the server requires the following conditions in order to reply with the payload:

WARNING: Be extra careful if you’re going to try the following, we’re dealing with live malware which communicates with a server that is up and running as of (9/5/2021). Do not execute anything you’re unsure of, and do not send any real or personal data to the server.
The PowerShell script has a function SIEX
which sends the web request to get the next script. If the parameters are not set properly, the server doesn’t respond with the next script.

However, if the parameters are correct, the server will respond with the next script and execute it using IEX
as shown: IEX (-join[char[]]$raw_bytes)
. We can add the correct parameters to the script to override the automatically collected ones and replace the IEX
near the end of the script with a Write-Host
to get the next script that the server sends back for analysis.
$params = "&DESKTOP-QTEKH69&09874D56-2136-3D8C-C24F-56EA5E7D6B61&00:0C:29:7D:6B:61&10 Pro_10.0.17763&1&User_Admin&TESTNAME&&Radeon 350&6&0&&&&&&&2475.187&157182335&0.6"

The Final Script
Simply by taking a look at this script, we can see the IEX
cmdlet at the start, so we can separate it and write the output to a file as before.

By looking again, we can see that this script uses the exact same IEX
hiding technique as in the initial stager script which used $SHELLID
.
We can separate it and write the output to a file for further analysis.

We can see that by no surprise, the script uses the same IEX
hiding technique as before which uses $env:COMSPEC
.
We can de-obfuscate it using the same method.

This script has an explicit iex
string at the start of it, so, again, we can just separate it and just de-obfuscate the rest of the code.

Now we have the final script, which shows some of the IOCs of the Lemon_Duck cryptominer. Many of which are used for persistence.
We hope that you found this blog post useful, thanks for reading. Good luck catching the IEX!
Post comments (0)