GreenBlood Ransomware Analysis

rakin rakin

A comprehensive reverse engineering breakdown of GreenBlood (DAF) ransomware — analyzing its Go-based architecture, AES-256-CTR encryption engine, system sabotage routines, and leaked artifacts.

GreenBlood Ransomware Analysis
Note (Sample Metadata)
  • SHA256: 12bba7161d07efcb1b14d30054901ac9ffe5202972437b0c47c88d71e45c7176
  • Language: Go, 64-bit PE, ~4MB
  • Encryption: AES-256-CTR, .tgbg extension
  • Contact: thegreenblood@proton.me
Note (Decompiled Codes)

For decompiled source code retrieved from Ghidra, please refer to the GitHub repository → greenblood-ransomware-analysis

At a Glance

I was handed a suspicious Windows executable: no metadata, no digital signature, just a 64-bit PE file. Before touching a debugger, I ran the simplest tool in the book: strings{}.

What jumped out immediately was “Go build ID” embedded right in the binary. This payload was written in Go, not the usual C/C++ malware binary. Even more interesting, it leaked its own source path: /root/victims/ransom/daf/enc.go (unstripped debug symbols). That is remarkably rare. Most malware authors strip symbols prior to deployment, but this author did not.

The strings also spilled its entire playbook: AES encryption routines, a .tgbg extension, destructive commands like vssadmin delete shadows{}, a Tor onion address, and two contact email addresses: thegreenblood@proton.me and thegreenblood@onionmail.org. We were looking at a full-fledged ransomware strain — and not a subtle one.

So I fired up Ghidra, installed the GoAnalyzerExtension (essential for Go binaries, as their calling conventions and runtime string layouts differ substantially from standard PE files), and started decompiling function by function.

Property Value
Malware Family Green Blood / DAF
Serial Number DAF-SN-1258745612548523
Source Path (leaked) /root/victims/ransom/daf/enc.go
Key Size 256-bit (32 bytes), fresh per victim
IV Size 128-bit (16 bytes), fresh per file
File Extension .tgbg
Ransom Note !!!READ_ME_TO_RECOVER_FILES!!!.txt
C2 Tor hidden service on port 8000

Execution Flow

flowchart TD A[Malware Executes] --> B{Already Running?} B -->|Yes| X[Exit] B -->|No| C[Phase 1: Initialize] C --> C1[Generate AES-256 Key] C --> C2[Create Machine Fingerprint] C --> C3[Spawn 50 Workers] C1 & C2 & C3 --> D[Phase 2: Encrypt] D --> D1[Enumerate Drives] D1 --> D2[Walk Each Drive] D2 --> D3[Queue 10,000 Files] D3 --> D4[50 Workers Encrypt in Parallel] D4 --> D5[Wait for Completion] D5 --> E{Is Admin?} E -->|Yes| E1[Phase 3: Sabotage] E -->|No| F[Phase 4: Finalize] E1 --> E2[Delete Shadow Copies] E1 --> E3[Disable WinRE & Defender] E1 --> E4[Turn Off Firewall] E2 & E3 & E4 --> F F --> F1[Drop Ransom Note] F --> F2[Print Summary Stats] F --> F3[Wait 10 Seconds] F3 --> F4[Self-Delete via Batch File]

Phase 1: Initialization

What happens

  1. Mutex check — creates Global\GREENBLOOD_ENCRYPTOR_MUTEX_2A3B4C5D{} to prevent multiple instances from running concurrently.
  2. Key generation — invokes crypto/rand.Read(){} to generate 32 cryptographically secure random bytes.
  3. Machine fingerprint — collects system attributes: hostname, drive serial numbers, MAC address, Windows Product ID, and motherboard BIOS UUID.
  4. Recovery token — derived from the machine fingerprint for victim tracking and payment attribution.
  5. Worker pool — initializes 50 worker goroutines backed by a 10,000-entry buffered file queue channel.
initialization.go
// Simplified key generation
key := make([]byte, 32)
crypto/rand.Read(key) // OS CSPRNG — unbreakable
// Simplified fingerprint
fingerprint := hostname + "||" +
driveSerials + "||" +
macAddress + "||" +
windowsProductId + "||" +
biosUUID
machineID := hex(first32Bytes(fingerprint))

Decompiled NewKeyManager function in Ghidra


Phase 2: Encryption

Drive Discovery

