Written by G3rling @N0wayback/C0ba1t
前言
大家好我是G3rling,很高兴今年以出题人和院校负责人的身份参与ISCTF2025。
本次比赛我出的题目是 “小蓝鲨的千层FLAG”,主要考点为压缩包套娃解压和明文攻击。在明文攻击方面,我也只是学到一点皮毛,通过这次比赛,更多的是想分享自己在学习过程中遇到的一些有趣的用法。
在目前的环境下,新生更多的只是知道如何使用板子(通过压缩包内的文件进行明文攻击、PNG头明文攻击)。但是明文攻击是极其灵活多变的,不应该仅仅局限于这类常见的攻击,容易产生定式思维。应该把思维打开发现更多更有趣的攻击方式,这便是这道题诞生的初衷。
题解
首先拿到题目观察第一层压缩包,可以看到在压缩包内的注释中提示

“The password is 0eb9d3986e56473c“
并且可以观察到里面一层压缩包的名字为 “flagggg998.zip” ,在原基础上最后的数字减少了1。
我们尝试手动解压几层,可以找到编写代码的逻辑:
- 密码在压缩包的注释区域,具体位置在 “The password is ”之后,密码为长度为16的字符串
- 设flagggg999.zip为初始解压目标,每次解压都会使解压目标后的数字减少1(或者可以直接使用os读取解压路径下的文件达到相同的效果)
这是我的代码,大家可以进行参考
import os
import pyzipper
def extract_until_no_pwd(start_zip):
current = start_zip
to_delete = []
print(f"开始解压: {os.path.basename(current)}")
count = 0
while True:
count += 1
try:
with pyzipper.AESZipFile(current, 'r') as zf:
comment = zf.comment.decode('utf-8')
if comment and comment.startswith('The password is '):
pwd = comment.split(' ')[-1]
else:
break
zf.setpassword(pwd.encode('utf-8'))
files_in_zip = zf.namelist()
if not files_in_zip:
break
zf.extractall(path='.', pwd=zf.pwd)
inner = files_in_zip[0]
# 只把内层解出的 zip 加入删除列表,避免删除最外层的 999 zip
to_delete.append(inner)
current = inner
except Exception:
if os.path.exists(current):
cleanup_files(to_delete, current)
return current
else:
cleanup_files(to_delete, None)
return None
if os.path.exists(current):
cleanup_files(to_delete, current)
return current
else:
cleanup_files(to_delete, None)
return None
def cleanup_files(to_remove, keep):
if not to_remove:
return
for file_path in to_remove:
if keep and os.path.abspath(file_path) == os.path.abspath(keep):
continue
if os.path.exists(file_path):
try:
os.remove(file_path)
except Exception:
pass
start_file = "flagggg999.zip"
if not os.path.exists(start_file):
pass
else:
result = extract_until_no_pwd(start_file)
if result:
print(f"完成: {result}")
说来惭愧,这是让AI进行优化后的代码。最开始自己写的代码会将每个解压出来的文件都放在解压目录,解压后在目录下的东西很多,所以就让AI优化了一下,只保留了初始和停止位置的Zip。
但是无伤大雅,前面是为了这份饺子买的醋,接下来才是这道题的重头大戏。
当我们解压到 "flagggg3.zip" 后发现注释内容发生了改变,变成了

The password is... wait, I forgot! But you must know what's inside, right?
翻译:密码是... 等等,我忘了!但是你肯定知道里面有什么,对吧?
不管是从 "你肯定知道里面有什么" 亦或是在Hint中放出的链接我们都可以大概猜到这里考的是明文攻击。
基础的明文攻击部分的知识在这篇文章
也就是Hint中放出的文章中已有提及,这篇文章比较系统的介绍了明文攻击的条件以及对于一些标准文件格式的明文攻击
明文攻击最重要的是在符合攻击条件的前提下找到能够用于攻击的明文,通过解压的压缩包我们可以知道每次解压最后的数字都会减1,因此可以合理推测到,在 flagggg2.zip 中为 “flagggg1.zip”,即能够用于攻击的明文。
我有问题
可能会有同学有疑问
“为什么需要知道里面是flagggg1.zip呢?flagggg3.zip不是已经知道里面是flagggg2.zip了吗?”
对于明文攻击的原理,结合题目来讲,我的理解是:
如果你想对 flagggg3.zip文件 进行明文攻击,就必须知道 flagggg3.zip文件 中某个文件的明文用于攻击,即通过 flagggg2.zip文件 的明文进行攻击,而不是将 “flagggg2.zip”字符串 作为攻击的明文。“flagggg2.zip”字符串 只能算是flagggg3.zip文件中的明文
可能又会有同学有疑问
“为什么知道文件名就可以作为攻击的明文呢?”
我们通过压缩一个文件进行测试,这是 testtest.txt 的文件内容

但是压缩后我们可以看到txt中的内容无法看到

