Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Analyzes encryption algorithms, key management, and file encryption routines used by ransomware families to assess decryption feasibility, identify implementation weaknesses, and support recovery efforts. Covers AES, RSA, ChaCha20, and hybrid encryption schemes. Activates for requests involving ransomware cryptanalysis, encryption analysis, key recovery assessment, or ransomware decryption feasibility.
.claude/skills/analyzing-ransomware-encryption-mechanisms/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-07 | ✗→✓ | ▲ Improved | — | — |
| case-22 | ✗→✓ | ▲ Improved | — | — |
| case-04 | ✗→✓ | ▲ Improved | — | — |
| case-10 | ✗→✓ | ▲ Improved | — | — |
| case-02 | ✗→✓ | ▲ Improved | — | — |
Do not use for production data recovery operations without first verifying the decryption method on test copies of encrypted files.
pycryptodome library for testing encryption/decryption routinesDetermine which cryptographic algorithm the ransomware uses:
python# Check for Windows Crypto API usage in imports import pefile pe = pefile.PE("ransomware.exe") crypto_apis = { "CryptAcquireContextA": "Windows CryptoAPI", "CryptAcquireContextW": "Windows CryptoAPI", "CryptGenKey": "Windows CryptoAPI key generation", "CryptEncrypt": "Windows CryptoAPI encryption", "CryptImportKey": "Windows CryptoAPI key import", "BCryptOpenAlgorithmProvider": "Windows CNG (modern crypto)", "BCryptEncrypt": "Windows CNG encryption", "BCryptGenerateKeyPair": "Windows CNG asymmetric key gen", } print("Crypto API Imports:") for entry in pe.DIRECTORY_ENTRY_IMPORT: for imp in entry.imports: if imp.name and imp.name.decode() in crypto_apis: print(f" {entry.dll.decode()} -> {imp.name.decode()}: {crypto_apis[imp.name.decode()]}")
Common Ransomware Encryption Schemes:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
AES-256-CBC + RSA-2048: Most common hybrid scheme (LockBit, REvil, Conti)
AES-256-CTR + RSA-4096: Stream cipher mode variant (BlackCat/ALPHV)
ChaCha20 + RSA-4096: Modern stream cipher (Hive, Royal)
Salsa20 + ECDH: Curve25519 key exchange (Babuk)
AES-128-ECB: Weak mode - potential decryption via known-plaintext
XOR-only: Trivial encryption - always recoverable
Custom algorithm: Often contains implementation flawsReverse engineer how encryption keys are generated and stored:
Key Management Patterns in Ransomware:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. STRONG (no recovery possible without key):
- Per-file AES key generated with CryptGenRandom
- AES key encrypted with embedded RSA public key
- Encrypted key appended to each file or stored separately
- RSA private key held only by attacker's C2 server
2. WEAK (potential recovery):
- AES key derived from predictable seed (timestamp, PID)
- Same AES key used for all files (single key compromise = full recovery)
- Key transmitted to C2 before encryption starts (PCAP may contain key)
- XOR with short repeating key (brute-forceable)
- PRNG seeded with GetTickCount or time() (limited keyspace)
3. FLAWED IMPLEMENTATION:
- ECB mode (preserves plaintext patterns)
- Initialization vector (IV) reuse across files
- Key stored in plaintext in memory (recoverable from memory dump)
- Partial encryption (only first N bytes encrypted)Reverse engineer the file processing logic:
c// Typical ransomware file encryption flow (decompiled pseudo-code from Ghidra): void encrypt_file(char *filepath) { // 1. Check file extension against target list if (!is_target_extension(filepath)) return; // 2. Generate per-file AES key (32 bytes for AES-256) BYTE aes_key[32]; CryptGenRandom(hProv, 32, aes_key); // 3. Generate random IV (16 bytes) BYTE iv[16]; CryptGenRandom(hProv, 16, iv); // 4. Read file contents HANDLE hFile = CreateFile(filepath, GENERIC_READ, ...); BYTE *plaintext = read_entire_file(hFile); // 5. Encrypt with AES-256-CBC aes_cbc_encrypt(plaintext, file_size, aes_key, iv); // 6. Encrypt AES key with RSA public key BYTE encrypted_key[256]; // RSA-2048 output rsa_encrypt(aes_key, 32, rsa_pubkey, encrypted_key); // 7. Write: encrypted_data + encrypted_key + IV to file write_file(filepath, encrypted_data, encrypted_key, iv); // 8. Rename file with ransomware extension rename_file(filepath, strcat(filepath, ".locked")); }
Test the implementation for exploitable flaws:
pythonfrom Crypto.Cipher import AES import os import struct # Test 1: Check if same key is used for multiple files # Compare encrypted versions of known files def check_key_reuse(file1_enc, file2_enc): with open(file1_enc, "rb") as f: data1 = f.read() with open(file2_enc, "rb") as f: data2 = f.read() # Extract IVs (location depends on ransomware family) # If IVs are same and files share encrypted blocks -> same key iv1 = data1[-16:] # Example: IV at end iv2 = data2[-16:] if iv1 == iv2: print("[!] Same IV detected - key reuse likely") # Test 2: Check for predictable key derivation # If key is derived from timestamp, iterate possible values def brute_force_timestamp_key(encrypted_file, known_header, timestamp_range): with open(encrypted_file, "rb") as f: encrypted_data = f.read() for ts in timestamp_range: # Derive key the same way ransomware does import hashlib key = hashlib.sha256(str(ts).encode()).digest() iv = encrypted_data[-16:] cipher = AES.new(key, AES.MODE_CBC, iv) decrypted = cipher.decrypt(encrypted_data[:16]) if decrypted[:len(known_header)] == known_header: print(f"[!] Key found! Timestamp: {ts}") return key return None # Test 3: Check for ECB mode (pattern preservation) def check_ecb_mode(encrypted_file): with open(encrypted_file, "rb") as f: data = f.read() # ECB produces identical ciphertext for identical plaintext blocks blocks = [data[i:i+16] for i in range(0, len(data), 16)] unique = len(set(blocks)) total = len(blocks) if unique < total * 0.95: print(f"[!] ECB mode likely: {total-unique} duplicate blocks out of {total}")
Use identified weaknesses for key recovery:
python# Recovery Method 1: Extract key from memory dump # Volatility plugin to scan for AES key schedules # vol3 -f memory.dmp windows.yarascan --yara-rule "aes_key_schedule" # Recovery Method 2: Known-plaintext attack (weak algorithms) def xor_key_recovery(encrypted_file, known_plaintext): """Recover XOR key from known plaintext-ciphertext pair""" with open(encrypted_file, "rb") as f: ciphertext = f.read() key = bytes(c ^ p for c, p in zip(ciphertext, known_plaintext)) # Find repeating key length for key_len in range(1, 256): candidate = key[:key_len] if all(key[i] == candidate[i % key_len] for i in range(min(len(key), key_len * 4))): print(f"XOR key (length {key_len}): {candidate.hex()}") return candidate return None # Recovery Method 3: Check NoMoreRansom for existing decryptors # https://www.nomoreransom.org/en/decryption-tools.html
Compile findings into a structured report:
Analysis should document:
- Algorithm identified (AES, RSA, ChaCha20, custom)
- Key size and mode of operation (CBC, CTR, ECB, GCM)
- Key generation method (CSPRNG, predictable seed, static key)
- Key storage location (appended to file, registry, C2 transmission)
- File modification pattern (full encryption, partial, header-only)
- Targeted file extensions
- Ransom note format and payment infrastructure
- Decryption feasibility assessment (possible/impossible/partial)
- Recommended recovery approach| Term | Definition | |------|------------| | Hybrid Encryption | Combining symmetric (AES) for fast file encryption with asymmetric (RSA) for secure key wrapping; the standard ransomware approach | | Key Wrapping | Encrypting the per-file symmetric key with the attacker's RSA public key so only the attacker's private key can decrypt it | | ECB Mode | Electronic Codebook mode encrypts each block independently; preserves patterns in plaintext, a critical weakness enabling partial recovery | | Known-Plaintext Attack | Using a known original file and its encrypted version to derive the encryption key; effective against XOR and weak stream ciphers | | Key Schedule | The expanded form of an AES key in memory; scannable in memory dumps to recover encryption keys before they are erased | | CSPRNG | Cryptographically Secure Pseudo-Random Number Generator; ransomware using CryptGenRandom produces unpredictable keys | | Partial Encryption | Some ransomware only encrypts the first N bytes or every Nth block for speed; unencrypted portions may aid recovery |
Context: An organization is hit with ransomware encrypting file servers. Management needs to know if decryption is possible without paying the ransom before making a recovery decision.
Approach:
Pitfalls:
RANSOMWARE ENCRYPTION ANALYSIS
================================
Sample: lockbit3.exe
Family: LockBit 3.0 / LockBit Black
SHA-256: abc123def456...
ENCRYPTION SCHEME
File Cipher: AES-256-CTR (per-file unique key)
Key Wrapping: RSA-2048 (public key embedded in binary)
Key Generation: CryptGenRandom (CSPRNG - unpredictable)
IV Generation: Random 16 bytes per file
File Structure: [encrypted_data][rsa_encrypted_key(256B)][iv(16B)][magic(8B)]
TARGETED EXTENSIONS
Total: 412 extensions targeted
Categories: Documents (.doc, .xls, .pdf), Databases (.sql, .mdb),
Archives (.zip, .7z), Source code (.py, .java, .cs)
Excluded: .exe, .dll, .sys, .lnk (system files preserved)
IMPLEMENTATION ANALYSIS
Key Strength: STRONG - per-file random keys, no reuse
Mode Security: STRONG - CTR mode with unique nonces
Key Storage: RSA-encrypted key appended to each file
Shadow Copies: Deleted via vssadmin and WMI
DECRYPTION FEASIBILITY
Without Key: NOT POSSIBLE
- No implementation flaws identified
- RSA-2048 key wrapping prevents brute force
- CSPRNG prevents key prediction
- No existing free decryptor available
RECOVERY OPTIONS
1. Restore from offline backups (recommended)
2. Check for volume shadow copies (low probability - ransomware deletes them)
3. Memory forensics if machine was not rebooted (key may persist in RAM)
4. Negotiate with attacker (last resort - no guarantee of decryption)| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-12 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-06 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-07 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-22 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-18 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-04 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-09 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-17 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-10 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-21 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-13 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-03 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-01 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-02 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-20 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-05 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-19 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-11 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-15 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-16 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +27 percentage points is the difference between those two pass rates over the 22 comparable cases.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
Other measured skills in the registry, with their headline benchmark lift.