# Run this before anything else — shows codec, resolution, duration, metadata, errors
$ ffmpeg -i challenge.mp4 # More detailed output including all metadata fields
$ ffprobe -v quiet -show_format -show_streams challenge.mp4
# Run this before anything else — shows codec, resolution, duration, metadata, errors
$ ffmpeg -i challenge.mp4 # More detailed output including all metadata fields
$ ffprobe -v quiet -show_format -show_streams challenge.mp4
# Run this before anything else — shows codec, resolution, duration, metadata, errors
$ ffmpeg -i challenge.mp4 # More detailed output including all metadata fields
$ ffprobe -v quiet -show_format -show_streams challenge.mp4
# Extract ALL frames (most important command in video forensics)
$ mkdir frames && ffmpeg -i challenge.mp4 frames/frame_%05d.png # Extract audio track as WAV (for Audacity/spectrogram analysis)
$ ffmpeg -i challenge.mp4 audio.wav # Extract a single frame at a specific timestamp
$ ffmpeg -ss 00:01:23 -i challenge.mp4 -vframes 1 frame_at_83s.png # Extract metadata to a file for searching
$ ffprobe -v quiet -show_format -show_streams challenge.mp4 > metadata.txt
$ grep -i "flag\|ctf\|pico\|comment\|title" metadata.txt
# Extract ALL frames (most important command in video forensics)
$ mkdir frames && ffmpeg -i challenge.mp4 frames/frame_%05d.png # Extract audio track as WAV (for Audacity/spectrogram analysis)
$ ffmpeg -i challenge.mp4 audio.wav # Extract a single frame at a specific timestamp
$ ffmpeg -ss 00:01:23 -i challenge.mp4 -vframes 1 frame_at_83s.png # Extract metadata to a file for searching
$ ffprobe -v quiet -show_format -show_streams challenge.mp4 > metadata.txt
$ grep -i "flag\|ctf\|pico\|comment\|title" metadata.txt
# Extract ALL frames (most important command in video forensics)
$ mkdir frames && ffmpeg -i challenge.mp4 frames/frame_%05d.png # Extract audio track as WAV (for Audacity/spectrogram analysis)
$ ffmpeg -i challenge.mp4 audio.wav # Extract a single frame at a specific timestamp
$ ffmpeg -ss 00:01:23 -i challenge.mp4 -vframes 1 frame_at_83s.png # Extract metadata to a file for searching
$ ffprobe -v quiet -show_format -show_streams challenge.mp4 > metadata.txt
$ grep -i "flag\|ctf\|pico\|comment\|title" metadata.txt
# Slow down video 2x (useful for catching fast-flashing frames visually)
$ ffmpeg -i challenge.mp4 -filter:v "setpts=2.0*PTS" slow_video.mp4 # Slow down audio 2x (for sped-up speech or tones)
$ ffmpeg -i audio.wav -filter:a "atempo=0.5" slow_audio.wav # Attempt to repair a corrupted/broken media file
$ ffmpeg -i broken.mp4 -c copy fixed.mp4
# If that fails, try re-encoding:
$ ffmpeg -i broken.mp4 fixed.mp4 # Extract raw pixel data in grayscale (for pixel-level steganography)
$ ffmpeg -i challenge.mp4 -pix_fmt gray gray_output.mp4
# Slow down video 2x (useful for catching fast-flashing frames visually)
$ ffmpeg -i challenge.mp4 -filter:v "setpts=2.0*PTS" slow_video.mp4 # Slow down audio 2x (for sped-up speech or tones)
$ ffmpeg -i audio.wav -filter:a "atempo=0.5" slow_audio.wav # Attempt to repair a corrupted/broken media file
$ ffmpeg -i broken.mp4 -c copy fixed.mp4
# If that fails, try re-encoding:
$ ffmpeg -i broken.mp4 fixed.mp4 # Extract raw pixel data in grayscale (for pixel-level steganography)
$ ffmpeg -i challenge.mp4 -pix_fmt gray gray_output.mp4
# Slow down video 2x (useful for catching fast-flashing frames visually)
$ ffmpeg -i challenge.mp4 -filter:v "setpts=2.0*PTS" slow_video.mp4 # Slow down audio 2x (for sped-up speech or tones)
$ ffmpeg -i audio.wav -filter:a "atempo=0.5" slow_audio.wav # Attempt to repair a corrupted/broken media file
$ ffmpeg -i broken.mp4 -c copy fixed.mp4
# If that fails, try re-encoding:
$ ffmpeg -i broken.mp4 fixed.mp4 # Extract raw pixel data in grayscale (for pixel-level steganography)
$ ffmpeg -i challenge.mp4 -pix_fmt gray gray_output.mp4
$ file challenge.mp4
challenge.mp4: ISO Media, MP4 Base Media v1 [ISO 14496-12:2003] $ strings challenge.mp4 | grep -i "flag\|ctf\|pico"
(no output)
# Flag was visual content in a frame, not ASCII bytes in the container # Opened in VLC, watched the 3-minute video
# Saw: a person talking, some background music, a brief flicker around the 1-minute mark
# Didn't pause at the flicker — it was too fast to catch manually $ ffmpeg -i challenge.mp4
# Duration: 00:03:12, Stream #0: Video h264, 1920x1080 @ 30fps
# Stream #1: Audio aac, stereo 44100Hz
# No errors, no obvious metadata
# I stopped reading here — missed the custom "comment" metadata field # Extracted audio and opened in Audacity
# Checked spectrogram — nothing obvious
# Tried reversing, slowing down — nothing # Tried ffprobe — copy-pasted output but didn't grep it
# Scrolled past "TAG:comment=Look at frame 2847" without noticing # --- 50 minutes in ---
# Extracted all frames (should have done this at minute 3)
$ mkdir frames && ffmpeg -i challenge.mp4 frames/frame_%05d.png
# Generated 5,760 PNG files # Wrote a quick script to find anomalous frames
$ python3 -c "
import os
from PIL import Image
sizes = [(os.path.getsize(f'frames/{f}'), f) for f in os.listdir('frames')]
sizes.sort(reverse=True)
print(sizes[:5])
"
# frame_02847.png was 8x larger than average — contained the QR code $ zbarimg frames/frame_02847.png
QR-Code:picoCTF{...}
$ file challenge.mp4
challenge.mp4: ISO Media, MP4 Base Media v1 [ISO 14496-12:2003] $ strings challenge.mp4 | grep -i "flag\|ctf\|pico"
(no output)
# Flag was visual content in a frame, not ASCII bytes in the container # Opened in VLC, watched the 3-minute video
# Saw: a person talking, some background music, a brief flicker around the 1-minute mark
# Didn't pause at the flicker — it was too fast to catch manually $ ffmpeg -i challenge.mp4
# Duration: 00:03:12, Stream #0: Video h264, 1920x1080 @ 30fps
# Stream #1: Audio aac, stereo 44100Hz
# No errors, no obvious metadata
# I stopped reading here — missed the custom "comment" metadata field # Extracted audio and opened in Audacity
# Checked spectrogram — nothing obvious
# Tried reversing, slowing down — nothing # Tried ffprobe — copy-pasted output but didn't grep it
# Scrolled past "TAG:comment=Look at frame 2847" without noticing # --- 50 minutes in ---
# Extracted all frames (should have done this at minute 3)
$ mkdir frames && ffmpeg -i challenge.mp4 frames/frame_%05d.png
# Generated 5,760 PNG files # Wrote a quick script to find anomalous frames
$ python3 -c "
import os
from PIL import Image
sizes = [(os.path.getsize(f'frames/{f}'), f) for f in os.listdir('frames')]
sizes.sort(reverse=True)
print(sizes[:5])
"
# frame_02847.png was 8x larger than average — contained the QR code $ zbarimg frames/frame_02847.png
QR-Code:picoCTF{...}
$ file challenge.mp4
challenge.mp4: ISO Media, MP4 Base Media v1 [ISO 14496-12:2003] $ strings challenge.mp4 | grep -i "flag\|ctf\|pico"
(no output)
# Flag was visual content in a frame, not ASCII bytes in the container # Opened in VLC, watched the 3-minute video
# Saw: a person talking, some background music, a brief flicker around the 1-minute mark
# Didn't pause at the flicker — it was too fast to catch manually $ ffmpeg -i challenge.mp4
# Duration: 00:03:12, Stream #0: Video h264, 1920x1080 @ 30fps
# Stream #1: Audio aac, stereo 44100Hz
# No errors, no obvious metadata
# I stopped reading here — missed the custom "comment" metadata field # Extracted audio and opened in Audacity
# Checked spectrogram — nothing obvious
# Tried reversing, slowing down — nothing # Tried ffprobe — copy-pasted output but didn't grep it
# Scrolled past "TAG:comment=Look at frame 2847" without noticing # --- 50 minutes in ---
# Extracted all frames (should have done this at minute 3)
$ mkdir frames && ffmpeg -i challenge.mp4 frames/frame_%05d.png
# Generated 5,760 PNG files # Wrote a quick script to find anomalous frames
$ python3 -c "
import os
from PIL import Image
sizes = [(os.path.getsize(f'frames/{f}'), f) for f in os.listdir('frames')]
sizes.sort(reverse=True)
print(sizes[:5])
"
# frame_02847.png was 8x larger than average — contained the QR code $ zbarimg frames/frame_02847.png
QR-Code:picoCTF{...}
$ mkdir frames && ffmpeg -i challenge.mp4 frames/frame_%05d.png # Find the anomalous frame by file size (larger = more content)
$ ls -la frames/ | sort -k5 -rn | head -10 # Or use Python to find outliers:
$ python3 -c "
import os
from pathlib import Path
files = list(Path('frames').glob('*.png'))
sizes = [(f.stat().st_size, f.name) for f in files]
avg = sum(s for s,_ in sizes) / len(sizes)
outliers = [(s,n) for s,n in sizes if s > avg * 3]
print(sorted(outliers, reverse=True)[:10])
" # Once you find the frame, decode it:
$ zbarimg frames/frame_02847.png # for QR/barcodes
$ tesseract frames/frame_02847.png out # for text (OCR)
$ mkdir frames && ffmpeg -i challenge.mp4 frames/frame_%05d.png # Find the anomalous frame by file size (larger = more content)
$ ls -la frames/ | sort -k5 -rn | head -10 # Or use Python to find outliers:
$ python3 -c "
import os
from pathlib import Path
files = list(Path('frames').glob('*.png'))
sizes = [(f.stat().st_size, f.name) for f in files]
avg = sum(s for s,_ in sizes) / len(sizes)
outliers = [(s,n) for s,n in sizes if s > avg * 3]
print(sorted(outliers, reverse=True)[:10])
" # Once you find the frame, decode it:
$ zbarimg frames/frame_02847.png # for QR/barcodes
$ tesseract frames/frame_02847.png out # for text (OCR)
$ mkdir frames && ffmpeg -i challenge.mp4 frames/frame_%05d.png # Find the anomalous frame by file size (larger = more content)
$ ls -la frames/ | sort -k5 -rn | head -10 # Or use Python to find outliers:
$ python3 -c "
import os
from pathlib import Path
files = list(Path('frames').glob('*.png'))
sizes = [(f.stat().st_size, f.name) for f in files]
avg = sum(s for s,_ in sizes) / len(sizes)
outliers = [(s,n) for s,n in sizes if s > avg * 3]
print(sorted(outliers, reverse=True)[:10])
" # Once you find the frame, decode it:
$ zbarimg frames/frame_02847.png # for QR/barcodes
$ tesseract frames/frame_02847.png out # for text (OCR)
$ ffprobe -v quiet -show_format -show_streams challenge.mp4 | grep -i "TAG\|title\|comment\|artist\|encoder"
TAG:title : nothing_here
TAG:comment : picoCTF{m3tadata_1s_0ften_0verl00ked}
TAG:encoder : Lavf58.76.100 # Also check for unusual stream counts or codec names
$ ffprobe -v quiet -show_streams challenge.mp4 | grep codec_name
codec_name=h264
codec_name=aac
codec_name=mjpeg # ← unexpected third stream — worth extracting
$ ffprobe -v quiet -show_format -show_streams challenge.mp4 | grep -i "TAG\|title\|comment\|artist\|encoder"
TAG:title : nothing_here
TAG:comment : picoCTF{m3tadata_1s_0ften_0verl00ked}
TAG:encoder : Lavf58.76.100 # Also check for unusual stream counts or codec names
$ ffprobe -v quiet -show_streams challenge.mp4 | grep codec_name
codec_name=h264
codec_name=aac
codec_name=mjpeg # ← unexpected third stream — worth extracting
$ ffprobe -v quiet -show_format -show_streams challenge.mp4 | grep -i "TAG\|title\|comment\|artist\|encoder"
TAG:title : nothing_here
TAG:comment : picoCTF{m3tadata_1s_0ften_0verl00ked}
TAG:encoder : Lavf58.76.100 # Also check for unusual stream counts or codec names
$ ffprobe -v quiet -show_streams challenge.mp4 | grep codec_name
codec_name=h264
codec_name=aac
codec_name=mjpeg # ← unexpected third stream — worth extracting
# Extract audio as WAV for Audacity analysis
$ ffmpeg -i challenge.mp4 audio.wav # If the audio is in an unusual format, normalize it first
$ ffmpeg -i challenge.mp4 -ar 44100 -ac 1 audio_mono.wav # For DTMF decoding without Audacity:
$ multimon-ng -t wav -a DTMF audio.wav # If the audio track sounds wrong — try slowing it down
$ ffmpeg -i audio.wav -filter:a "atempo=0.5" audio_half_speed.wav
# Extract audio as WAV for Audacity analysis
$ ffmpeg -i challenge.mp4 audio.wav # If the audio is in an unusual format, normalize it first
$ ffmpeg -i challenge.mp4 -ar 44100 -ac 1 audio_mono.wav # For DTMF decoding without Audacity:
$ multimon-ng -t wav -a DTMF audio.wav # If the audio track sounds wrong — try slowing it down
$ ffmpeg -i audio.wav -filter:a "atempo=0.5" audio_half_speed.wav
# Extract audio as WAV for Audacity analysis
$ ffmpeg -i challenge.mp4 audio.wav # If the audio is in an unusual format, normalize it first
$ ffmpeg -i challenge.mp4 -ar 44100 -ac 1 audio_mono.wav # For DTMF decoding without Audacity:
$ multimon-ng -t wav -a DTMF audio.wav # If the audio track sounds wrong — try slowing it down
$ ffmpeg -i audio.wav -filter:a "atempo=0.5" audio_half_speed.wav
# Video too fast — slow it to half speed
$ ffmpeg -i challenge.mp4 -filter:v "setpts=2.0*PTS" -filter:a "atempo=0.5" slowed.mp4 # Audio too fast (chipmunk voice) — slow audio only, preserve video
$ ffmpeg -i challenge.mp4 -filter:a "atempo=0.5" -c:v copy audio_fixed.mp4 # For extreme speed changes (< 0.5x or > 2.0x), chain atempo filters:
$ ffmpeg -i challenge.mp4 -filter:a "atempo=0.5,atempo=0.5" quarter_speed.wav
# atempo only accepts 0.5–2.0 per filter; chain for larger changes # Pitch-shifted audio (content shifted up or down by semitones):
$ ffmpeg -i audio.wav -filter:a "asetrate=44100*0.5,aresample=44100" pitch_down.wav
# Video too fast — slow it to half speed
$ ffmpeg -i challenge.mp4 -filter:v "setpts=2.0*PTS" -filter:a "atempo=0.5" slowed.mp4 # Audio too fast (chipmunk voice) — slow audio only, preserve video
$ ffmpeg -i challenge.mp4 -filter:a "atempo=0.5" -c:v copy audio_fixed.mp4 # For extreme speed changes (< 0.5x or > 2.0x), chain atempo filters:
$ ffmpeg -i challenge.mp4 -filter:a "atempo=0.5,atempo=0.5" quarter_speed.wav
# atempo only accepts 0.5–2.0 per filter; chain for larger changes # Pitch-shifted audio (content shifted up or down by semitones):
$ ffmpeg -i audio.wav -filter:a "asetrate=44100*0.5,aresample=44100" pitch_down.wav
# Video too fast — slow it to half speed
$ ffmpeg -i challenge.mp4 -filter:v "setpts=2.0*PTS" -filter:a "atempo=0.5" slowed.mp4 # Audio too fast (chipmunk voice) — slow audio only, preserve video
$ ffmpeg -i challenge.mp4 -filter:a "atempo=0.5" -c:v copy audio_fixed.mp4 # For extreme speed changes (< 0.5x or > 2.0x), chain atempo filters:
$ ffmpeg -i challenge.mp4 -filter:a "atempo=0.5,atempo=0.5" quarter_speed.wav
# atempo only accepts 0.5–2.0 per filter; chain for larger changes # Pitch-shifted audio (content shifted up or down by semitones):
$ ffmpeg -i audio.wav -filter:a "asetrate=44100*0.5,aresample=44100" pitch_down.wav
# Try remuxing first (fast, no re-encoding)
$ ffmpeg -i broken.mp4 -c copy fixed.mp4 # If that fails, try full re-encode
$ ffmpeg -i broken.mp4 fixed.mp4 # If ffmpeg can't identify the format, try forcing one:
$ ffmpeg -f mp4 -i broken.bin fixed.mp4
$ ffmpeg -f avi -i broken.bin fixed.avi # Check the actual file header to identify the real format:
$ xxd broken.mp4 | head -3
# ftyp = MP4, RIFF = AVI, 1a45dfa3 = MKV/WebM
# If the header doesn't match the extension, rename and re-try
# Try remuxing first (fast, no re-encoding)
$ ffmpeg -i broken.mp4 -c copy fixed.mp4 # If that fails, try full re-encode
$ ffmpeg -i broken.mp4 fixed.mp4 # If ffmpeg can't identify the format, try forcing one:
$ ffmpeg -f mp4 -i broken.bin fixed.mp4
$ ffmpeg -f avi -i broken.bin fixed.avi # Check the actual file header to identify the real format:
$ xxd broken.mp4 | head -3
# ftyp = MP4, RIFF = AVI, 1a45dfa3 = MKV/WebM
# If the header doesn't match the extension, rename and re-try
# Try remuxing first (fast, no re-encoding)
$ ffmpeg -i broken.mp4 -c copy fixed.mp4 # If that fails, try full re-encode
$ ffmpeg -i broken.mp4 fixed.mp4 # If ffmpeg can't identify the format, try forcing one:
$ ffmpeg -f mp4 -i broken.bin fixed.mp4
$ ffmpeg -f avi -i broken.bin fixed.avi # Check the actual file header to identify the real format:
$ xxd broken.mp4 | head -3
# ftyp = MP4, RIFF = AVI, 1a45dfa3 = MKV/WebM
# If the header doesn't match the extension, rename and re-try
$ ffprobe -v quiet -show_streams challenge.mkv | grep -E "codec_name|index|codec_type"
index=0
codec_type=video
codec_name=h264
index=1
codec_type=audio
codec_name=aac
index=2
codec_type=subtitle # ← subtitle stream — extract and read it
codec_name=subrip
index=3
codec_type=data # ← data stream — extract as binary
codec_name=bin_data # Extract specific streams by index
$ ffmpeg -i challenge.mkv -map 0:2 subtitles.srt
$ ffmpeg -i challenge.mkv -map 0:3 -c copy hidden_data.bin # Then: strings hidden_data.bin | grep -i flag
# Or: file hidden_data.bin (may reveal a zip, image, etc.)
$ ffprobe -v quiet -show_streams challenge.mkv | grep -E "codec_name|index|codec_type"
index=0
codec_type=video
codec_name=h264
index=1
codec_type=audio
codec_name=aac
index=2
codec_type=subtitle # ← subtitle stream — extract and read it
codec_name=subrip
index=3
codec_type=data # ← data stream — extract as binary
codec_name=bin_data # Extract specific streams by index
$ ffmpeg -i challenge.mkv -map 0:2 subtitles.srt
$ ffmpeg -i challenge.mkv -map 0:3 -c copy hidden_data.bin # Then: strings hidden_data.bin | grep -i flag
# Or: file hidden_data.bin (may reveal a zip, image, etc.)
$ ffprobe -v quiet -show_streams challenge.mkv | grep -E "codec_name|index|codec_type"
index=0
codec_type=video
codec_name=h264
index=1
codec_type=audio
codec_name=aac
index=2
codec_type=subtitle # ← subtitle stream — extract and read it
codec_name=subrip
index=3
codec_type=data # ← data stream — extract as binary
codec_name=bin_data # Extract specific streams by index
$ ffmpeg -i challenge.mkv -map 0:2 subtitles.srt
$ ffmpeg -i challenge.mkv -map 0:3 -c copy hidden_data.bin # Then: strings hidden_data.bin | grep -i flag
# Or: file hidden_data.bin (may reveal a zip, image, etc.)
# Extract frames in different pixel formats for channel analysis
$ ffmpeg -i challenge.mp4 -pix_fmt gray frames_gray/frame_%05d.png # luminance only
$ ffmpeg -i challenge.mp4 -pix_fmt yuv420p frames_yuv/frame_%05d.yuv # raw YUV # For a single suspicious frame, extract and analyze with stegsolve:
$ ffmpeg -ss 00:01:23 -i challenge.mp4 -vframes 1 suspect_frame.png
$ java -jar stegsolve.jar # open suspect_frame.png and cycle through bit planes # LSB extraction from extracted frames:
$ zsteg suspect_frame.png # checks multiple LSB patterns automatically
# Extract frames in different pixel formats for channel analysis
$ ffmpeg -i challenge.mp4 -pix_fmt gray frames_gray/frame_%05d.png # luminance only
$ ffmpeg -i challenge.mp4 -pix_fmt yuv420p frames_yuv/frame_%05d.yuv # raw YUV # For a single suspicious frame, extract and analyze with stegsolve:
$ ffmpeg -ss 00:01:23 -i challenge.mp4 -vframes 1 suspect_frame.png
$ java -jar stegsolve.jar # open suspect_frame.png and cycle through bit planes # LSB extraction from extracted frames:
$ zsteg suspect_frame.png # checks multiple LSB patterns automatically
# Extract frames in different pixel formats for channel analysis
$ ffmpeg -i challenge.mp4 -pix_fmt gray frames_gray/frame_%05d.png # luminance only
$ ffmpeg -i challenge.mp4 -pix_fmt yuv420p frames_yuv/frame_%05d.yuv # raw YUV # For a single suspicious frame, extract and analyze with stegsolve:
$ ffmpeg -ss 00:01:23 -i challenge.mp4 -vframes 1 suspect_frame.png
$ java -jar stegsolve.jar # open suspect_frame.png and cycle through bit planes # LSB extraction from extracted frames:
$ zsteg suspect_frame.png # checks multiple LSB patterns automatically
# Step 1: Identify and inspect — read ALL of this output
$ ffmpeg -i challenge.mp4 2>&1 | tee ffmpeg_info.txt
$ ffprobe -v quiet -show_format -show_streams challenge.mp4 | tee ffprobe_info.txt
$ grep -i "TAG\|comment\|title\|artist\|flag\|ctf" ffprobe_info.txt # Step 2: Check stream count — more than 2 streams = suspicious
$ ffprobe -v quiet -show_streams challenge.mp4 | grep -c "^index=" # Step 3: Extract everything in parallel
$ mkdir frames
$ ffmpeg -i challenge.mp4 frames/frame_%05d.png &
$ ffmpeg -i challenge.mp4 audio.wav &
wait # Step 4: Find anomalous frames by size
$ ls -la frames/ | sort -k5 -rn | head -10 # Step 5: Check audio in Audacity
# Spectrogram view first — always # Step 6: If video looks corrupted
$ ffmpeg -i broken.mp4 -c copy fixed.mp4 # Step 7: If still nothing, check raw bytes of anomalous frames
$ binwalk frames/frame_02847.png # may contain embedded files
# Step 1: Identify and inspect — read ALL of this output
$ ffmpeg -i challenge.mp4 2>&1 | tee ffmpeg_info.txt
$ ffprobe -v quiet -show_format -show_streams challenge.mp4 | tee ffprobe_info.txt
$ grep -i "TAG\|comment\|title\|artist\|flag\|ctf" ffprobe_info.txt # Step 2: Check stream count — more than 2 streams = suspicious
$ ffprobe -v quiet -show_streams challenge.mp4 | grep -c "^index=" # Step 3: Extract everything in parallel
$ mkdir frames
$ ffmpeg -i challenge.mp4 frames/frame_%05d.png &
$ ffmpeg -i challenge.mp4 audio.wav &
wait # Step 4: Find anomalous frames by size
$ ls -la frames/ | sort -k5 -rn | head -10 # Step 5: Check audio in Audacity
# Spectrogram view first — always # Step 6: If video looks corrupted
$ ffmpeg -i broken.mp4 -c copy fixed.mp4 # Step 7: If still nothing, check raw bytes of anomalous frames
$ binwalk frames/frame_02847.png # may contain embedded files
# Step 1: Identify and inspect — read ALL of this output
$ ffmpeg -i challenge.mp4 2>&1 | tee ffmpeg_info.txt
$ ffprobe -v quiet -show_format -show_streams challenge.mp4 | tee ffprobe_info.txt
$ grep -i "TAG\|comment\|title\|artist\|flag\|ctf" ffprobe_info.txt # Step 2: Check stream count — more than 2 streams = suspicious
$ ffprobe -v quiet -show_streams challenge.mp4 | grep -c "^index=" # Step 3: Extract everything in parallel
$ mkdir frames
$ ffmpeg -i challenge.mp4 frames/frame_%05d.png &
$ ffmpeg -i challenge.mp4 audio.wav &
wait # Step 4: Find anomalous frames by size
$ ls -la frames/ | sort -k5 -rn | head -10 # Step 5: Check audio in Audacity
# Spectrogram view first — always # Step 6: If video looks corrupted
$ ffmpeg -i broken.mp4 -c copy fixed.mp4 # Step 7: If still nothing, check raw bytes of anomalous frames
$ binwalk frames/frame_02847.png # may contain embedded files