news 2026/10/17 5:36:43

CTF杂项实战:从ZIP伪加密到LSB隐写的完整拆解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CTF杂项实战:从ZIP伪加密到LSB隐写的完整拆解

刷杂项题最怕的不是没思路,而是每个知识点都“好像见过”,一到手就卡住。这道名叫“赛博朋克”的题目,我前前后后折腾了将近二十分钟,最后发现自己其实栽在最基础的两件事上:ZIP文件的文件头协议没吃透,LSB隐写只听说过没用熟。题目本身不算刁钻,但知识点覆盖得非常典型——压缩包伪装、伪加密、图片低位隐写,全都在MISC方向里高频出现。这篇就把完整拆解思路和实操过程倒出来,给还在刷题入门的朋友一个可以直接照做的参考。

建议手边准备这几个东西:一个十六进制编辑器,一个支持无损格式的图片查看器,一个能跑Python的环境。工具不在多,关键是每一步都知道自己在看什么、改什么。

1. 拿到赛博朋克题,先别急着解压

很多人在杂项题上效率低,不是因为工具不会用,而是拿到文件就默认它是什么就是什么。题目给的资源叫“赛博朋克”,后缀可能是ZIP,也可能是图片,甚至可能是个没有后缀的二进制块。第一步永远不是解压,而是先确认这个文件的真实身份。

这道题的实际场景是这样的:我下载下来一个名为“赛博朋克.zip”的文件,大小只有几十KB,第一反应是直接双击解压。但WinRAR弹了个错误,说文件头损坏,需要密码。这个“需要密码”的提示其实已经很不寻常了——正常压缩包即使有密码,文件头也应该能被正确解析,只是解压的时候才会要求输入密码。一个连文件头都被判定为“损坏”的包,大概率是被动过手脚。

这时候我的习惯是分四步走:

  1. 用file命令看一眼真实文件类型;
  2. 用binwalk扫描文件里是否存在内嵌的其他文件;
  3. 用十六进制工具打开,直接看文件头签名;
  4. 根据签名判断是用错扩展名、伪加密、还是整包被塞进了其他文件里。

这个顺序看起来很基础,但能过滤掉九成以上的“假压缩包”问题。赛博朋克这道题就是典型例子:file显示它是一个ZIP压缩包,binwalk也扫出了ZIP的签名,但解压程序却死死咬住“需要密码”。问题不在文件是不是ZIP,而在ZIP内部的结构被动了手脚。

1.1 工具清单与实际分工

我常用的组合并不复杂,都是公开、常见的工具:

工具用途说明
file识别文件真实类型系统自带,先做一次粗判断
binwalk扫描文件内的隐藏数据能快速发现ZIP、PNG等签名
010 Editor / WinHex查看和修改二进制内容关键字段的定位和修复都靠它
7-Zip解压测试、检验修复结果对ZIP结构的报错信息比WinRAR更直白
StegSolve查看位平面、提取LSB隐写杂项题必备的Java图形工具
Python + PIL自定义提取LSB处理StegSolve无法覆盖的细节情况

工具不在多,StegSolve和010 Editor是这题的核心。前者负责后半段的隐写提取,后者负责前半段的ZIP结构修复。两者的切换逻辑非常清晰:先二进制修包,再像素提取。

1.2 为什么不能盲信扩展名

扩展名是操作系统给人看的标签,不是文件本身。一个文件叫“赛博朋克.zip”,它完全可以是PNG图片、PDF文档,甚至是纯文本。判断文件真实格式最可靠的依据是文件头签名,也就是一组特定的十六进制字节。

常见格式的文件头签名我都记了一些,方便快速对照:

  • ZIP:50 4B 03 04(也可能是50 4B 05 06这种空压缩包);
  • PNG:89 50 4E 47 0D 0A 1A 0A;
  • JPEG:FF D8 FF;
  • GIF:47 49 46 38;
  • PDF:25 50 44 46;
  • ELF可执行文件:7F 45 4C 46。

这些签名只要瞄一眼就能知道文件到底是什么。赛博朋克这题的文件头是50 4B 03 04,所以确认是ZIP,但ZIP的“文件头协议”并不仅仅指开头这两个字节,后面一大段结构都有讲究。下一节重点讲这个。

2. ZIP文件头协议:压缩包最容易被忽略的细节

很多人对ZIP的理解停留在“能压缩”“能加密”“能解压”,但一旦碰到“压缩包损坏”或者“伪加密”,就完全摸不着方向。ZIP文件头协议本质是一套二进制的元数据格式,它描述了文件在压缩包内如何存储、是否加密、压缩算法是什么、文件名多长、数据在哪个偏移位置。CTF题目最喜欢动的手脚,就在这个结构里。

