Nikhil "Kaido" Hegde

M&M: Malware and Musings

View on GitHub

Source

Analysis

Obfuscation

A quick skim through the script indicated that there was lots of junk code, which bloated the file size. I’ll get through each of them individually. AST-based deobfuscation was the primary weapon of choice.

Loop Constructs - Junk Code

...
for ($i = 0; $i -lt 8; $i++) { $nextvar = $i }
...
$counter = 0; while ($counter -lt 3) { $counter++ }
...
for ($j = 0; $j -lt 4; $j++) { $final += $j }
...
for ($i = 0; $i -lt 1; $i++) { $nextvar = $i }
...
$counter = 0; while ($counter -lt 7) { $counter++ }
...

Try-Catch Constructs - Junk Code

...
try { $y = 5 } catch { $error }
...
try { $y = 2 } catch { $error }
...

Backticks - Junk Code

...
$randIndex = Get-Ran`dom -Minimum 0 -Maximum 13
...
while ($binpath) { Start-`Sleep -Seconds 1 }
...

Variable Renaming Trick + Split Base64 Strings

...
${Write-Output -ArgumentList -DestinationPath .\Archive && ($opr)} = "CgoKZnVuY3Rpb";${Clear-RecycleBin -InputFormat -Path %APPDATA% && ($afrre)} = "24gR2V0LVRl";
...
${Get-Date -ErrorAction Stop -Name OneDrive && ($sdicna)} = "ID0gTmV3LU";${Get-Location -ArgumentList -InformationLevel Minimal && ($slt)} = "9iamV";${Get-NetAdapter -ErrorAction SilentlyContinue -Class Win32_Process && ($akasgo)} = "jdCBTeXN0ZW0uQ";
...

Split Strings