txt的内容就是我们需要寻找的能够用于攻击的明文,即123……。虽然txt的内容不可见了,但是txt的文件名依然可见,所以我们可以将 flagggg3.zip文件 中的 flagggg2.zip文件中的 flagggg1.zip字符串(因为flagggg2.zip里面压缩了flagggg1.zip)作为攻击的明文
同时需要注意的是,不仅是文件名,文件后缀也是可以使用的明文哦~这样看下来是不是刚好满足8+4的条件了。虽然8+4在这里不是很明显。(8指的是至少8字节连续的明文)
好啦,一切都准备好了。在通过合理的推理和对明文攻击原理的熟悉后我们找到了正确的明文(对的!就是 flagggg1.zip),我们接下来会用到明文攻击常用的工具 bkcrack,首先我们需要准备明文
echo -n "flagggg1.zip" > attack
接着我们通过准备的明文文件进行攻击
./bkcrack.exe -C flagggg3.zip -c flagggg2.zip -p attack -o 30
Zip文件头(504b0304)是固定的,因此可以将其作为补充明文
./bkcrack.exe -C flagggg3.zip -c flagggg2.zip -p attack -o 30 -x 0 504b0304
这里的参数解释大致如下
-C flagggg3.zip
被攻击的加密压缩包-c flagggg2.zip
已知的明文压缩包-p attack
明文存储文件-o 30
指定的明文在压缩包内目标文件的偏移量为30-x 0 504b0304
压缩包内目标文件部分额外已知明文值的偏移地址 :从偏移 0 开始明文为50 4b 03 04
bkcrack 1.7.1 - 2024-12-21
[13:04:46] Z reduction using 4 bytes of known plaintext
100.0 % (4 / 4)
[13:04:46] Attack on 1388313 Z values at index 37
Keys: ae0c4b27 66c21cba b9a7958f
10.0 % (139129 / 1388313)
Found a solution. Stopping.
You may resume the attack with the option: --continue-attack 139129
[13:05:17] Keys
ae0c4b27 66c21cba b9a7958f
得到的了key
ae0c4b27 66c21cba b9a7958f
接着我们可以使用key进行修改压缩包密码,爆破压缩包密码,或者是直接解压文件。
这里我建议直接通过key进行解压
/bkcrack.exe -C flagggg3.zip -c flagggg2.zip -k ae0c4b27 66c21cba b9a7958f -d flagggg2.zip
还没吃好
比较老练的师傅可能会觉得这样比较繁杂,那么有没有一步明文攻击呢?有的有的
./bkcrack.exe -C flagggg3.zip -c flagggg2.zip -x 30 666c6167676767312e7a6970
666c6167676767312e7a6970 是 flagggg1.zip 的Hex数据,结合上面的还可以用Zip文件头进行数据补充
./bkcrack.exe -C flagggg3.zip -c flagggg2.zip -x 30 666c6167676767312e7a6970 -x 0 504b0304
还没吃饱
为了减少难度,这道题将文件名就已经设置成了12字节。但是我们刚才还发现可以通过Zip固定文件头的504b0304进行攻击,那么就没有其他能够利用的明文了吗?
这里非常感谢 BR 师傅对于明文攻击思路的开拓,提出可以使用部分固定文件格式进行攻击。
这就要进一步了解Zip的结构了,继续请出刚才的 testtest.zip 老师

再请出 testtest.zip 老师 的弟弟 test.zip

观察可以发现,在相同压缩方式情况下,中央目录结束标记(504b0506)的偏移是相对固定的(偏移 -22),可以通过文件大小倒推得出正向偏移
查看 flagggg3.zip 可以看到 flagggg2.zip 原始大小为 254,计算正向偏移 254-22=232,为了验证是否正确,我们使用 504b0506 替代 504b0304 进行攻击尝试
./bkcrack.exe -C flagggg3.zip -c flagggg2.zip -x 30 666c6167676767312e7a6970 -x 232 504b0506
攻击成功
bkcrack 1.7.1 - 2024-12-21
[13:30:48] Z reduction using 4 bytes of known plaintext
100.0 % (4 / 4)
[13:30:48] Attack on 1388313 Z values at index 37
Keys: ae0c4b27 66c21cba b9a7958f
10.1 % (140370 / 1388313)
Found a solution. Stopping.
You may resume the attack with the option: --continue-attack 140370
[13:31:19] Keys
ae0c4b27 66c21cba b9a7958f
我们通过研究Zip文件结构特性,又找到了4个字节的明文。虽然4字节很短,但是对于明文攻击而言已经是非常宝贵的了。回顾这道题,在集思广益之下,我们已经找到了 12( flagggg1.zip)+ 4(504b0304) + 4 (504b0506)= 20 。接近明文攻击条件所需字节的两倍。
激进一点,甚至可以使用 504B0304140009000000 + 504B0506 直接进行明文攻击(10+4>12)。当然,这个肯定不是一定能行得通,需要对解压文件版本以及标识位进行确定(Just Guess!)

明文攻击永远不是死板的,而是极其灵活的。希望各位同学不要将思维固定,应该将思维扩展,将那些隐藏的明文挖掘出来。当然,挖掘需要建立在对压缩包文件熟悉的基础上,通过Misc了解到有趣的知识,这是Misc最吸引我的地方。
Misc不是套娃,不是脑洞。Misc是学习能力,是广阔知识面,望牢记在心。
感恩之心
感谢 N0wayback队长Cain 在NKCTF2023 出的 五年Misc,三年模拟(iCain一辈子❤)
感谢 N0wayback内部靶场 8+4的秘密升级版(虽然题目有问题,但是开拓了通过文件名进行明文攻击的思路)
感谢 2hi5hu 师傅投喂的国际赛非常好的明文攻击题目
感谢 BR 师傅对利用中央目录结束标记进行明文攻击思路的扩展
…… 还有很多未提及并不意味着忘记,只是因为记性不好一时想不起来
感谢g3喵
感谢g3喵