-
-
Notifications
You must be signed in to change notification settings - Fork 191
/
makezip.py
65 lines (46 loc) · 1.42 KB
/
makezip.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#!/usr/bin/env python3
# a zip generator for root prefix.
# abusing the extra field of an empty file to host collision blocks.
# Craft your root files (with forged CRC32s)
# Then run with the name of the root in the archive
# Ex: makezip.py root1 root2 _rels/.rels
# Ange Albertini 2021 - MIT licence
import io
import sys
import zlib
# ZipFile, with no Extra Field written in the Central Directory
import ziphack as z
def collblock(count):
result = b""
for _ in range(count + 1):
result += bytes(range(64))
return result
ROOT_NAME = sys.argv[3]
# Better keep this file uncompressed and of same length
# so that offsets are guaranteed to be constant
relsfile = z.ZipInfo(filename=ROOT_NAME)
blocks = z.ZipInfo(
filename="blocks",
date_time=(2015, 11,13, 21,15,0),
)
field = collblock(10)
blocks.extra = b"AP" + \
len(field).to_bytes(2, "little") + \
field
def poc(rels, fn):
hFile = io.BytesIO()
with z.ZipFile(hFile, mode='w') as final:
final.writestr(relsfile, rels)
final.writestr(blocks, b"\x9c\x7c\xbe\xae") # => 0xC0111DED CRC32
with open(fn, "wb") as f:
f.write(hFile.getvalue())
with open(sys.argv[1], "rb") as f1:
root1 = f1.read()
with open(sys.argv[2], "rb") as f2:
root2 = f2.read()
# requirements to keep the Central Directory identical
assert root1 != root2
assert zlib.crc32(root1) == zlib.crc32(root2) # == 0XC0111DED
assert len(root1) == len(root2)
poc(root1, "root1.zip")
poc(root2, "root2.zip")