Foreign Body
Foreign Body
Platform: hackerlab | Category: Steganography | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-04-08 | Status: Solved Techniques: empty_passphrase_extraction, payload_carving_validation, steghide_seed_discovery, zip_header_repair
Summary
Task: a clean-looking JPEG with 'YOU HAVE BEEN HACKED!' hides a steghide payload without obvious metadata or appended data. Solution: use stegseek to identify steghide, extract the payload with the empty passphrase, repair a one-byte ZIP header corruption, and read the embedded Python file containing the flag.
Foreign Body — Hackerlab
Description
Инородное тело
English summary: the challenge provides a JPEG image named hacked.jpg. The file looks ordinary and displays YOU HAVE BEEN HACKED!, but the goal is to recover the hidden CODEBY{...} flag.
Analysis
Initial reconnaissance
The archive unpacked to a single file: hacked.jpg.
Basic triage gave a very clean result:
fileandexiftoolidentified a normal baseline JPEG- image size was
480x272 - metadata was minimal JFIF only
- no appended bytes appeared after the JPEG EOI marker
FF D9 - no obvious embedded
ZIP,RAR,7z,PNG, orPDFsignatures were present
Visually, the image only showed the message YOU HAVE BEEN HACKED!.
That pushed the investigation toward classic JPEG steganography rather than metadata tricks or appended-file carving.
False lead: channel, bitplane, and OutGuess-style probing
Several standard JPEG stego paths were tested first:
- RGB channel inspection
- bitplane extraction
- additional contrast and difference views
- OutGuess-style extraction attempts
OutGuess produced multiple random-looking blobs and inconsistent output sizes depending on the key. This was a false lead: the extracted data did not contain stable plaintext, recognizable magic bytes, or the flag. The important lesson here is that noisy output from JPEG stego tools is not proof of a correct path.
The pivot: confirming steghide with stegseek --seed
The decisive clue came from:
bash read-only • copy allowedstegseek --seed hacked.jpg
This reported a likely hidden steghide payload and exposed several useful properties:
- possible seed / passphrase candidate:
3b75655e - embedded plain size: about
1.1 KB (compressed) - encryption:
rijndael-128 - mode:
cbc
At that point the file no longer looked like a generic JPEG puzzle. It looked like a steghide container inside a perfectly normal JPEG.
Solution
Step 1: Extract the steghide payload
Run stegseek against the image with a wordlist. In this case, the successful passphrase was the empty string:
bash read-only • copy alloweddocker run --rm -v "$PWD":/steg -v "/Users/sergeyskorobogatov/Projects/Agents/CTF/resources/wordlists/passwords":/wordlists:ro rickdejager/stegseek:latest /steg/hacked.jpg /wordlists/rockyou.txt -xf /steg/steghide_extracted.bin
Important output:
- passphrase found:
"" - original embedded filename:
data.bin
This produced steghide_extracted.bin.
Step 2: Validate the extracted blob
The blob looked like ZIP data, but unzip failed because the local file header was corrupted.
The start of the file was:
text read-only • copy allowed50 4b ff 04
but a valid ZIP local header must begin with:
text read-only • copy allowed50 4b 03 04
At the same time, the central directory and end-of-central-directory records were still present. That strongly suggested a deliberately damaged container rather than a wrong extraction.
Step 3: Repair the ZIP header
Fix the third byte from 0xff to 0x03, then save the result as steghide_fixed.zip:
python read-only • copy allowed#!/usr/bin/env python3 from pathlib import Path data = bytearray(Path("steghide_extracted.bin").read_bytes()) assert data[:4] == bytes.fromhex("50 4b ff 04") data[2] = 0x03 Path("steghide_fixed.zip").write_bytes(data)
After that, the archive extracted normally.
Step 4: Read the recovered file
Unzip the repaired archive:
bash read-only • copy allowedunzip steghide_fixed.zip -d zip_fixed
The ZIP contained CODEBY.py. Opening it revealed:
python read-only • copy allowedprint("CODEBY{REDACTED}")
So the final flag is recovered directly from the embedded script.
Full working recovery script
python read-only • copy allowed#!/usr/bin/env python3 from pathlib import Path import zipfile blob_path = Path("steghide_extracted.bin") fixed_zip = Path("steghide_fixed.zip") out_dir = Path("zip_fixed") data = bytearray(blob_path.read_bytes()) if data[:4] == bytes.fromhex("50 4b ff 04"): data[2] = 0x03 else: raise SystemExit(f"Unexpected header: {data[:4].hex()}") fixed_zip.write_bytes(data) with zipfile.ZipFile(fixed_zip, "r") as zf: zf.extractall(out_dir) code = zf.read("CODEBY.py").decode("utf-8", errors="replace") print(code)