Description
Find the hidden flag. A packet capture file is provided containing network traffic - dig through it to uncover something suspicious.
Setup
Download try_me.pcap.
Install Wireshark to inspect and export objects from the capture.
sudo apt install wiresharkSolution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1
Export HTTP objects from the captureObservationI noticed that the PCAP contains a mix of TLS-encrypted and plain HTTP traffic, which suggested exporting HTTP objects as the first step since cleartext transfers would be immediately readable and might contain suspicious files.Open try_me.pcap in Wireshark. Most traffic is TLS-encrypted, but five plain HTTP transfers are visible. Use File > Export Objects > HTTP and click Save All. Among the exports you will find duck.png and evil_duck.png. Both images share the same pixel dimensions, yet evil_duck.png is more than twice the file size of duck.png despite appearing lower quality - a clear sign that extra data is packed inside it.bash# In Wireshark: File -> Export Objects -> HTTP -> Save Allbashls -lh duck.png evil_duck.pngbashfile duck.png evil_duck.pngWhat didn't work first
Tried: Running steghide extract -sf evil_duck.png to find the hidden data
steghide uses its own passphrase-protected embedding format (DCT coefficient modification in JPEG or palette manipulation) and will report 'steghide: could not extract any data with that passphrase' because evil_duck.png uses Invoke-PSImage's LSB-4 pixel encoding, not steghide's scheme. No passphrase guess will succeed; the correct approach reads raw pixel values with PIL.
Tried: Running zsteg evil_duck.png to detect the hidden payload automatically
zsteg scans common LSB bit-planes but defaults to checking 1 bit per channel. Invoke-PSImage writes into the low 4 bits (not just 1 bit), so zsteg either produces garbled output or no hits at all. The correct approach targets exactly bits 0-3 of the blue and green channels rather than relying on a generic scanner.
Learn more
Why evil_duck.png is suspicious. When two images are the same dimensions but one is dramatically larger, the extra bytes are almost always hidden payload rather than image data. Standard tools like
steghideandzstegfind nothing here because the data is not hidden with those schemes.The HTTP traffic also reveals a tell: Wireshark's Server Name column shows the user browsed GitHub, Microsoft documentation, and powershell.org before the image downloads. That browsing trail is a hint that PowerShell steganography is involved.
Step 2
Identify the steganography scheme: Invoke-PSImageObservationI noticed that evil_duck.png was more than twice the file size of duck.png despite identical dimensions and lower apparent quality, and the PCAP's browsing history included PowerShell and GitHub visits, which suggested a PowerShell-based pixel steganography tool like Invoke-PSImage was responsible.Searching for 'powershell steganography' surfaces the peewpw/Invoke-PSImage GitHub repository. This tool hides a PowerShell script inside a PNG file by writing the payload into the least significant 4 bits of the blue and green channel of each pixel. The image looks nearly normal to the eye, but every pixel carries 1 byte of hidden script data, which explains why evil_duck.png is so much larger than a clean image of the same dimensions.Learn more
How LSB-4 pixel encoding works. Each pixel stores 1 byte of payload across two channels: the low 4 bits of blue hold the upper nibble, and the low 4 bits of green hold the lower nibble. Reconstructing the byte:
r, g, b = pixel byte = ((b & 0x0F) << 4) | (g & 0x0F)
Walking every pixel in order yields a complete PowerShell script embedded invisibly inside the image.
Step 3
Decode evil_duck.png and run the extracted scriptObservationI noticed that Invoke-PSImage encodes exactly one payload byte per pixel using bits 0-3 of the blue and green channels, which suggested writing a Python PIL loop to reconstruct those nibbles and recover the hidden PowerShell script that XORs two strings to produce the flag.Use the PCsXcetra/Decode_PS_Stego tool (PowershellStegoDecode.exe on Windows) to extract the hidden PowerShell from evil_duck.png. Alternatively, the Python snippet below decodes it manually. The extracted script XORs two hardcoded strings together and writes the result to flag.txt. Running that script produces the flag.bash# Python manual decode (Linux-friendly):pythonpython3 -c "pythonfrom PIL import Imagebashim = Image.open('evil_duck.png')bashw, h = im.sizebashout = []bashfor x in range(h):bashfor y in range(w):bashr, g, b = im.getpixel((y, x))bashout.append(chr(((b & 15) << 4) | (g & 15)))pythonprint(''.join(out))" > extracted.ps1bashbash# Then run the extracted PowerShell (Windows / pwsh):bashpwsh extracted.ps1bashcat flag.txtExpected output
picoCTF{n1c3_job_f1nd1ng_th3_s3cr3t_in_the_im@g3}The extracted script XORs
$string1against$string2byte-by-byte and writes the binary result toflag.txt. You can also paste the two strings into CyberChef's XOR operation to decode without PowerShell.What didn't work first
Tried: Running the Python pixel extraction on duck.png instead of evil_duck.png
duck.png is the clean original image with no hidden payload; reading its pixel LSBs produces random-looking garbage characters rather than a recognizable PowerShell script. The correct target is evil_duck.png, which is the file identified as suspicious by its unexpectedly large file size compared to duck.png.
Tried: Trying to extract the flag by XORing the strings manually in CyberChef before running the pixel decoder
The two XOR strings ($string1 and $string2) are embedded inside the PowerShell script that is hidden in evil_duck.png, not available as standalone data. You must first complete the pixel extraction step to obtain extracted.ps1 and read the two string values from it before CyberChef XOR can be applied.
Learn more
The embedded PowerShell payload (for reference).
$out = "flag.txt" $enc = [system.Text.Encoding]::UTF8 $string1 = "HEYWherE(IS_tNE)50uP?^DId_YOu(\]E@t*mY_3RD()B2g3l?" $string2 = "8,:8+14>Fx0l+$*KjVD>[o*.;+1|*[n&2G^201l&,Mv+_'T_B" $data1 = $enc.GetBytes($string1) $bytes = $enc.GetBytes($string2) for($i=0; $i -lt $bytes.count ; $i++) { $bytes[$i] = $bytes[$i] -bxor $data1[$i] } [System.IO.File]::WriteAllBytes("$out", $bytes)XOR-ing those two strings byte-by-byte produces the ASCII bytes of the flag. The XOR key and ciphertext are baked into the image - no external password or key file is needed.
Interactive tools
- StegallDrop any file and Stegall runs every applicable steg technique in parallel: LSB sweeps, bit planes, spectrograms, polyglot carving, metadata, whitespace decode, and a 6-layer base/ROT/XOR/zlib cascade. Recursively unpacks results and surfaces flag matches.
- Hex ViewerView text or raw hex bytes as a xxd-style hex dump with byte offset, hex columns, and ASCII sidebar. Highlights printable characters and null bytes.
- Strings ExtractorPull printable text from any binary, library, or image. ASCII and UTF-16 detection, configurable minimum length, flag-like highlight, no command line needed.
Flag
Reveal flag
picoCTF{n1c3_job_f1nd1ng_th3_s3cr3t_in_the_im@g3}
The flag is recovered by exporting HTTP objects from the PCAP, then decoding evil_duck.png with the Invoke-PSImage reversal tool (or a Python pixel-reader) to extract a PowerShell XOR script that writes flag.txt.