公司动态

CTF杂项实战:ZIP伪加密与Base64隐写原理剖析与破解

📅 2026/8/26 1:56:45
CTF杂项实战:ZIP伪加密与Base64隐写原理剖析与破解
1. 从一道“损坏”的ZIP包说起CTF杂项的实战起点最近在带新人打CTF练习赛又遇到了那道经典的“损坏的压缩包”题。题目给了一个ZIP文件下载下来用系统自带的解压工具或者一些常规软件打开要么提示“文件头错误”要么直接报“不是有效的ZIP文件”。新手往往就卡在这里开始怀疑人生甚至去重新下载。但如果你有经验看到这种提示尤其是CTF杂项Misc分类下的第一反应不会是文件真坏了而是“这里头有活儿”。这道题就是一个典型的“ZIP伪加密”结合“Base64隐写”的复合型题目它完美地串联了文件格式分析、编码转换和隐写术这几个杂项的核心考点。今天我就以这道题为引子把ZIP伪加密的原理、手工分析与破解以及Base64隐写在CTF中的各种花活掰开揉碎了讲清楚。无论你是刚入门CTF的新手还是想巩固基础的老手这篇从实战出发的深度解析都能让你下次遇到类似题目时心里有谱手上有招。所谓“杂项”考的就是你的知识广度、信息搜集能力和脑洞。它不像PWN或逆向那样有明确的路径往往需要你从一堆看似杂乱无章的数据Miscellaneous Data里找到那条隐藏的线索。ZIP和Base64一个是压缩归档的事实标准一个是网络传输中最常见的编码方式它们太基础、太常见了以至于出题人特别喜欢在这上面做文章设置各种“视觉盲区”。弄懂它们不仅是解几道题更是构建你安全与数据分析思维的重要一环。2. ZIP伪加密一场精心策划的“骗局”2.1 ZIP文件格式的“密码位”玄机要理解伪加密首先得知道ZIP文件是怎么说自己被加密的。一个ZIP文件由三部分组成本地文件头Local File Header、文件数据File Data和中央目录Central Directory。其中中央目录里还有一个中央目录文件头Central Directory File Header。判断一个文件是否加密、用什么方式加密关键就藏在两个地方本地文件头的通用位标记General Purpose Bit Flag和中央目录文件头的加密标记。重点看通用位标记的第0位bit 0。如果这个位被设置为1通常表示该文件是加密的。但是这里存在一个历史遗留的“漏洞”或者说“设计上的不严谨”ZIP标准APPNOTE.TXT规定判断加密的最终依据应该是中央目录文件头里的加密方法字段如果非零则表示加密。然而很多常见的解压软件如Windows资源管理器、早期版本的WinRAR、一些在线解压工具在解压时会优先检查本地文件头的通用位标记。如果看到bit 0是1它们就“想当然”地认为文件被加密了进而要求你输入密码而不会去严格校验中央目录里的加密标记是否真的有效。伪加密就是钻了这个空子。出题人通过十六进制编辑器手动将ZIP文件中本地文件头的通用位标记的bit 0修改为1而保持中央目录文件头中的加密方法字段为0表示未加密。这样一来文件本身的数据根本没有被任何算法加密是完全可读的。但那些“偷懒”的解压软件却被这个假的“加密标志”给骗了弹出了密码输入框。如果你没有密码或者输入任意密码这些软件都会解压失败。这就制造了一个“文件被加密”的假象。2.2 手工分析与修复用WinHex和010 Editor透视ZIP面对一个疑似伪加密的ZIP我们不能依赖常规软件。必须请出十六进制编辑器直接查看文件的“骨骼”。这里我推荐WinHex或010 Editor后者因为有ZIP模板解析起来更直观。第一步定位文件头签名。用编辑器打开ZIP文件搜索十六进制签名。ZIP本地文件头的签名是50 4B 03 04PK..中央目录文件头的签名是50 4B 01 02PK..。通常文件开头就是第一个本地文件头。第二步分析关键字段。找到50 4B 03 04后向后偏移一定字节就是关键字段偏移6字节处通用位标记2字节。我们关心它的低字节Little-Endian。例如看到09 00实际值是0x0009。将其转为二进制看最低位bit 0是否为1。0x0009的二进制是0000 0000 0000 1001bit 0是1说明本地文件头标记为加密。继续往后找到50 4B 01 02中央目录文件头。在其后偏移8字节处加密方法字段2字节。如果这里是00 00则表示实际上未使用任何加密算法。第三步实施修复。修复原理很简单既然问题是本地文件头谎报了加密那就把谎报的标记改回去。找到50 4B 03 04后的通用位标记2字节将其值修改。伪加密通常只设置bit 0为1所以我们只需将这个位的1改为0。例如原值为09 00(0x0009)。0x0009-0x00010x0008。所以将09 00改为08 00即可。更稳妥的方法是直接计算原值 AND 0xFFFE即清除最低位。0x0009 0xFFFE 0x0008。修改后保存文件。此时再用任何解压软件打开应该就能直接解压看到里面的文件了。注意有些题目可能会设置多重伪加密即修改了多个地方。或者真正的加密与伪加密混合。我们的核心判断依据始终是中央目录文件头的加密方法字段是否为0。如果为0则文件数据本身未加密所有阻碍都来自被篡改的标记位。2.3 自动化工具与脚本效率解法的两面性手工分析虽能加深理解但比赛时争分夺秒。掌握自动化工具是必须的。ZipCenOp.jar这是最经典的Java工具。命令很简单java -jar ZipCenOp.jar r 你的文件.zip。它会自动扫描并修复伪加密。原理就是遍历ZIP结构将那些加密方法为0但通用位标记却显示加密的条目修复。对于纯伪加密的题这就是一键秒杀。binwalk强大的文件分析工具虽然不直接修复伪加密但能快速识别。binwalk -e 文件.zip命令有时能直接暴力提取出未被伪加密标记影响的文件数据非常有用。Python脚本自己写脚本能应对更复杂的情况。下面是一个简单的修复脚本框架import sys import os def fix_fake_encryption(zip_path): with open(zip_path, rb) as f: data bytearray(f.read()) i 0 while i len(data): # 查找本地文件头 if data[i:i4] bPK\x03\x04: gp_bit data[i6] (data[i7] 8) # 如果通用位标记的bit 0被设置 if gp_bit 1: print(f发现疑似伪加密标记在偏移 0x{i:x}, GP Bit: {gp_bit:#06x}) # 这里可以添加逻辑检查对应的中央目录条目确认是否为真加密 # 简单修复直接清除bit 0 data[i6] (gp_bit 0xFFFE) 0xFF data[i7] ((gp_bit 0xFFFE) 8) 0xFF print(f 已修复为: {(data[i6] (data[i7] 8)):#06x}) # 查找中央目录文件头用于辅助判断略 i 1 f.seek(0) f.write(data) print(处理完成。) if __name__ __main__: if len(sys.argv) ! 2: print(用法: python fix_zip.py file.zip) sys.exit(1) fix_fake_encryption(sys.argv[1])实操心得不要过度依赖自动化工具。工具可能更新不及时或对某些畸形ZIP处理不佳。尤其在混合型题目中如伪加密真加密损坏手工分析结合脚本才是王道。先用zipdetailsLinux或010 Editor模板整体看结构再用工具或脚本进行定点修复。3. Base64隐写藏在“等号”里的秘密解开了伪加密的ZIP我们拿到了一个看起来正常的文本文件或者一张图片。但flag并不在明面上。这时就要把目光投向Base64编码了。Base64隐写Base64 Steganography是一种将信息隐藏于Base64编码数据本身而非编码后内容的技术。它的核心在于利用Base64编码过程中填充字符“”所对应的未使用比特。3.1 Base64编码原理与隐写空间Base64将每3个字节24位的二进制数据转换为4个ASCII字符。这4个字符从包含64个字符的码表A-Z, a-z, 0-9, , /中选取。如果原始数据长度不是3的倍数就需要在末尾填充一个或两个“”号使总长度变为4的倍数。关键点来了这些填充的“”本身并不携带原始数据的信息它们只是占位符。但是在编码的最后一段可能不足3字节转换成4个字符的过程中原始数据的最后几个比特会决定最后一个或两个有效编码字符的低位比特。隐写如何发生假设我们有一段数据编码后末尾是...YQ。Y是有效字符Q也是两个是填充。在标准解码时解码器只关心Y和Q携带的足以还原原始字节的比特被忽略。然而Q这个字符的低位具体是最后2个或4个比特取决于原始数据余数在解码时其实是用不到的是“冗余”的。隐写术就是修改这些“冗余”的低位比特将其替换为秘密信息。因为修改的是解码时不使用的比特所以用标准Base64解码器解码出来的明文Carrier Data完全不变但隐写的信息却藏在了编码数据本身的结构里。3.2 识别与提取从怀疑到验证在CTF中如何判断一个文件可能包含Base64隐写文件内容特征拿到一个文本文件里面是大量的、看起来像Base64编码的字符串由64个字符组成可能以结尾。用常规Base64解码后得到的内容可能是一段无意义的文字、另一个Base64串、或者一张图片的二进制数据。如果解码结果看起来“正常”但没有flag就要考虑隐写。工具探测使用专门的Base64隐写检测与提取工具是最快的方法。Python库base64stego可以安装pip install base64stego。使用base64stego extract -f 疑似文件.txt尝试提取。在线工具或脚本很多CTF平台会提供相关的解题脚本。核心逻辑是读取Base64字符串提取每个编码字符除了填充的最后几个有效比特然后拼接起来。手动分析脚本理解原理后自己写一个提取脚本并不难。下面是一个简化版的Python提取脚本import base64 # Base64字符集索引即其代表的6位值 BASE64_CHARS ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789/ def get_base64_char_value(char): 获取Base64字符对应的6位整数值 if char : return 0 # 填充符按0处理但实际不携带信息 return BASE64_CHARS.index(char) def extract_lsb_from_base64_file(filename): 从包含Base64字符串的文件中提取LSB隐写信息 with open(filename, r) as f: b64_string f.read().replace(\n, ) # 去除换行 binary_secret # 遍历Base64字符串每次处理4个字符一个编码块 for i in range(0, len(b64_string), 4): chunk b64_string[i:i4] if len(chunk) 4: break # 计算这个块中有几个填充 padding_count chunk.count() # 有效字符数 valid_chars 4 - padding_count # 根据有效字符数判断可以提取多少LSB # 规则有效字符为2时如YQ最后一个有效字符索引1的最后2位是冗余的。 # 有效字符为3时如YWJ最后一个有效字符索引2的最后4位是冗余的。 # 有效字符为4时无冗余但有些变种可能利用所有字符的最后几位需看题目。 if valid_chars 2: # 例子YQ我们关注 Q (chunk[1]) char_value get_base64_char_value(chunk[1]) # 提取最后2位 (bit 1和 bit 0) lsb_bits char_value 0b11 binary_secret f{lsb_bits:02b} elif valid_chars 3: # 例子YWJ我们关注 J (chunk[2]) char_value get_base64_char_value(chunk[2]) # 提取最后4位 (bit 3,2,1,0) lsb_bits char_value 0b1111 binary_secret f{lsb_bits:04b} elif valid_chars 4: # 标准无填充块通常不用于简单隐写但有些题目可能用所有字符的LSB # 这里作为扩展可以提取每个字符的最后1位或2位具体看题目暗示 # 本例暂不处理 pass # 将二进制字符串转换为字节 secret_bytes bytearray() for j in range(0, len(binary_secret), 8): byte_str binary_secret[j:j8] if len(byte_str) 8: secret_bytes.append(int(byte_str, 2)) return bytes(secret_bytes) # 使用示例 secret_data extract_lsb_from_base64_file(hidden_message.txt) print(提取出的原始字节:, secret_data) # 尝试以文本形式输出 try: print(尝试解码为文本:, secret_data.decode(utf-8)) except UnicodeDecodeError: print(非UTF-8文本可能是其他数据如flag、密钥等) # 可以尝试打印十六进制 print(十六进制表示:, secret_data.hex())注意事项Base64隐写的具体规则可能有变种。上述脚本遵循最常见的规则根据填充数决定提取位数。有些题目可能会利用所有Base64字符的最低1位LSB来隐写类似于图片LSB隐写。这就需要你观察解码后明文数据的特征或者题目描述中的提示如“最低位”。3.3 实战中的复合技巧ZIPBase64其他CTF高手出题不会只考单一知识点。我们开头提到的题目就是经典复合一个伪加密的ZIP解压后得到一个文本文件里面是Base64编码的数据。Base64解码后可能得到另一个ZIP文件的二进制数据需要保存为.zip文件再次分析可能又是伪加密或真加密。一张图片但图片文件可能被损坏或者其文件头尾附加了多余数据需要binwalk或foremost分离或者图片本身包含LSB隐写。一段看似乱码的文本可能经过了一次或多次Base64/Base32/Hex/ROT等编码需要循环解码。直接就是flag但被花括号、下划线等包围需要你识别。通用解题流程建议拿到文件先file命令在Linux下或用Cygwin/Git Bashfile 题目.zip可以快速识别文件真实类型有时ZIP后缀下可能是其他文件。binwalk和foremost分离对任何文件尤其是图片、文档先用binwalk -e 文件和foremost -i 文件尝试自动分离嵌入的文件。这能解决很多“套娃”题。strings找线索strings 文件 | grep -i flag或strings 文件 | grep -E ‘[{}]’有时能直接发现flag或提示。循环解码思维对于文本数据如果看起来像编码Base64字符集、只有0-9a-f等就用CyberChef在线神器或自己写脚本尝试Base64、Base32、Hex、URL编码、ASCII移位等常见编码解码直到出现可读文本或类似PK头50 4B的文件签名。留意文件末尾用十六进制编辑器查看文件末尾有时flag就附加在那里。4. 进阶Base64隐写的变种与对抗基础的Base64隐写利用的是填充位的冗余比特。但出题人的脑洞是无限的。变种1全字符LSB隐写不局限于最后一块而是对所有Base64编码字符的最低1位或2位进行隐写。这样即使没有填充也能隐藏信息。提取时需要遍历整个Base64字符串的每个字符提取其LSB然后拼接。这要求载体Base64数据足够长才能隐藏有意义的秘密信息。变种2修改码表标准的Base64码表是A-Za-z0-9/。但RFC 4648也允许使用A-Za-z0-9-_作为“URL安全的”Base64变种。出题人可能会自定义一个码表。如果你发现一段数据很像Base64但用标准码表解不出来或者解出来是乱码就要考虑码表是否被替换。线索可能在题目描述、注释或文件其他部分。变种3结合加密或压缩隐写的信息本身可能是经过加密或压缩的。提取出二进制数据后发现不是明文flag可能需要进一步分析。例如提取出的数据有89 50 4E 47(PNG头) 或1F 8B(Gzip头)那就保存为相应文件格式再打开。对抗与检测从防守或取证角度如何发现Base64隐写统计异常分析Base64字符串中不同字符的分布。如果某些字符的低位比特分布不均匀可能存在隐写。但这需要大量样本。工具扫描使用像steghide、zsteg针对PNG/BMP等工具的扩展插件或专门的Base64隐写检测脚本。流量分析在网络流量中异常长或重复出现的Base64编码数据流可能用于隐蔽信道传输。5. 实战案例复盘与工具箱推荐让我们复盘一个简化但完整的比赛题串联所有步骤题目misc50.zip下载后解压要求密码。file misc50.zip确认是ZIP文件。用zipdetails misc50.zip或010 Editor查看发现本地文件头通用位标记为0x0009中央目录加密方法为0x0000。判定为伪加密。使用java -jar ZipCenOp.jar r misc50.zip修复或手动用WinHex将09 00改为08 00。成功解压得到message.txt内容是一长串Base64。用base64 -d message.txt output解码发现output文件用file命令查看是PNG image data。将output重命名为flag.png打开图片显示一张二维码。用手机或工具扫描二维码得到一串字符cGljb0NURntiYXNlNjRfaXNfY29vbH0。这看起来又是Base64再次解码echo ‘cGljb0NURntiYXNlNjRfaXNfY29vbH0’ | base64 -d得到最终flagpicoCTF{base64_is_cool}。在这个过程中如果第5步Base64解码后得到的不是PNG而是一段文本可能就需要考虑Base64隐写了。我们会用之前写的脚本对message.txt进行LSB提取尝试。必备工具箱清单十六进制编辑器WinHex (Windows), 010 Editor (跨平台强推), HxD (免费轻量)。文件分析file,binwalk,foremost,exiftool(查看元数据)。压缩包处理7z,unzip,ZipCenOp.jar以及Python的zipfile库。编码解码CyberChef (网页神器)本地可用base64,xxd(hex),iconv(字符集转换)Python的codecs、base64库。隐写分析steghide(需要密码),zsteg(PNG/BMP),stegsolve(图片层分析),outguess。脚本环境Python3配备PIL(图像处理)、pwntools(CTF万能)、cryptography等库。6. 常见问题排查与避坑指南Q1: 修复了伪加密还是解压失败提示“文件头错误”或“压缩包损坏”。A1: 这可能不是简单的伪加密。检查以下几点真加密伪加密混合中央目录的加密方法字段非零如01 00表示传统ZIP加密。你需要密码。尝试常用弱口令password,123456,flag, 比赛名等或暴力破解用fcrackzip。ZIP文件结构被故意破坏比如被附加了多余数据或头部被修改。用binwalk -e尝试分离或用dd命令截取正确的ZIP部分。不是ZIP文件用file命令确认真实类型。可能是其他文件被改了后缀。Q2: Base64解码后是乱码或者提示“填充错误”。A2:不是标准Base64尝试Base32、Base16(Hex)、Ascii85等编码。观察字符集。换行符问题确保解码前去除所有换行符和空格。自定义码表寻找题目中是否提示了码表替换规则。多层编码解码出的乱码可能又是另一种编码继续解码。数据被截断或损坏检查原始Base64字符串长度是否为4的倍数填充是否正确。Q3: 提取Base64隐写后得到的二进制数据看不出是什么。A3:检查文件头用xxd或十六进制编辑器看前几个字节判断文件类型如FF D8 FF是JPEG25 50 44 46是PDF。尝试常见格式将其保存为.zip,.png,.jpg,.txt等后缀尝试打开。可能是加密数据如果看起来完全随机可能是AES等加密后的数据。需要结合上下文找密钥。Q4: 在Linux下操作ZIP文件权限或中文文件名乱码。A4:尽量使用7z命令它对各种压缩格式和编码支持更好7z x 文件.zip。对于乱码可以尝试指定编码unzip -O CP936 文件.zip针对GBK编码。最大的坑思维定势不要看到ZIP就只想到伪加密看到Base64就只想到解码。CTF杂项的魅力在于“杂”。始终保持怀疑多角度验证。一个文件先用file、binwalk、strings三连往往能发现意想不到的入口点。工具是辅助理解原理和灵活运用才是关键。