The malware calls GetLogicalDrives(){} to obtain a bitmask of available drive letters, followed by GetDriveTypeW(){} for each letter to filter out remote and non-local drives. Only fixed drives and removable drives are targeted — network shares are excluded entirely. On a standard workstation, this targets C:\ and any attached auxiliary drives like D:\.

Directory Walking

Each drive is walked recursively inside its own goroutine. As directories are discovered, the malware validates them against a hardcoded exclusion list containing 14 critical system paths. The motivation is purely practical: encrypting C:\Windows would crash the operating system before file encryption could complete, and the malware requires the system to stay operational to finalize its routine.

system_exclusions.go
systemExclusions := map[string]bool{
"C:\\Windows": true,
"C:\\Program Files": true,
"C:\\Program Files (x86)": true,
"C:\\ProgramData": true,
"C:\\Boot": true,
"C:\\Recovery": true,
"C:\\System Volume Information": true,
// ... 14 paths total
}

File Targeting

The file selection logic applies a multi-tiered filtering mechanism:

  1. Hard block: Ignores any file containing "read_me" in the name.
  2. Hard block: Skips extensionless files entirely.
  3. Blacklist check: If the file extension matches extensionsToSkip, it is skipped.
  4. Whitelist check: If the file extension matches extensionsToEncrypt, it is queued for encryption.
  5. Fallthrough condition: If the extension is in neither list but the file size exceeds 100 bytes, it gets encrypted anyway.
  6. Catch-all: Small, unrecognized files below 100 bytes are ignored.

Encryption Algorithm

The encryption scheme uses AES-256 in CTR mode. A unique 32-byte key is generated per victim via crypto/rand.Read(){}, and every individual file receives its own 16-byte random IV from the CSPRNG.

The 16-byte IV is prepended directly to the output .tgbg file, followed by the plaintext streamed through crypto/cipher.NewCTR{} using a 1MB buffer. Because CTR mode is a stream cipher construct, encryption and decryption operations are symmetrical: XORing the ciphertext with the generated keystream yields the plaintext.

The file renaming sequence is designed to be crash-safe: original → original.bak → original.tmp → original.tgbg → delete original.bak.

If a sudden power loss occurs mid-encryption, the original data remains recoverable from the uncorrupted .bak file.

encryption.go
// Simplified encryption routine
iv := make([]byte, 16)
crypto/rand.Read(iv) // Unique 16-byte IV per file
block := aes.NewCipher(encryptionKey) // AES-256 block cipher
stream := cipher.NewCTR(block, iv) // CTR stream mode
writer := cipher.StreamWriter{dstFile, stream}
buffer := make([]byte, 1024 * 1024) // 1 MB buffer chunks
for {
n, err := sourceFile.Read(buffer)
if n > 0 {
writer.Write(buffer[:n])
}
if err != nil {
break
}
}
// Atomic rename chain: original -> .bak -> .tgbg -> delete .bak
os.Rename(originalPath, originalPath + ".bak")
os.Rename(tempPath, originalPath + ".tgbg")
os.Remove(originalPath + ".bak")

The malware also imposes an upper file size limit, skipping files that exceed 0xC80000000 bytes (53,687,091,200 bytes ≈ 50 GB):

Large file size limit check decompiled in Ghidra

Warning (CTR, not CBC)

Despite AES-CBC strings appearing in the compiled binary (originating from Go’s internal FIPS 140-3 self-test routines), the actual encryption call path is strictly AES-256-CTR:

encryptFile → crypto/aes.NewCipher → crypto/cipher.NewCTR → StreamWriter.Write

Concurrency Model

The encryption engine leverages Go’s native channel architecture as a producer-consumer pipeline:

  • File Queue Channel (capacity 10,000): Decouples directory traversal goroutines from encryption workers. Walkers push discovered file paths into the queue, and workers consume them.
  • Worker Pool Semaphore (capacity 50): A buffered channel token pool limiting active concurrent encryptions to 50 threads.
  • Stop Channel: Broadcasts a termination signal to gracefully shut down worker goroutines upon task completion.
flowchart TD subgraph "50 Workers" W1[Worker 1] W2[Worker 2] W3[...] W50[Worker 50] end Queue["File Queue<br/>(10,000 buffered)"] --> W1 Queue --> W2 Queue --> W3 Queue --> W50 W1 --> Pool{"Semaphore<br/>(50 slots)"} W2 --> Pool W3 --> Pool W50 --> Pool Pool --> Encrypt[encryptFile] Encrypt --> Release[Release Slot]
concurrency.go
workerPool := make(chan struct{}, 50) // 50 concurrent encryptors
fileQueue := make(chan string, 10000) // 10,000 queued file paths
stopChan := make(chan struct{}) // shutdown broadcast