2.1 ZIP的局部文件头长什么样

ZIP文件由三大部分组成:若干个局部文件头(Local File Header)、中央目录区(Central Directory)、以及目录结束标记(End of Central Directory Record)。每个被压缩的文件,都会先有一个局部文件头,再跟一段压缩数据。

局部文件头的关键字段是这样的:

偏移量长度内容典型值
0x004字节文件头签名50 4B 03 04
0x042字节解压所需版本14 00
0x062字节通用位标志00 00或00 01
0x082字节压缩方法00 00存储、08 00Deflate
0x0A2字节文件修改时间无固定值
0x0C2字节文件修改日期无固定值
0x0E4字节CRC32校验值无固定值
0x124字节压缩后数据大小无固定值
0x164字节未压缩数据大小无固定值
0x1A2字节文件名长度无固定值
0x1C2字节扩展字段长度无固定值
0x1EN字节文件名ASCII字符串

这里最需要留意的就是偏移量0x06的通用位标志。它的Bit0如果为1,表示这个文件是加密的;如果为0,表示不加密。CTF里的伪加密,通常就是把这个bit改成1,让解压工具认为文件需要密码,但实际上压缩数据根本没有被加密。换句话说,这就像一个假锁,看着锁上了,实际上门根本没关。

2.2 伪加密:一个bit如何拦住所有人

用十六进制工具打开正常的ZIP包,局部文件头第7和第8个字节(偏移0x06和0x07)通常是00 00。伪加密时,这里会变成01 00,也就是第一个字节最低位被置为1。

但这里有一个非常坑的点:ZIP文件里,同一个文件的加密标志并不只出现在局部文件头,还出现在中央目录区(Central Directory File Header,签名50 4B 01 02)中。中央目录区内同样有一个通用位标志字段。有些题目只改局部文件头,有些只改中央目录,有些两个都改。如果只修复其中一个,另一个区域的标志没有清除,解压工具依然可能报需要密码。

判断到底是不是伪加密,有一个非常朴素的方法:把两个区域的加密标志位都改成0,然后用解压工具测试。如果修改之后直接解压成功,说明原本就是伪加密;如果提示密码错误或者数据出错,说明是真加密,那就要考虑其他思路了。

赛博朋克这道题中,局部文件头的0x06位置显示01 00,中央目录区中的对应标志也是01 00。我先把这两个位置都记录下原始字节,然后逐一改成00 00,再用7-Zip测试。修改后解压非常顺畅,没有要求输入密码,这就确认了是伪加密。

2.3 修复伪加密的具体操作

用010 Editor打开ZIP文件后,定位到局部文件头签名50 4B 03 04之后,直接看第7个字节。也可以搜索50 4B 01 02定位中央目录区,检查同样位置的字段。

以赛博朋克这题为例,我搜到的是:

  • 局部文件头:签名后第7字节为01;
  • 中央目录区:签名后第7字节也为01。

在010 Editor里逐个改成00,保存后退出。再用7-Zip测试时,已经没有任何密码提示。

修改时我习惯先复制一份原始文件,避免改坏后没有退路。伪加密修复只修改标志位一个字节,理论上风险很低,但谁也不能保证文件里还有其他陷阱。

2.4 其他常见的ZIP文件头伪装

除了伪加密,ZIP还经常被改扩展名、改签名、甚至把文件头整个抠掉。比如一个压缩包被改名为PNG图片后,文件头还是50 4B 03 04,此时用图片查看器会直接报错。反过来,有些题目会把一个ZIP文件头50 4B 03 04篡改成50 4B 03 05之类的无效值,让压缩包“文件头损坏”。

处理这类题目的通用步骤是:用binwalk扫描原始文件,看它能否识别出内嵌的ZIP签名。如果能,直接提取出来;如果不能,手动搜索50 4B 03 04或者50 4B 01 02,在十六进制工具中定位,修正错误的字节。

3. LSB隐写:藏在图片最低位里的暗道

修复完ZIP之后,解压出来一张PNG图片,图片本身看起来就像一张赛博朋克风格的城市夜景图。到这里很多人以为结束了,但杂项题不会把flag直接放在压缩包里。这张图片大概率还藏着一层隐写。常见的图片隐写包括LSB隐写、EXIF信息、文件尾附加数据、颜色通道异常,而赛博朋克这道题考的是LSB。

3.1 为什么PNG图片能藏数据

