Steghide
Steghide hides one file inside a JPEG, BMP, WAV, or AU carrier and pulls it back out with the right passphrase. In CTFs it is the first move when a challenge hands you a suspicious .jpg or .wav and hints there is more here than meets the eye. It answers one question cleanly: is something embedded in this file, and how do I get it out?
Mental model: steghide <info|extract|embed> -sf CARRIER -p PASS. Point an action at the carrier and hand it a passphrase; embed takes the clean input as -cf (the cover) instead of -sf.
New to steganography? Core concepts
- Carrier / cover file: the media that holds the payload; Steghide supports
JPEG,BMP,WAV,AUonly (never PNG or GIF) - Payload / embedded file: the file you hide (
-efgoing in) or recover (-xfcoming out) - Stego file: the carrier after embedding (
-sf); forextractandinfothis is the input - Passphrase: gates both embed and extract (
-p); an empty-p ''is common in easy challenges but never assumed - Encryption: the payload is encrypted by default (rijndael-128 / AES, cbc mode);
steghide encinfolists algorithms,-e noneturns it off - Compression: the payload is zlib-compressed by default;
-Zdisables it - Seed-driven embedding: Steghide scatters bits with a passphrase-seeded pseudo-random walk, not naive LSB, so the passphrase IS the seed and the only proof of a payload
- Capacity: how many bytes a carrier can hold;
inforeports it andembedfails past it
When to reach for Steghide (and when not)
Reach for it when a CTF hands you a JPEG, BMP, WAV, or AU and stego is implied, when you have a candidate passphrase (often hidden elsewhere in the challenge), or when you want to tuck a file behind a passphrase in your own carrier.
Reach for something else for PNG or GIF LSB stego (zsteg), for data appended or concatenated onto a file rather than embedded (binwalk, foremost), for pure metadata hiding (exiftool), for visual and bit-plane inspection (stegsolve), or when there is simply no stego and the challenge points elsewhere.
Install, update, verify#
sudo apt install -y steghide # Debian / Ubuntu / Kali
sudo dnf install -y steghide # Fedora / RHEL (EPEL)
sudo apt install --only-upgrade -y steghide # update
steghide --version # version string
command -v steghide # confirm it is on PATH
steghide encinfo # list available encryption algorithms
stegseek is a separate, much faster passphrase cracker: sudo apt install -y stegseek on Kali, or grab the .deb from its GitHub releases on other distros. Confirm your install round-trips before trusting it (see the breakdown below).
Flags you’ll actually use#
Skim these first; the cheat sheet below is these flags combined.
| Flag | Does | Flag | Does |
|---|---|---|---|
embed | Hide a file in a carrier | -p | Passphrase (-p '' = empty) |
extract | Pull the hidden file out | -xf | Output file (-xf - = stdout) |
info | Report format, capacity, payload | -e | Encryption algo+mode (-e none) |
-cf | Cover file (the embed input) | -z / -Z | Compress level 1-9 / disable |
-ef | File to embed (the payload) | -N | Do not embed the original filename |
-sf | Stego file (embed out / extract in) | -f | Force overwrite existing files |
encinfo | List encryption algorithms | -q / -v | Quiet / verbose |
Cheat sheet#
Each block starts simple and adds one flag at a time, so you can stop at the line that does the job.
# Inspect first: what format, how big, is there a payload?
steghide info CARRIER # format + capacity, then prompts to probe
steghide info CARRIER -p '' # probe with the empty passphrase, no prompt
# Extract: build up from the easy case
steghide extract -sf CARRIER -p '' # 1. try the empty passphrase (common)
steghide extract -sf CARRIER -p 'PASS' # 2. a passphrase the challenge hinted at
steghide extract -sf CARRIER -p 'PASS' -xf out.bin # 3. + choose the output name
steghide extract -sf CARRIER -p 'PASS' -xf - # 4. + stream straight to stdout
# Crack an unknown passphrase (stegseek is the fast path)
stegseek CARRIER /usr/share/wordlists/rockyou.txt # crack + auto-extract to CARRIER.out
stegseek --seed CARRIER # is this even a steghide file? (no wordlist)
stegseek CARRIER wordlist.txt loot.bin # write the recovered payload to loot.bin
# Embed your own files (round-trip self-test)
steghide embed -cf cover.jpg -ef secret.txt -p 'PASS' # overwrites cover.jpg!
steghide embed -cf cover.jpg -ef secret.txt -p 'PASS' -sf out.jpg # keep the original
steghide embed -cf cover.wav -ef secret.txt -p '' -N # empty pass, hide the name
steghide embed -cf cover.jpg -ef secret.txt -p 'PASS' -e none -Z # no crypto, no compression
# Chain with the usual first-look tools
file CARRIER && exiftool CARRIER # confirm the real format, skim metadata for a passphrase
binwalk CARRIER # rule out appended/concatenated data instead
Command breakdowns#
Each block below explains one situation, the exact command, and what to expect back.
Inspect a file for a hidden payload#
steghide info image.jpg
Prints the format and capacity, then asks Try to get information about embedded data ?. Answer y and it prompts for a passphrase; a correct one reveals the embedded file’s name, size, and whether it is encrypted and compressed.
Try the empty passphrase first#
steghide extract -sf image.jpg -p ''
Many easy challenges embed with no passphrase. -p '' tests that in one shot instead of waiting on the interactive prompt. Success prints wrote extracted data to "...".
Extract with a known passphrase#
steghide extract -sf image.jpg -p 'hunter2' -xf loot.bin
Use the passphrase the challenge hinted at (a filename, an EXIF comment, a nearby string). -xf pins the output name so the payload does not overwrite something in your working directory.
Extract straight to your terminal#
steghide extract -sf image.jpg -p 'hunter2' -xf -
-xf - streams the payload to stdout. Handy when the hidden file is plain text (a flag or clue) and you do not want a file on disk. Pipe it: ... -xf - | strings.
Crack an unknown passphrase with stegseek#
stegseek image.jpg /usr/share/wordlists/rockyou.txt
stegseek brute-forces the passphrase against the wordlist orders of magnitude faster than older scripts, and on a hit it auto-extracts the payload to image.jpg.out. This is the move whenever info or extract fails and stego is still expected.
Confirm a file is even a steghide carrier#
stegseek --seed image.jpg
Seed-cracking needs no wordlist: it finds the embedding seed to tell you whether the file holds Steghide data at all (and roughly how long the payload is). Run it before burning time on a full wordlist crack.
Embed a file behind a passphrase#
steghide embed -cf cover.jpg -ef secret.txt -p 'PASS'
-cf is the clean cover, -ef is the file to hide. By default the payload is AES-encrypted and zlib-compressed. Note this writes the result back into cover.jpg in place.
Embed without clobbering the original carrier#
steghide embed -cf cover.jpg -ef secret.txt -p 'PASS' -sf out.jpg
Adding -sf out.jpg sends the stego output to a new file and leaves cover.jpg untouched. Do this every time you care about keeping the pristine carrier.
Hide the payload’s original filename#
steghide embed -cf cover.wav -ef flag.txt -p 'PASS' -N -sf out.wav
-N omits the embedded filename, so a later extract cannot leak flag.txt as a hint. The extractor must supply -xf to name the output themselves.
Turn off encryption or compression#
steghide embed -cf cover.jpg -ef secret.txt -p 'PASS' -e none -Z -sf out.jpg
-e none skips encryption and -Z skips compression. Useful when you want the raw bytes recoverable by other tools, or to reproduce a challenge that embedded without crypto.
Round-trip test that your install works#
echo 'flag{demo}' > secret.txt
steghide embed -cf small.jpg -ef secret.txt -p 'pass123' -sf out.jpg
steghide extract -sf out.jpg -p 'pass123' -xf - # should print flag{demo}
Proves embed and extract both work end to end on a file you own. If embed complains the cover is too short, use a larger JPEG.
Read metadata for a passphrase hint before you crack#
file image.jpg && exiftool image.jpg
Confirms the carrier is really a JPEG (not a renamed PNG) and dumps EXIF fields. Challenges love to stash the passphrase in a comment, artist, or copyright field, which saves you a wordlist run entirely.
Rule out appended data before assuming steghide#
binwalk image.jpg
If binwalk shows a trailing ZIP or another file signature, the data is appended, not Steghide-embedded. Carve it with binwalk -e or foremost instead of fighting a passphrase that does not exist.
Reading the output#
capacity: 3.6 KBfrominfo: how much this carrier can hold.embedfails past it.embedded file "secret.txt": size / encrypted: rijndael-128, cbc / compressed: yes: the payload metadata, shown only after a correct passphrase toinfo.wrote extracted data to "secret.txt".: success. Open the file, it is the payload.could not extract any data with that passphrase!: wrong passphrase OR no Steghide payload at all. It does not mean the file is clean.- stegseek
[i] Found passphrase: "..."then[i] Extracting to image.jpg.out: it cracked the passphrase and dumped the payload.
Troubleshooting#
| Problem | Cause | Fix |
|---|---|---|
command not found | steghide or stegseek not installed | apt install steghide; grab stegseek’s .deb |
the file format ... is not supported | PNG / GIF or a non-carrier format | Not JPEG/BMP/WAV/AU; switch to zsteg or binwalk |
could not extract any data with that passphrase! | Wrong / blank passphrase, or no payload | Try -p '', then crack with stegseek |
the cover file is too short to embed the data | Payload larger than capacity | Use a bigger carrier or a smaller payload |
the file "out.jpg" does already exist | Output name already taken | Add -f, or choose another -sf name |
| stegseek finds nothing | Passphrase not in the wordlist, or not steghide | Bigger wordlist; stegseek --seed to confirm it is steghide |
| Extract overwrote a local file | Steghide restores the embedded filename | Always pin the output with -xf out.bin |
Gotchas#
embedoverwrites the cover file in place unless you pass-sf out.jpg. Skip it and your pristine carrier is gone.- The passphrase IS the seed. There is no “detect then decode” step, so a correct passphrase is the only proof a payload exists. That is exactly why stegseek exists.
- “could not extract” is ambiguous between wrong passphrase and no payload. Never conclude a file is clean from it; confirm with
stegseek --seed. - The empty passphrase is common, not default. You still pass
-p ''explicitly, or Steghide drops to an interactive prompt. - A
.jpgthat is secretly a PNG is rejected. Check the real type withfilefirst; Steghide only ever touches JPEG, BMP, WAV, and AU.
Pairs with#
file and exiftool confirm the carrier’s real format and surface the passphrase that challenges love to hide in metadata, before you ever open a wordlist. stegseek is the fast passphrase cracker and pairs with Steghide directly, auto-extracting the payload on a hit. When the format is PNG or GIF, or the data is appended rather than embedded, reach for zsteg or binwalk and foremost instead. If a recovered archive is itself password-protected, hand it to john or hashcat.