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, AU only (never PNG or GIF)
  • Payload / embedded file: the file you hide (-ef going in) or recover (-xf coming out)
  • Stego file: the carrier after embedding (-sf); for extract and info this 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 encinfo lists algorithms, -e none turns it off
  • Compression: the payload is zlib-compressed by default; -Z disables 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; info reports it and embed fails 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.

FlagDoesFlagDoes
embedHide a file in a carrier-pPassphrase (-p '' = empty)
extractPull the hidden file out-xfOutput file (-xf - = stdout)
infoReport format, capacity, payload-eEncryption algo+mode (-e none)
-cfCover file (the embed input)-z / -ZCompress level 1-9 / disable
-efFile to embed (the payload)-NDo not embed the original filename
-sfStego file (embed out / extract in)-fForce overwrite existing files
encinfoList encryption algorithms-q / -vQuiet / 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 KB from info: how much this carrier can hold. embed fails past it.
  • embedded file "secret.txt": size / encrypted: rijndael-128, cbc / compressed: yes: the payload metadata, shown only after a correct passphrase to info.
  • 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#

ProblemCauseFix
command not foundsteghide or stegseek not installedapt install steghide; grab stegseek’s .deb
the file format ... is not supportedPNG / GIF or a non-carrier formatNot JPEG/BMP/WAV/AU; switch to zsteg or binwalk
could not extract any data with that passphrase!Wrong / blank passphrase, or no payloadTry -p '', then crack with stegseek
the cover file is too short to embed the dataPayload larger than capacityUse a bigger carrier or a smaller payload
the file "out.jpg" does already existOutput name already takenAdd -f, or choose another -sf name
stegseek finds nothingPassphrase not in the wordlist, or not steghideBigger wordlist; stegseek --seed to confirm it is steghide
Extract overwrote a local fileSteghide restores the embedded filenameAlways pin the output with -xf out.bin

Gotchas#

  • embed overwrites 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 .jpg that is secretly a PNG is rejected. Check the real type with file first; 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.

Find us elsewhere

Merch, stickers, and moreSupport the work at the Solvere Labs shop