Each file encryption operation is granted up to 4 retry attempts with exponential backoff (100ms × attempt number) in case of transient file-lock contention.


Phase 3: System Sabotage

Phase 3 executes conditionally if the malware detects administrative elevation. The privilege check builds a well-known SID structure (S-1-5-32-544, representing the BUILTIN\Administrators group) with a TokenIsElevated API fallback for UAC split tokens.

flowchart LR A["isAdmin?"] -->|Yes| B["Execute 7 Sabotage Commands"] A -->|No| C["Skip Sabotage"] B --> B1["vssadmin delete shadows /all /quiet"] B --> B2["wbadmin delete catalog -quiet"] B --> B3["bcdedit /set recoveryenabled no"] B --> B4["bcdedit /set bootstatuspolicy ignoreallfailures"] B --> B5["wmic shadowcopy delete"] B --> B6["reg add ...DisableRealtimeMonitoring /d 1"] B --> B7["netsh advfirewall set allprofiles state off"]
# Command Destructive Impact
1 vssadmin delete shadows /all /quiet{} Deletes Volume Shadow Copies (System Restore Points)
2 wbadmin delete catalog -quiet{} Wipes Windows Backup Catalog and restore history
3 bcdedit /set recoveryenabled no{} Disables the Windows Recovery Environment (WinRE)
4 bcdedit /set bootstatuspolicy ignoreallfailures{} Suppresses automatic recovery menus on boot failure
5 wmic shadowcopy delete{} Secondary deletion call for Volume Shadow Copies via WMI
6 reg add ...DisableRealtimeMonitoring /d 1{} Disables Windows Defender real-time protection
7 netsh advfirewall set allprofiles state off{} Shuts off Windows Defender Firewall on all network profiles

Decompiled command execution block:

Decompiled disableRecovery command execution

Warning (Ordering Bug)

Windows Defender real-time monitoring is disabled at step 6 — after Phase 2 encryption has already completed. If Windows Defender signature or behavioral heuristics were going to detect the rapid encryption activity, it would have intervened in Phase 2. This represents an operational design flaw by the malware author.


Phase 4: Finalization

Ransom Note

A ransom note is dropped onto the victim’s desktop as !!!READ_ME_TO_RECOVER_FILES!!!.txt and duplicated across every traversed directory as READ_ME_TO_RECOVER_FILES.txt.

The ransom note embeds the victim’s unique recovery token, machine identifier, attacker contact emails, and extortion terms: payment within 7 days, with key destruction threatened after 21 days. It systematically lists third-party decryption utilities and recovery approaches, warning that each attempt will permanently corrupt the files — a psychological tactic to force ransom compliance.

Dropped ransom note text

Self-Deletion Routine

Because Windows locks executing binaries from deletion, the malware writes a temporary batch script (cleanup_greenblood.bat{}) to %TEMP%.

Following a 10-second countdown to allow file descriptors and socket buffers to flush, the process spawns the batch script with a hidden console window. The batch script pauses for 5 seconds and enters a loop attempting to delete the main executable. Once the PE file is deleted, the script deletes itself, leaving minimal forensic traces on disk.


Machine Fingerprint Construction

The machine fingerprint uniquely identifies the compromised endpoint without transmitting plain identifiers. It aggregates five distinct host parameters:

  • Hostname: The workstation’s NetBIOS / DNS name.
  • Drive Serial Numbers: 32-bit volume serial numbers for all connected local partitions.
  • MAC Address: Physical address of the primary active network adapter.
  • Windows Product ID: Extracted from the Windows registry hive.
  • BIOS UUID: Extracted via wmic csproduct get uuid{}, hardcoded in motherboard firmware.

All five values are concatenated with a double-pipe separator (||), and a 32-byte window is extracted from the start (cycling if the string is under 32 bytes), formatted as uppercase hexadecimal:

Fingerprint Formulation
Raw String : HOSTNAME||DRIVE_SERIALS||MAC_ADDR||PRODUCT_ID||BIOS_UUID
Slicing : first32Bytes(RawString)
Machine ID : UPPERCASE_HEX(32_BYTES)

Key Data Structures

