zip 文件暴破
2007年年初,美国德克萨斯州的布朗斯维尔市的程序员 Albert Castillo 协助警方调查 John Craig Zimmerman 涉嫌拥有和制造儿童色情物品的案件,而 Castillo 就是使用字典式攻击技术^[字典式攻击是一种破解密码的方法。是指在破解密码或密钥时,逐一偿试用户自定义词典中的单词或短语的攻击方式。——字典式攻击技术]解密了嫌疑人电脑中的zip压缩文件,从而得到儿童色情图片等证据,得以结束案件。本文十里就简单介绍一下使用 python3 进行字典式破解加密zip文件的方法。
准备工作
加密码的zip文件
首先得准备一个加密码的 zip 文件,以 macOS 为例,可以使用 zip
命令进行压缩并加密码,比如要将 demo.md
压缩为 demo.zip
,可以使用命令:
$ zip -e demo.zip demo.md
Enter password:
Verify password:
updating: demo.md (stored 0%)
输入密码和确认密码,即可完成加密码的压缩,我这里使用密码是 4 位的,为 6556,不自己做压缩包的话,可以使用压缩的 zip 压缩包:demo.zip
了解 zipfile 库
本文使用 zipfile 库操作压缩文件,这是一个内建的 python 库,不需要手动安装,这一小节在 ipython 环境下简单了解一下使用方法。命令行中输入 ipython
回车即可进入 python 交互环境:
导入 zipfile 库:
In [1]: import zipfile
打开压缩包
In [2]: zFile = zipfile.ZipFile('demo.zip')
解压压缩包
In [3]: zFile.extractall()
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-3-24da26377a5e> in <module>()
----> 1 zFile.extractall()
...
RuntimeError: File <ZipInfo filename='demo.md' filemode='-rw-r--r--' file_size=17 compress_size=29> is encrypted, password required for extraction
可以看到提示,因为我们测试用的 demo.zip
是我们加了密码的,所以需要密码。extractall
方法有一个参数 pwd
指明密码,用正确密码试一下:
In [4]: zFile.extractall(pwd='6556')
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-4-f057151f05c2> in <module>()
----> 1 zFile.extractall(pwd='6556')
...
TypeError: pwd: expected bytes, got str
抛出了一个 TypeError
类型错误的异常,看提示是需要密码是 bytes
,那进行转码^[Zipfile Method doesn’t works]试一下:
In [5]: zFile.extractall(pwd='6556'.encode('utf8'))
没有了异常或错误提示,说明解压成功了!我们再试一个错误密码:
In [6]: zFile.extractall(pwd='5555'.encode('utf8'))
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-6-260b36a163cd> in <module>()
----> 1 zFile.extractall(pwd='5555'.encode('utf8'))
RuntimeError: Bad password for file <ZipInfo filename='demo.md' filemode='-rw-r--r--' file_size=17 compress_size=29>
可以看到如果密码错误会提示密码不对,所以可以使用try-except
调用 extractall
方法验证密码是否正确,形如:
import zipfile
zFile = zipfile.ZipFile('demo.zip')
try:
zFile.extractall(pwd=key.encode('utf8'))
print(f'find password -> {key}')
except:
print('bad password')
检查是否加密
检查压缩包是否加密这也是必要的,这里使用 infolist
方法可以查看加密文件信息^[How to check if a zip file is encrypted using python’s standard library zipfile?],从而知道是否加密,如下:
In [7]: for i in zFile.infolist():
...: if i.flag_bits & 0x01:
...: print('is encrypted')
...:
is encrypted
密码字典
这里使用 python3 生成密码字典 一文中实现的 keykey 工具生成数字组成的4位密码字典,使用keykey工具在当前目录生成密码字典文件 keys.dict
:
$ python3 keykey 4 4 -c '0123456789' -o 'keys.dict'
[+] 已生成 4 ~ 4 位密码字典 [+]
[+] 已保存密码字典到 /Users/5km/Documents/workspace/python/tools-with-script/crackzip/keys.dict [+]
最终生成密码字典文件 keys.dict 中,每行一个密码:
实现
新建 crackzip
文件,添加以下内容,指明脚本解释器和导入 zipfile
库:
#!/usr/bin/env python3
import zipfile
import argparse
为文件 `` 添加运行权限:
$ chmod +x crackzip
为了使程序模块化,需要对一些操作进行封装。
破解操作
破解操作封装如下:
def crackzipfile(zipFile, password):
"""尝试破解 zip 文件
Args:
zipFile: 待解压 zip 实例
password: 密码
Return:
如果破解解压成功返回 True,如果出现异常返回 False,说明密码错误
"""
try:
zipFile.extractall(pwd=password.encode('utf8'))
return password
except:
return
如果密码正确就会返回密码,反之返回 None
。
判断是否加密
使用 检查是否加密 小节中的方法,封装为 is_encrypted
方法如下:
def is_encrypted(zipFile):
"""检查 zip 文件是否加密
Args:
zipFile: 待解压 zip 实例
"""
for info in zipFile.infolist():
if info.flag_bits & 0x01:
return True
else:
return False
获取命令参数
使用 argparse
库解析命令参数,这里也进行封装:
def getargs():
""" 获取命令行参数
Return:
返回命令行参数解析结果
"""
parser = argparse.ArgumentParser(description='暴力破解 zip 文件的密码')
parser.add_argument('zfile', help='指明要破解的 zip 文件,可以是多个')
parser.add_argument('dict', help='指定使用的密码词典')
return parser.parse_args()
配置了两个占位参数,调用命令时,必须指定 zip 文件和密码字典文件。
主函数
整个破解流程封装到 main
方法:
def main():
args = getargs()
with zipfile.ZipFile(args.zfile) as zFile:
if not is_encrypted(zFile):
print(f'{args.zfile} 未加密!')
exit(0)
with open(args.dict, 'r') as f:
for line in f.readlines():
key = line.strip('\n')
password = crackzipfile(zFile, key)
if password:
print(f'[+] 密码是 {password} [+]')
exit(0)
首先获取命令参数,因为要用到 zip 文件和密码字典文件的路径,打开 zip 文件,然后判断是否是加密的,如果不是加密的就打印提示信息,并结束程序;如果是加密的,就读取密码字典中的密码尝试解压,如果找到正确密码,打印信息并结束程序。
直接在执行 main
即可运行程序:
if __name__ == '__main__':
main()
完整代码参考:tools-with-script/crackzip/crackzip
测试
目录下有一个加密的 zip 文件 demo.zip
和一个没有加密的 zip 文件 demo1.zip
,测试如下:
$ ./crackzip -h
usage: crackzip [-h] zfile dict
暴力破解 zip 文件的密码
positional arguments:
zfile 指明要破解的 zip 文件,可以是多个
dict 指定使用的密码词典
optional arguments:
-h, --help show this help message and exit
$ ./crackzip demo.zip keys.dict
[+] 密码是 6556 [+]
$ ./crackzip demo1.zip keys.dict
demo1.zip 未加密!
总结
通过使用 zipfile 库和 argparse 库简单实现了破解 zip 文件密码的工具,也简单了解了字典式破解密码的基本原理,这种破解原理适用大部分的密码破解工作,但有时也必须考虑所需时间的合理性。