PNG是一种无损格式,图片的每个像素颜色由RGB三个分量组成,每个分量通常占8个bit,取值范围0到255。人的眼睛对颜色的细微变化非常不敏感,尤其是对最低位的改变。一个像素的红色分量从240变成241,肉眼几乎看不出任何区别。

LSB隐写利用的就是这种感知盲区。它把秘密信息的二进制数据,逐bit拆开,替换掉图片像素分量中的最低位。比如原始像素的红色分量是240,二进制是11110000,如果把最低位改成1,就变成11110001,也就是241。多个像素的冗余空间足以塞下一段完整的flag。

用一个生活类比来理解:这就像在一面白色墙壁上,用几乎透明的颜料写了一段文字。正常光线下去看,墙壁还是白的;但只要换一个特定角度的光,文字就浮现出来了。LSB隐写就是这种“特定角度”的数字实现。

3.2 用StegSolve逐层检查位平面

StegSolve是一个专门用来做图片隐写分析的小工具,打开图片后,进入Analyse菜单,选择Data Extract。界面里可以分别勾选Red、Green、Blue三个通道的位平面(Bit Plane),从0到7共8个层级。

正常情况下,一个普通图片的0号位平面看起来是均匀的彩色噪声,没有任何几何图案或文字轮廓。如果某个通道的最低几位呈现出明显的规律性,或者能隐约看出文字的形状,说明这里被塞了数据。StegSolve左下角的预览窗口会实时显示提取结果。

赛博朋克这张图在Bit Order选择“LSB First”、勾选RGB三个通道的0号位平面之后,左下角直接出现了一段可读的文字。这个现象非常典型:隐藏信息未经任何二次加密,直接以ASCII明文字节流写入最低位,所以用位平面视图就能轻松看到。

3.3 自己写脚本提取LSB

StegSolve能解决的问题只是“肉眼看到”,如果隐藏信息不是按常规顺序写入,或者数据被拆分在多个通道的不同bit位,就需要用脚本精确提取。我自己写Python时,通常用PIL库逐像素读取RGB值,把想要的分量最低bit拼接起来,再按8bit一组还原成字节。

from PIL import Image img = Image.open("cyberpunk.png") pixels = img.load() width, height = img.size bits = [] for y in range(height): for x in range(width): r, g, b = pixels[x, y][:3] # 提取三个通道的最低bit,按RGB顺序排列 bits.append(r & 1) bits.append(g & 1) bits.append(b & 1) # 每8个bit转成一个字节 data = bytearray() for i in range(0, len(bits) - 7, 8): byte = 0 for j in range(8): byte = (byte << 1) | bits[i + j] data.append(byte) # 过滤可打印字符并输出 print(data.decode("latin-1", errors="ignore"))

这个脚本的提取顺序是“逐像素、每像素内按RGB、每个bit按高位在前”。实际运行后,我在输出里直接看到了形如flag{...}的字符串。

需要特别注意的是,不同题目写入LSB时的顺序可能不一样。有些按RGB顺序,有些按BGR;有些低位在前,有些高位在前;有些按行优先,有些按列优先。因此,一个脚本提不到正常文字不代表没有隐写,可能只是顺序不对。这种情况下,我建议把三个变量做成可配置项,逐个尝试。

4. 完整实操:从赛博朋克题目到flag

前面两节把原理讲清楚了,这一节把整道题的完整流程走一遍。所有关键步骤和细节都会写出来,方便直接对照操作。

4.1 第一步:识别文件身份

拿到赛博朋克.zip后,我先在终端里执行:

file 赛博朋克.zip

输出是:

赛博朋克.zip: Zip archive data, at least v2.0 to extract

确认是ZIP包。然后执行binwalk扫描:

binwalk 赛博朋克.zip

扫描结果显示在偏移0的位置发现了ZIP签名50 4B 03 04,没有发现内嵌的其他文件。这说明文件就是一个独立的ZIP压缩包,没有用图片包着ZIP,也不是ZIP包着图片的复合结构。

4.2 第二步:查看ZIP文件头并定位标志位

用010 Editor打开ZIP,搜索十六进制串50 4B 03 04,很快就定位到了第一个局部文件头。在010 Editor的模板视图里,可以看到通用位标志字段的值为0x0001,Bit0是1。也就是说,ZIP的局部文件头认为自己“加密了”。

接着搜索50 4B 01 02,找到中央目录区,对应的通用位标志同样为0x0001。两个区域都带了加密标志位,这就是伪加密的典型形态。

4.3 第三步:修复伪加密字节

