← Back to Writeups
CUSTOMMediumForensics

Oleg

XESXOR9/2/20262 min read
#forensics#custom#medium#utf16le_string_search#zipcrypto_known_plaintext_atta#mask_bruteforce#linux

Oleg

Platform: duckerz | Category: Forensics | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-01-09 | Status: Solved Techniques: utf16le_string_search, zipcrypto_known_plaintext_attack, mask_bruteforce

Summary

Task: recover the password for a protected GovSec ZIP using a VMware memory dump and the encrypted archive contents. Solution: treat the UTF-16LE flag-like string in memory as a stale decoy, recover ZipCrypto keys with bkcrack known plaintext, then brute-force only the changed suffix behind the stable prefix.

Oleg — duckerz

Description

The challenge provided two important artifacts: a VMware memory dump Oleg-GovSec.vmem and a password-protected archive Oleg/GovSec.zip. The goal was to recover the real archive password, which was also the flag.

Recon

The first useful observation was that this was not just a memory-forensics task. The archive itself mattered because it used legacy ZipCrypto, which is vulnerable to known-plaintext attacks.

Relevant files:

  • Oleg-GovSec.vmem
  • Oleg/GovSec.zip

Initial string searches through the dump quickly produced something that looked extremely convincing: a full DUCKERZ{...} value stored as UTF-16LE. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

False Lead / Decoy

Searching the memory dump revealed this flag-like UTF-16LE string:

text
DUCKERZ{REDACTEDEKxuj0atl2D}

It matched the challenge theme perfectly and looked like the shared archive password mentioned in the scenario. However, testing it against the archive failed. This was the key turning point: the memory dump contained an old or stale password-like value, not the current one.

The important lesson is that a believable string found in memory is not automatically the live credential. Here the dump gave a stable prefix, but the suffix had changed. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

ZipCrypto Recovery

Because GovSec.zip used legacy ZipCrypto, the better route was to recover the internal stream-cipher state with a known-plaintext attack. A good plaintext target was monitor.bat, using the decrypted file recovered from the archive contents.

bash
bkcrack -C "Oleg/GovSec.zip" -c monitor.bat -p govsec_decrypted/monitor.bat

Recovered ZipCrypto keys:

  • 0eb36c38
  • 10584367
  • 983d6808

Once these keys were known, the task was no longer “guess the whole password”. It became “recover the real password that matches the already recovered ZipCrypto state”. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Final Password Recovery

The stale memory string was still useful because almost all of it looked stable. Instead of brute-forcing a full flag, it was enough to keep the fixed prefix and search only the unknown suffix with a mask.

bash
bkcrack -k 0eb36c38 10584367 983d6808 -m 'DUCKERZ{REDACTED?a?a?a?a?a?a?a?a?a}' -j 8

This quickly recovered the real password:

text
DUCKERZ{REDACTED}

Verification:

bash
7z t -p'DUCKERZ{REDACTED}' 'Oleg/GovSec.zip'

Expected result:

text
Everything is Ok

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Minimal Reproduction Script

python
#!/usr/bin/env python3
import subprocess

zip_path = "Oleg/GovSec.zip"
known_plaintext = "govsec_decrypted/monitor.bat"
keys = ["0eb36c38", "10584367", "983d6808"]
mask = "DUCKERZ{REDACTED?a?a?a?a?a?a?a?a?a}"

subprocess.run([
    "bkcrack",
    "-C", zip_path,
    "-c", "monitor.bat",
    "-p", known_plaintext,
], check=False)

subprocess.run([
    "bkcrack",
    "-k", *keys,
    "-m", mask,
    "-j", "8",
], check=False)

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