GreenBlood Ransomware Analysis
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.
Note (Sample Metadata)
- SHA256:
12bba7161d07efcb1b14d30054901ac9ffe5202972437b0c47c88d71e45c7176 - Language: Go, 64-bit PE, ~4MB
- Encryption: AES-256-CTR,
.tgbgextension - 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
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
| Property | Value |
|---|---|
| Malware Family | Green Blood / DAF |
| Serial Number | DAF-SN-1258745612548523 |
| Source Path |
/root/victims/ransom/daf/enc.go |
| Key Size | 256-bit |
| IV Size | 128-bit |
| File Extension | .tgbg |
| Ransom Note | !!!READ_ME_TO_RECOVER_FILES!!!.txt |
| C2 | Tor hidden service on port 8000 |
Execution Flow
Phase 1: Initialization
What happens
- Mutex check — creates
Global\GREENBLOOD_ENCRYPTOR_MUTEX_2A3B4C5D{} to prevent multiple instances from running concurrently. - Key generation — invokes
crypto/rand.Read(){} to generate 32 cryptographically secure random bytes. - Machine fingerprint — collects system attributes: hostname, drive serial numbers, MAC address, Windows Product ID, and motherboard BIOS UUID.
- Recovery token — derived from the machine fingerprint for victim tracking and payment attribution.
- Worker pool — initializes 50 worker goroutines backed by a 10,000-entry buffered file queue channel.
// Simplified key generationkey := make([]byte, 32)crypto/rand.Read(key) // OS CSPRNG — unbreakable
// Simplified fingerprintfingerprint := hostname + "||" + driveSerials + "||" + macAddress + "||" + windowsProductId + "||" + biosUUIDmachineID := hex(first32Bytes(fingerprint))
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.
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:
- Hard block: Ignores any file containing
"read_me"in the name. - Hard block: Skips extensionless files entirely.
- Blacklist check: If the file extension matches
extensionsToSkip, it is skipped. - Whitelist check: If the file extension matches
extensionsToEncrypt, it is queued for encryption. - Fallthrough condition: If the extension is in neither list but the file size exceeds 100 bytes, it gets encrypted anyway.
- 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.
// Simplified encryption routineiv := make([]byte, 16)crypto/rand.Read(iv) // Unique 16-byte IV per file
block := aes.NewCipher(encryptionKey) // AES-256 block cipherstream := cipher.NewCTR(block, iv) // CTR stream modewriter := cipher.StreamWriter{dstFile, stream}
buffer := make([]byte, 1024 * 1024) // 1 MB buffer chunksfor { n, err := sourceFile.Read(buffer) if n > 0 { writer.Write(buffer[:n]) } if err != nil { break }}
// Atomic rename chain: original -> .bak -> .tgbg -> delete .bakos.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

Warning (CTR, not CBC)
Despite AES-CBC strings appearing in the compiled binary
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.
workerPool := make(chan struct{}, 50) // 50 concurrent encryptorsfileQueue := make(chan string, 10000) // 10,000 queued file pathsstopChan := make(chan struct{}) // shutdown broadcastEach file encryption operation is granted up to 4 retry attempts with exponential backoff
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.
| # | Command | Destructive Impact |
|---|---|---|
| 1 | vssadmin delete shadows /all /quiet{} |
Deletes Volume Shadow Copies |
| 2 | wbadmin delete catalog -quiet{} |
Wipes Windows Backup Catalog and restore history |
| 3 | bcdedit /set recoveryenabled no{} |
Disables the Windows Recovery Environment |
| 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:

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.

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
Raw String : HOSTNAME||DRIVE_SERIALS||MAC_ADDR||PRODUCT_ID||BIOS_UUIDSlicing : first32Bytes(RawString)Machine ID : UPPERCASE_HEX(32_BYTES)Key Data Structures
The core internal architecture in Ghidra reveals clean object modeling:
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};
How Decryption Works
The attacker exfiltrates the generated AES-256 key to the C2 server during Phase 2 main.main.func1)
Because CTR mode acts as a symmetric stream cipher, the decryption transformation is mathematically identical to encryption:
.tgbg File Structure: [ 16-byte IV ] [ Ciphertext Data ... ] │AES-256(Key) ──▶ CTR(IV) ──▶ XOR ──────▶ Recovered PlaintextIndicators of Compromise (IoCs)
Filesystem Artifacts
*.tgbg— Encrypted file extensions!!!READ_ME_TO_RECOVER_FILES!!!.txt— Desktop ransom noteREAD_ME_TO_RECOVER_FILES.txt— Directory ransom notescleanup_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
.bakintermediary 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 |