...
${Get-Hotfix -InformationAction Continue -Seconds 4 && ($genifg)} = [Convert]::FromBase64String(${Start-Sleep -ArgumentList -DestinationPath .\Data && ($lagfi)}); [Text.Encoding]::("U" +           "TF" +           "8").("G" +   "e" +   "t" +   "S" +   "trin" +   "g")(${Get-Hotfix -InformationAction Continue -Seconds 4 && ($genifg)}) | iex; ${Import-Module -Force -LogName 
...

Split Array Elements

During the process of de-obfuscation, the below form of obfuscation was also seen.

${Get-Date -InputFormat -ComputerName $server_ip_08 && ($pprpmn)} += @('CgoKZnVuY3Rpb', '24gR2V0LVRl', 'bGVncmFtV', 'GRhdG', 'FGb2', 'xkZXJzIHsKICAg')
...
${Get-Date -InputFormat -ComputerName $server_ip_08 && ($pprpmn)} += @('ICRsaXN0', 'ID0gTmV3LU', '9iamV', 'jdCBTeXN0ZW0uQ', '29sbGVjdG')
...

Deobfuscation

Deobfuscation of the above elements was performed through Abstract Syntax Tree (AST) analysis. These utilities are available in the blog repo.

> .\PsRemove-DeadCode.ps1

cmdlet PsRemove-DeadCode.ps1 at command pipeline position 1
Supply values for the following parameters:
InputFile: C:\Users\Ashura\Desktop\1fcd76aba39621c66c834538fc37a8d9249b4004e62343d594334ff40d9269ab\1fcd76aba39621c66c834538fc37a8d9249b4004e62343d594334ff40d9269ab.ps1
OutputFile: C:\Users\Ashura\Desktop\1fcd76aba39621c66c834538fc37a8d9249b4004e62343d594334ff40d9269ab\1fcd76aba39621c66c834538fc37a8d9249b4004e62343d594334ff40d9269ab_pass1.ps1
{"changed":3917,"output_path":"C:\\Users\\Ashura\\Desktop\\1fcd76aba39621c66c834538fc37a8d9249b4004e62343d594334ff40d9269ab\\1fcd76aba39621c66c834538fc37a8d9249b4004e62343d594334ff40d9269ab_pass1.ps1","output_bytes":263671,"by_reason":"317x unreachable loop, 550x dead if (unreachable/empty), 435x non-functional loop, 286x dead try block, 2329x dead store","input_bytes":361366}

> .\PsInline-Constants.ps1

cmdlet PsInline-Constants.ps1 at command pipeline position 1
Supply values for the following parameters:
InputFile: C:\Users\Ashura\Desktop\1fcd76aba39621c66c834538fc37a8d9249b4004e62343d594334ff40d9269ab\1fcd76aba39621c66c834538fc37a8d9249b4004e62343d594334ff40d9269ab_pass1.ps1
OutputFile: C:\Users\Ashura\Desktop\1fcd76aba39621c66c834538fc37a8d9249b4004e62343d594334ff40d9269ab\1fcd76aba39621c66c834538fc37a8d9249b4004e62343d594334ff40d9269ab_pass2.ps1
MaxUses: 0
{"input_bytes":263671,"output_path":"C:\\Users\\Ashura\\Desktop\\1fcd76aba39621c66c834538fc37a8d9249b4004e62343d594334ff40d9269ab\\1fcd76aba39621c66c834538fc37a8d9249b4004e62343d594334ff40d9269ab_pass2.ps1","output_bytes":71256,"changed":1426,"occurrences_replaced":1426,"assignments_removed":1426,"skipped":3}

> .\PsFold-ArrayJoins.ps1

cmdlet PsFold-ArrayJoins.ps1 at command pipeline position 1
Supply values for the following parameters:
InputFile: C:\Users\Ashura\Desktop\1fcd76aba39621c66c834538fc37a8d9249b4004e62343d594334ff40d9269ab\1fcd76aba39621c66c834538fc37a8d9249b4004e62343d594334ff40d9269ab_pass2.ps1
OutputFile: C:\Users\Ashura\Desktop\1fcd76aba39621c66c834538fc37a8d9249b4004e62343d594334ff40d9269ab\1fcd76aba39621c66c834538fc37a8d9249b4004e62343d594334ff40d9269ab_pass3.ps1
{"changed":1,"output_path":"C:\\Users\\Ashura\\Desktop\\1fcd76aba39621c66c834538fc37a8d9249b4004e62343d594334ff40d9269ab\\1fcd76aba39621c66c834538fc37a8d9249b4004e62343d594334ff40d9269ab_pass3.ps1","output_bytes":45253,"input_bytes":71256}

Payload

After deobfuscation, a base64-encoded string remained, which was then base64-decoded and executed through iex Powershell alias. Below is a Claude-generated analysis (verified) of the payload.

Flowchart

flowchart TD
    START["Script Entry\nBackup-TelegramData called"]
    DISCOVER["Discover tdata Folders\nAPPDATA path / Get-Process / WMI / hardcoded paths"]
    FOUND{tdata folders found?}
    COMPRESS["Compress tdata to ZIP\nin-memory via ZipArchive\nFileShare.ReadWrite bypasses live Telegram locks"]
    GEOLOCATE["Geolocate Victim\nGET ip-api.com/json\nbuild filename: CC_IP_telegram_data_timestamp.zip"]
    EXFIL["Exfiltrate to FTP C2\nftp://185.92.181.207\nftpuser / xOy5pE0GJEEhPrLA\npassive binary mode"]
    RETRY{Upload succeeded?\nattempt 1-3}
    EXIT_EARLY["Exit — nothing to steal"]
    DONE["Script End"]

    START --> DISCOVER
    DISCOVER --> FOUND
    FOUND -->|No| EXIT_EARLY
    FOUND -->|Yes| COMPRESS
    COMPRESS --> GEOLOCATE
    GEOLOCATE --> EXFIL
    EXFIL --> RETRY
    RETRY -->|Yes| DONE
    RETRY -->|No — retries exhausted| DONE

    classDef entry fill:#c0392b,color:#fff,stroke:#922b21,stroke-width:3px
    classDef action fill:#2980b9,color:#fff,stroke:#1a5276,stroke-width:2px
    classDef decision fill:#f39c12,color:#000,stroke:#d68910,stroke-width:2px
    classDef malicious fill:#8e44ad,color:#fff,stroke:#6c3483,stroke-width:2px
    classDef terminal fill:#7f8c8d,color:#fff,stroke:#626d6d,stroke-width:2px

    class START entry
    class DISCOVER,COMPRESS,GEOLOCATE action
    class EXFIL malicious
    class FOUND,RETRY decision
    class EXIT_EARLY,DONE terminal

Summary

Classification: Data Stealer — Telegram Session Exfiltrator (PowerShell)

payload.ps1 is a purpose-built credential/session theft tool that extracts Telegram Desktop’s local session store (tdata) and silently exfiltrates it to an attacker-controlled FTP server. The script requires no user interaction after execution, leaves no staging artifact on disk under normal conditions, and operates without requiring elevated privileges.

Why tdata is catastrophic to lose: The folder contains MTProto session keys that reconstitute an authenticated Telegram session on any device. An attacker in possession of this folder achieves full account takeover — reading all messages, impersonating the victim, and accessing contact graphs — without ever knowing the account password or bypassing 2FA.

Execution Chain

# Phase Description
1 Discovery Four parallel strategies locate every tdata path on the host: the standard %APPDATA% install path; live process inspection via Get-Process telegram; WMI Win32_Process WHERE name LIKE '%telegram%'; and four hardcoded common install locations. All results are case-insensitively deduplicated.
2 Collection New-ZipBytes compresses discovered folders entirely in memory using System.IO.Compression.ZipArchive. Root-level tdata files are taken wholesale; subdirectories are filtered to the 16-alphanumeric-char naming pattern ([A-Za-z0-9]{16}) used by Telegram’s internal session key folders. Files are opened with FileShare.ReadWrite to bypass locks held by a concurrently running Telegram process. No temporary files touch disk on the primary path.
3 Victim Enrichment An HTTP GET to http://ip-api.com/json/ (with a spoofed Mozilla/5.0 User-Agent) geolocates the victim. Country code and public IP are embedded in the exfiltration filename, enabling the attacker to sort victims by geography.
4 Exfiltration The in-memory ZIP is uploaded via FTP (passive mode, binary transfer) to hardcoded C2 185.92.181[.]207. Filename format: {CC}_{IP}_telegram_data_{dd_MM_yyyy_HH_mm}.zip. Three retry attempts with linear back-off; 30-second connect timeout; 5-minute read/write timeout.
5 Exit Terminates cleanly on success. Any unhandled exception surfaces through a top-level catch that calls exit 1.

Evasion & Resilience Techniques

Technique Implementation Effect
No disk staging ZIP built in MemoryStream, never written to disk (primary path) Defeats file-based forensic recovery and AV on-write scanning
File lock bypass FileShare.ReadWrite when opening tdata files Steals data while Telegram is actively running
Silent exception handling Pervasive try { } catch { } with no logging No console output, no Event Log entries, no error indicators
UA spoofing Mozilla/5.0 header on geolocation HTTP request Blends network traffic with normal browser activity
Dependency-free parsing Regex extraction of HTTP JSON instead of ConvertFrom-Json Avoids module dependencies; harder to fingerprint by static import analysis
COM fallback Shell.Application CopyHere if .NET Compression is unavailable Ensures ZIP creation on stripped or locked-down systems
Multi-method discovery APPDATA path + Get-Process + WMI + hardcoded paths Succeeds across portable installs, system installs, and non-default-path Telegram instances
Non-fatal geolocation Falls back to UnknownCountry/UnknownIP on API failure Exfiltration proceeds regardless of outbound HTTP restrictions

Indicators of Compromise

Type Value
IPv4 — C2 FTP server 185.92.181[.]207
Protocol / Port FTP / TCP 21
HTTP endpoint http://ip-api.com/json/
Spoofed User-Agent Mozilla/5.0
Exfil filename pattern {CC}_{IP}_telegram_data_{dd_MM_yyyy_HH_mm}.zip
Field Value
FTP Username ftpuser
FTP Password xOy5pE0GJEEhPrLA
Path Notes
%APPDATA%\Telegram Desktop\tdata Primary standard install
%LOCALAPPDATA%\Telegram\Telegram Desktop\tdata Alternate install path
%ProgramFiles%\Telegram Desktop\tdata 64-bit system-wide install
%ProgramFiles(x86)%\Telegram Desktop\tdata 32-bit system-wide install
C:\Users\{USERNAME}\AppData\Local\Telegram Desktop\tdata Hardcoded explicit user path
Subdirectory regex [A-Za-z0-9]{16} Telegram internal session key folder naming pattern