在010 Editor里,把局部文件头偏移0x09和中央目录区偏移0x09处的01都改成00。保存之后,我用7-Zip打开压缩包,文件列表正常显示,不再提示输入密码。双击里面的图片文件,可以正常解压。

这里有一点要强调:修复磁盘上第二个01的时候,不要盲目全局替换所有01为00。ZIP内部有很多字段都可能包含01这个字节,比如压缩方法、文件名内容。只修改通用位标志字段,其他一律不动。

4.4 第四步:解压并检查内容物

用7-Zip解压后,得到一张名为cyberpunk.png的图片。先用file确认它是PNG:

cyberpunk.png: PNG image data, 800 x 600, 8-bit/color RGB, non-interlaced

图片是一张赛博朋克风格的街景图,霓虹灯、雨夜、高楼,感官上完全正常。普通看图软件打开没有任何异常。这时候就需要怀疑LSB隐写。

4.5 第五步:StegSolve提取LSB数据

打开StegSolve,加载PNG图片,进入Analyse菜单的Data Extract。在Bit Plane选项里,把Red、Green、Blue都勾选为0,Bit Order选LSB First,然后点击Preview。

左下角预览窗口出现了文字,开头的几个字符是flag{。整段文字直接显示在预览区域里。由于隐藏信息没有加密,只是逐bit嵌入,StegSolve的预览功能足以完整还原。

如果预览窗口发现文字是倒序或者乱序,可以把Bit Order改成MSB First或者调整通道顺序再试。不同工具写LSB隐写的顺序不同,StegSolve的可视化选项恰好覆盖了大部分情况。

4.6 第六步:整理Flag并提交

从StegSolve预览窗口复制出整段flag{...}内容。提交到CTF平台时,注意不要有多余的空格或换行。这道题的flag是一串很短的字符串,比如flag{cyberpunk_lsb_zip_protocol}这种格式。

实际做题时,我复制完直接粘贴到提交框,一次通过。整个流程下来,真正耗时间的不是提取操作,而是前期识别伪加密和分析图片位平面的过程。

5. 常见问题与排查技巧

刷题多了会发现,同样的知识点,换个出题方式就变了个坑。这里把做题过程中最常见的几种卡点整理出来,方便以后遇到类似问题时快速排查。

5.1 压缩包提示文件头损坏怎么办

“文件头损坏”这个提示,在CTF杂项题里的出现率非常高。可能的原因有三种:一是扩展名被改错,文件本身不是ZIP;二是ZIP签名被别人改成无效值;三是文件被截断,缺少目录结束记录。

排查顺序是:先用file和binwalk看真实类型和签名位置;再用十六进制工具搜索50 4B 03 04和50 4B 01 02;如果签名正常但解压失败,检查文件末尾是否有50 4B 05 06。缺少结束记录时,可以用7-Zip的“修复压缩文件”功能,或者手工在文件末尾补一段目录结束结构。后者比较麻烦,但好在绝大部分CTF题目只要恢复签名就能解决。

5.2 伪加密和真加密如何区分

区分方法其实很简单:把加密标志位改成0,然后尝试解压。如果文件正常解压,说明原本是伪加密;如果提示密码错误或者解压出来的文件CRC校验失败,说明是真加密,数据和文件头都做了密码处理。

还有一种辅助判断方法:看压缩数据的熵。真加密的ZIP,压缩数据部分看起来是完全均匀的随机字节;伪加密的ZIP,压缩数据部分还是正常的Deflate流,包含大量可识别的结构特征。把标志位改成0后,如果解压得到一堆乱码文件,可能是压缩方法或者数据偏移量也被改动过,不只是伪加密的问题。

5.3 LSB提取出来是乱码怎么办

用StegSolve能看到位平面有异常,但提取出来的字符串是乱码,通常是因为顺序不对。我遇到最多的情况是:

  • 通道顺序不是RGB,而是BGR;
  • bit组合顺序是LSB First,但脚本写成了MSB First;
  • 数据写入时按列优先,而不是按行优先;
  • 每个像素不只取最低位,而是取了连续两个bit或多个bit。

遇到乱码时,我会调整Python脚本的这几个参数,组合尝试。比如把通道顺序改成BGR,把字节拼接改成小端模式,或者把步长改成2。大部分情况尝试几次就能得到明文。

5.4 StegSolve异常或图片显示不了位平面

StegSolve对JPEG格式支持不好,因为JPEG是有损压缩,像素值经过离散余弦变换后并不直接等于原始RGB值,LSB位早就被破坏掉了。如果题目给的图片是JPG,建议先转换成PNG或BMP再分析。转换过程也要用无损格式,不要用截图或者再保存一次JPEG,否则会进一步破坏低位信息。

如果StegSolve打不开,可能是Java环境问题,也可以直接用zsteg这个命令行工具检测。它能自动尝试多个通道和多个位平面组合,一条命令输出所有可能的隐藏信息。

5.5 一张图多重隐写的扩展思路

有些进阶题会在同一张图里既放LSB隐写,又在文件尾部追加隐藏文件,或者修改图片的宽高比隐藏信息。赛博朋克这道题没有那么多花活,但做题时如果发现LSB提取结果只是线索,而不是最终flag,就需要回头再看一遍图片的原始二进制数据。

检查图片末尾是否有50 4B开头的ZIP签名,检查PNG的IHDR块里宽度和高度是否被修改过,检查EXIF段是否包含字符串。这些都能作为二次排查的方向。不过这些都是后话,先把ZIP文件头协议和LSB隐写本身掌握扎实,才是基础中的基础。


具体到赛博朋克这题,我最大的收获不是多认识了一个工具,而是重新理解了“伪加密”这个概念的判定逻辑。以前遇到要密码的压缩包,第一反应就是找密码、试爆破,其实只要看一眼通用位标志,可能就只是一个bit的事。同理,看到图片就想StegSolve自动提取,也需要注意提取顺序是否匹配写入顺序。这两件事都不难,但每一个“不难”的背后,都需要真正理解底层结构才能做到心里有数。以后再看到类似“文件头损坏”或“位平面有规律噪声”的提示,至少知道该往哪个方向排查了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 5:36:43

线性多智能体分布式动态事件触发一致性控制详解

在做多智能体系统一致性的时候&#xff0c;我最常被问到的问题不是“控制器怎么设计”&#xff0c;而是“通信这么频繁&#xff0c;系统还能不能撑得住”。很多刚接触这个方向的同学&#xff0c;第一版仿真跑出来的结果往往不是系统收敛不了&#xff0c;而是通信次数多到离谱。…

作者头像 李华
网站建设 2026/10/17 5:36:35

微电网能量管理中的强化学习应用:Q-Learning与SARSA(λ)的实现

做微电网能量管理这几年&#xff0c;我越发觉得强化学习这条路子值得认真对待。尤其是当你面对一个 4 节点微型电网&#xff0c;既要平衡分布式电源出力&#xff0c;又要压储能充放电的成本&#xff0c;还得盯住负载波动的时候&#xff0c;Q-Learning 和 SARSA(λ) 这两种算法能…

作者头像 李华
网站建设 2026/10/17 5:36:32

蓝牙HCI协议详解:从抓包到Python解析的BLE开发实战指南

简介&#xff1a;这是一份专为蓝牙技术初学者打造的协议原理入门资料包&#xff0c;系统梳理蓝牙协议栈各层核心机制&#xff0c;帮助读者建立从物理层到应用层的完整认知框架&#xff0c;解决概念抽象、层次混乱、实践脱节等学习痛点。资源共3个文件&#xff1a;HTML网页&…

作者头像 李华
网站建设 2026/10/17 5:36:26

编程作业怎么做?从需求拆解到调试排错的完整指南

1. 第一次作业&#xff1a;别急着写代码&#xff0c;先搞懂这几件事第一次拿到编程类作业&#xff0c;大多数人第一反应是打开编辑器直接开写。我见过太多同学栽在这一步——需求还没看明白就动手&#xff0c;跑不通就反复改&#xff0c;最后在调试上浪费的时间比写代码本身还多…

作者头像 李华
网站建设 2026/10/17 5:36:08

软件开发生命周期(SDLC):一个软件从“想法“到“退休“的一生

一句话&#xff1a;软件开发生命周期就是做一个软件要经历的固定几个阶段&#xff1a;想清楚要做什么 → 设计 → 写代码 → 测试 → 发布 → 维护。前面讲的系统设计、架构原则属于"设计"阶段&#xff1b;碰撞检测、走迷宫算法属于"写代码"阶段。这一篇把…

作者头像 李华
网站建设 2026/10/17 5:35:54

Kimi Code Desktop 评测:从终端到桌面的 AI 编程新体验

最近项目群里被一条发布消息刷屏&#xff1a;Kimi Code Desktop 发布了&#xff0c;143MB 的体积让不少人惊讶。朋友圈里做前端、做运维的都在转&#xff0c;说“AI程序员终于从终端搬进了办公室”。我第一反应是打开下载页面看了一眼&#xff0c;然后装上跑了个小需求&#xf…

作者头像 李华