The core internal architecture in Ghidra reveals clean object modeling:

datastructures.h
struct EncryptionEngine {
KeyManager *keyManager; // Holds master AES key & victim tokens
chan *workerPool; // Semaphore channel (50 slots)
WaitGroup wg; // Synchronizes directory walkers
WaitGroup fileWg; // Synchronizes worker goroutines
struct {
int64 encryptedFiles; // Successfully encrypted count
int64 encryptedBytes; // Total bytes encrypted
int64 failedFiles; // Failed encryption attempts
int64 skippedFiles; // Excluded / unhandled files
} stats;
RWMutex statsMutex; // Thread-safe mutex for metrics
chan *fileQueue; // 10,000 buffered file paths
chan *stopChan; // Shutdown broadcast channel
};
struct KeyManager {
[]byte encryptionKey; // 32-byte master key (AES-256)
string recoveryToken; // Victim-facing recovery identifier
string machineID; // Derived hardware fingerprint
};

Decompiled EncryptionEngine and KeyManager structure


How Decryption Works

The attacker exfiltrates the generated AES-256 key to the C2 server during Phase 2 (via an asynchronous goroutine at main.main.func1).

Because CTR mode acts as a symmetric stream cipher, the decryption transformation is mathematically identical to encryption:

CTR Stream Decryption Workflow
.tgbg File Structure: [ 16-byte IV ] [ Ciphertext Data ... ]
│
AES-256(Key) ──▶ CTR(IV) ──▶ XOR ──────▶ Recovered Plaintext

Indicators of Compromise (IoCs)

Filesystem Artifacts

  • *.tgbg — Encrypted file extensions
  • !!!READ_ME_TO_RECOVER_FILES!!!.txt — Desktop ransom note
  • READ_ME_TO_RECOVER_FILES.txt — Directory ransom notes
  • cleanup_greenblood.bat — Self-deletion batch script

Mutex Objects

  • Global\GREENBLOOD_ENCRYPTOR_MUTEX_2A3B4C5D{}

Network Infrastructure

  • scbrksw5fgjtujc2ah42roo6bij2unr2tggfcynpbql5a7yp3s22taid.onion:8000

Behavioral Signatures

  • Bulk sequential file renaming to .tgbg
  • vssadmin.exe delete shadows /all /quiet{}
  • bcdedit.exe /set {default} recoveryenabled no{}
  • netsh.exe advfirewall set allprofiles state off{}

Key Takeaways & Assessment

Strengths

  • Go Implementation: Non-standard runtime and memory layout complicates naive reverse engineering without specialized tooling like GoAnalyzerExtension.
  • Cryptographic Rigor: Proper AES-256-CTR implementation with fresh per-file random IVs generated via the OS CSPRNG.
  • Atomic File Operations: Crash-safe .bak intermediary staging prevents corrupt half-written files if interrupted.
  • Robust Concurrency: Producer-consumer architecture with buffered channels avoids resource starvation and locks.

Deficiencies

  • Unstripped Symbols: Source file paths (/root/victims/ransom/daf/enc.go) and internal function signatures left completely intact.
  • Defensive Ordering Flaw: Antivirus and firewall tampering occurs after encryption completes.
  • No Long-term Persistence: Self-deletes immediately, giving no secondary re-entry mechanism.
  • Crude Fingerprint Reduction: The fingerprint generation truncates raw bytes rather than hashing via SHA-256.
  • Static Mutex Identifier: Distinctive mutex string creates a trivial indicator for EDR detection.

Key Function Addresses (Ghidra)

Virtual Address Function Symbol Purpose
0x4db8c0{} main.main{} Main entry point driving all 4 phases
0x4d85c0{} main.NewKeyManager{} Generates master AES-256 key
0x4d87e0{} main.getMachineFingerprint{} Computes machine hardware ID
0x4d93c0{} main.NewEncryptionEngine{} Allocates 50-worker pool and channels
0x4d9740{} main.(*Engine).encryptFile{} Executes AES-256-CTR streaming encryption
0x4db500{} main.disableRecovery{} Dispatches 7 system sabotage commands
0x4db720{} main.isAdmin{} Performs SID-based administrator check
0x4dc4a0{} main.isAlreadyRunning{} Mutex check for single-instance enforcement
0x4dc780{} main.removeExecutable{} Dispatches self-delete batch script
0x4da9a0{} main.(*Engine).placeRansomNote{} Drops ransom notes to traversed paths