ctf-stego
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCTF Steganography
CTF隐写术
Quick Start — Try These First
快速开始——先尝试这些操作
bash
undefinedbash
undefinedBasic analysis
Basic analysis
file image.png
exiftool image.png # EXIF metadata (flags hide here!)
strings image.png | grep -iE "flag|ctf"
binwalk image.png # Embedded files
xxd image.png | tail # Data appended after EOF
file image.png
exiftool image.png # EXIF metadata (flags hide here!)
strings image.png | grep -iE "flag|ctf"
binwalk image.png # Embedded files
xxd image.png | tail # Data appended after EOF
Steganography tools
Steganography tools
steghide extract -sf image.jpg # JPEG stego (tries empty password)
steghide extract -sf image.jpg -p "" # Explicit empty password
zsteg image.png # PNG/BMP LSB analysis
stegsolve # Visual bit-plane analysis (GUI)
undefinedsteghide extract -sf image.jpg # JPEG stego (tries empty password)
steghide extract -sf image.jpg -p "" # Explicit empty password
zsteg image.png # PNG/BMP LSB analysis
stegsolve # Visual bit-plane analysis (GUI)
undefinedImage Steganography
图片隐写术
LSB (Least Significant Bit)
LSB(最低有效位)
The most common image stego technique. Data hidden in the least significant bits of pixel values.
python
from PIL import Image
img = Image.open('image.png')
pixels = list(img.getdata())这是最常见的图片隐写技术,数据隐藏在像素值的最低有效位中。
python
from PIL import Image
img = Image.open('image.png')
pixels = list(img.getdata())Extract LSB from each channel
Extract LSB from each channel
bits = ''
for pixel in pixels:
for channel in pixel[:3]: # R, G, B
bits += str(channel & 1)
bits = ''
for pixel in pixels:
for channel in pixel[:3]: # R, G, B
bits += str(channel & 1)
Convert bits to bytes
Convert bits to bytes
flag = bytes(int(bits[i:i+8], 2) for i in range(0, len(bits), 8))
print(flag)
**Tools:**
- `zsteg` — Automated LSB analysis for PNG/BMP (try `zsteg -a image.png`)
- `stegsolve` — Visual analysis, toggle bit planes
- `Stegano` — Python library: `pip install stegano`flag = bytes(int(bits[i:i+8], 2) for i in range(0, len(bits), 8))
print(flag)
**工具:**
- `zsteg` — 针对PNG/BMP的自动化LSB分析(可尝试`zsteg -a image.png`)
- `stegsolve` — 可视化位平面分析(GUI工具)
- `Stegano` — Python库:`pip install stegano`Pixel Value Encoding
像素值编码
python
undefinedpython
undefinedValues ARE the data (not hidden in LSB)
Values ARE the data (not hidden in LSB)
from PIL import Image
img = Image.open('image.png')
pixels = list(img.getdata())
from PIL import Image
img = Image.open('image.png')
pixels = list(img.getdata())
Each pixel R value is an ASCII char
Each pixel R value is an ASCII char
flag = ''.join(chr(p[0]) for p in pixels if 32 <= p[0] < 127)
flag = ''.join(chr(p[0]) for p in pixels if 32 <= p[0] < 127)
Or pixel coordinates encode data
Or pixel coordinates encode data
Or specific color pixels spell out a message
Or specific color pixels spell out a message
undefinedundefinedImage Format Tricks
图片格式技巧
PNG chunks:
bash
pngcheck -v image.png # Validate and list chunks
python3 -c "
import struct
with open('image.png', 'rb') as f:
data = f.read()PNG块:
bash
pngcheck -v image.png # Validate and list chunks
python3 -c "
import struct
with open('image.png', 'rb') as f:
data = f.read()Look for custom chunks (tEXt, zTXt, iTXt)
Look for custom chunks (tEXt, zTXt, iTXt)
idx = data.find(b'tEXt')
if idx > 0:
print(data[idx:idx+100])
"
**JPEG markers:**
```bashidx = data.find(b'tEXt')
if idx > 0:
print(data[idx:idx+100])
"
**JPEG标记:**
```bashData after JPEG EOF marker (FF D9)
Data after JPEG EOF marker (FF D9)
python3 -c "
with open('image.jpg', 'rb') as f:
data = f.read()
eof = data.find(b'\xff\xd9')
if eof > 0 and eof + 2 < len(data):
print(f'Data after EOF: {data[eof+2:eof+102]!r}')
"
**BMP:**
```bashpython3 -c "
with open('image.jpg', 'rb') as f:
data = f.read()
eof = data.find(b'\xff\xd9')
if eof > 0 and eof + 2 < len(data):
print(f'Data after EOF: {data[eof+2:eof+102]!r}')
"
**BMP:**
```bashBMP has a data offset field — gap between header and pixel data can hide data
BMP has a data offset field — gap between header and pixel data can hide data
xxd image.bmp | head -5
**GIF:**
```bashxxd image.bmp | head -5
**GIF:**
```bashGIF frames may contain hidden data
GIF frames may contain hidden data
ffmpeg -i image.gif frame_%03d.png # Extract all frames
identify -verbose image.gif # Frame details
undefinedffmpeg -i image.gif frame_%03d.png # Extract all frames
identify -verbose image.gif # Frame details
undefinedImage Dimension Tricks
图片尺寸技巧
Wrong dimensions in header:
python
undefined头部中的错误尺寸:
python
undefinedPNG: Fix height to reveal hidden rows
PNG: Fix height to reveal hidden rows
import struct, zlib
with open('image.png', 'rb') as f:
data = bytearray(f.read())
import struct, zlib
with open('image.png', 'rb') as f:
data = bytearray(f.read())
PNG IHDR chunk starts at offset 16 (width at 16, height at 20)
PNG IHDR chunk starts at offset 16 (width at 16, height at 20)
Try increasing height
Try increasing height
struct.pack_into('>I', data, 20, 1000) # Set height to 1000
with open('fixed.png', 'wb') as f:
# Recalculate IHDR CRC
ihdr_data = data[12:29]
crc = zlib.crc32(ihdr_data) & 0xffffffff
struct.pack_into('>I', data, 29, crc)
f.write(data)
undefinedstruct.pack_into('>I', data, 20, 1000) # Set height to 1000
with open('fixed.png', 'wb') as f:
# Recalculate IHDR CRC
ihdr_data = data[12:29]
crc = zlib.crc32(ihdr_data) & 0xffffffff
struct.pack_into('>I', data, 29, crc)
f.write(data)
undefinedSteghide (JPEG/WAV/BMP/AU)
Steghide(支持JPEG/WAV/BMP/AU)
bash
steghide extract -sf file.jpg -p "password"
steghide info file.jpg # Check if data is embeddedbash
steghide extract -sf file.jpg -p "password"
steghide info file.jpg # Check if data is embeddedBrute force password
Brute force password
stegcracker file.jpg wordlist.txt
stegcracker file.jpg wordlist.txt
Or use stegseek (much faster)
Or use stegseek (much faster)
stegseek file.jpg wordlist.txt
undefinedstegseek file.jpg wordlist.txt
undefinedVisual Steganography
视觉隐写术
- Flags as tiny/low-contrast text in images
- Black text on dark background, white on light
- Check ALL corners and edges at full resolution
- Profile pictures and avatars are common hiding spots
- Zoom in on what looks like solid color areas
- 标志作为微小/低对比度文本隐藏在图片中
- 深色背景上的黑色文本,浅色背景上的白色文本
- 以全屏分辨率检查所有角落和边缘
- 头像和个人资料图片是常见的隐藏位置
- 放大看似纯色的区域
Audio Steganography
音频隐写术
Spectrogram Analysis
频谱图分析
bash
undefinedbash
undefinedGenerate spectrogram image
Generate spectrogram image
sox audio.wav -n spectrogram -o spectrogram.png
sox audio.wav -n spectrogram -o spectrogram.png
Or use Audacity: View → Spectrogram
Or use Audacity: View → Spectrogram
Look for text/images drawn in frequency domain
Look for text/images drawn in frequency domain
undefinedundefinedSSTV (Slow-Scan Television)
SSTV(慢扫描电视)
bash
undefinedbash
undefinedDecode SSTV signal from audio
Decode SSTV signal from audio
qsstv # GUI decoder
qsstv # GUI decoder
Or sstv Python package
Or sstv Python package
pip install sstv
sstv -d audio.wav -o output.png
undefinedpip install sstv
sstv -d audio.wav -o output.png
undefinedDTMF Tones
DTMF音调
bash
undefinedbash
undefinedPhone keypad tones
Phone keypad tones
multimon-ng -t wav -a DTMF audio.wav
multimon-ng -t wav -a DTMF audio.wav
Or via sox + multimon-ng:
Or via sox + multimon-ng:
sox audio.wav -t raw -r 22050 -e signed-integer -b 16 -c 1 - | multimon-ng -t raw -a DTMF -
undefinedsox audio.wav -t raw -r 22050 -e signed-integer -b 16 -c 1 - | multimon-ng -t raw -a DTMF -
undefinedAudio LSB
音频LSB
python
import wave
import struct
wav = wave.open('audio.wav', 'rb')
frames = wav.readframes(wav.getnframes())
samples = struct.unpack(f'<{len(frames)//2}h', frames)python
import wave
import struct
wav = wave.open('audio.wav', 'rb')
frames = wav.readframes(wav.getnframes())
samples = struct.unpack(f'<{len(frames)//2}h', frames)Extract LSB from samples
Extract LSB from samples
bits = ''.join(str(s & 1) for s in samples)
data = bytes(int(bits[i:i+8], 2) for i in range(0, len(bits), 8))
print(data[:100])
undefinedbits = ''.join(str(s & 1) for s in samples)
data = bytes(int(bits[i:i+8], 2) for i in range(0, len(bits), 8))
print(data[:100])
undefinedMorse Code
摩尔斯电码
bash
undefinedbash
undefinedVisual: look at waveform for long/short patterns
Visual: look at waveform for long/short patterns
Audio: listen for dots and dashes
Audio: listen for dots and dashes
Automated: use online morse decoder or:
Automated: use online morse decoder or:
pip install morse-audio-decoder
undefinedpip install morse-audio-decoder
undefinedText/Data Steganography
文本/数据隐写术
Whitespace Stego
空白字符隐写
bash
undefinedbash
undefinedZero-width characters in text
Zero-width characters in text
python3 -c "
with open('text.txt', 'rb') as f:
data = f.read()
python3 -c "
with open('text.txt', 'rb') as f:
data = f.read()
Zero-width space: U+200B, Zero-width joiner: U+200D
Zero-width space: U+200B, Zero-width joiner: U+200D
for b in data:
if b in [0xe2]: # Start of multi-byte UTF-8
print(f'Found zero-width char at position')
"
for b in data:
if b in [0xe2]: # Start of multi-byte UTF-8
print(f'Found zero-width char at position')
"
snow tool for whitespace stego
snow tool for whitespace stego
snow -C -p "password" stego.txt
undefinedsnow -C -p "password" stego.txt
undefinedUnicode Stego
Unicode隐写
- Homoglyph substitution (Cyrillic а vs Latin a)
- Invisible Unicode characters between visible text
- Variation selectors and combining characters
- 同形字符替换(西里尔字母а vs 拉丁字母a)
- 可见文本之间的不可见Unicode字符
- 变体选择符和组合字符
File Concatenation / Polyglots
文件拼接/多格式文件
bash
undefinedbash
undefinedMultiple files concatenated
Multiple files concatenated
binwalk suspicious_file # Find embedded files
foremost suspicious_file # Carve out files
binwalk suspicious_file # Find embedded files
foremost suspicious_file # Carve out files
ZIP at end of image
ZIP at end of image
unzip image.png # Works if ZIP appended after image data
unzip image.png # Works if ZIP appended after image data
PDF + ZIP polyglot
PDF + ZIP polyglot
File is valid as both PDF and ZIP
File is valid as both PDF and ZIP
undefinedundefinedNetwork Steganography
网络隐写术
PCAP Hidden Data
PCAP隐藏数据
- DNS queries encoding data in subdomain labels
- ICMP payloads carrying hidden messages
- TCP sequence numbers encoding data
- HTTP headers with encoded data
- TLS certificate fields
- DNS查询在子域名标签中编码数据
- ICMP载荷携带隐藏消息
- TCP序列号编码数据
- HTTP头中包含编码数据
- TLS证书字段
DNS Exfiltration
DNS数据泄露
bash
tshark -r capture.pcap -Y "dns.qry.name" -T fields -e dns.qry.name | \
sed 's/\.example\.com//' | tr -d '\n' | base64 -dbash
tshark -r capture.pcap -Y "dns.qry.name" -T fields -e dns.qry.name | \
sed 's/\.example\.com//' | tr -d '\n' | base64 -dCommon Patterns
常见模式
| Clue | Technique |
|---|---|
| "Look closer" / "More than meets the eye" | LSB or visual stego |
| Image looks normal but file is huge | Embedded/appended data |
| Audio with static/noise sections | Spectrogram or SSTV |
| "Password protected" | Steghide with password |
| PNG with wrong colors or glitches | Bit plane analysis |
| Text file with trailing whitespace | Whitespace stego |
| Challenge says "nothing to see here" | Definitely stego |
| 线索 | 技术手段 |
|---|---|
| “仔细看” / “不止表面所见” | LSB或视觉隐写 |
| 图片看起来正常但文件体积很大 | 嵌入/附加的数据 |
| 包含静态/噪音段的音频 | 频谱图或SSTV |
| “需要密码” | 带密码的Steghide |
| 颜色异常或有故障的PNG | 位平面分析 |
| 带有尾随空白的文本文件 | 空白字符隐写 |
| 挑战提示“这里没什么可看的” | 肯定存在隐写 |