/
← Voltar ao feed
high

Nine 0-days in HTML-to-PDF libraries: ReportLab RCE, ReLaXed RCE, WeasyPrint SSRF/LFR, mPDF, Dompdf and TCPDF flaws

1 fonte(s) · 1 atualização(ões) · publicado 23/06/2026 16:45 · coletado há 2 dias

Researcher Pedro Cruz (Hakai Security / The Hacking Club) disclosed nine vulnerabilities in widely used server-side PDF generation libraries, with a combined footprint of over 90 million monthly installs. The cases range from RCE (ReportLab, ReLaXed) through arbitrary file read and SSRF (WeasyPrint, Browsershot) to DoS and file/directory existence oracles (mPDF, Dompdf, TCPDF). The common cause is user input concatenated into templates or HTML and passed to a renderer that runs on the backend with filesystem, internal network and possibly cloud metadata access. The article also gives a six-step methodology for testing any "Export PDF" feature. No in-the-wild exploitation is reported.

Detalhes técnicos

  • ReportLab (<4.4.8, 50.9M downloads/month): decode_label in reportlab/lib/utils.py calls pickle.loads(). It is reachable through the <onDraw name="_indexAdd" label="..."/> tag inside a Paragraph when the app uses SimpleIndex (getCanvasMaker()) or PageAccumulator, giving RCE from user-controlled text. Fixed in 4.4.8 (2026-01-15).
  • ReLaXed: user input concatenated into the Pug template source lets an attacker open a - ... code block, because require is exposed in the pug.render() context. This gives RCE as the ReLaXed process.
  • WeasyPrint <link rel="attachment" href="file://..."> gives local file read (CVE-2024-28184, credited to @nullie, not the author's discovery).
  • WeasyPrint <68.0 (CVE-2025-68616, CVSS 7.5): default_url_fetcher uses urllib.request.urlopen, which follows 301/302/307 redirects without re-entering a custom url_fetcher. This bypasses SSRF blocklists and exposed AWS STS credentials via 169.254.169.254. Advisory GHSA-983w-rhvv-gwmv.
  • Browsershot (spatie): the $unsafeProtocols blocklist misses UNC paths, so Chromium resolves \\localhost/etc/passwd as a local file (LFI).
  • mPDF: the manual SVG <!ENTITY> expander built on preg_replace allows a Billion Laughs DoS (482 bytes of input, ~1 GB RAM). A second issue is directory enumeration via LocalContentLoader::load warnings.
  • Dompdf (CVE-2026-55555, CVSS 6.3, CWE-770): @font-face src:url(file://...) causes OOM only when the file exists, creating a file existence oracle that also bypasses chroot. Fixed in 3.1.6+.
  • TCPDF: Image() error messages leak the full path when getimagesize() fails, so one request can test thousands of paths.
  • Methodology and bypasses: HTML injection test, JS execution test, tag-by-tag attacks, encoding, alternative protocols and tags, <base href>, redirects, IPv6 forms of the metadata IP (6to4, NAT64), and side-channels.

Remediação

  • Update ReportLab to ≥4.4.8, WeasyPrint to ≥68.0 and Dompdf to ≥3.1.6.
  • Never concatenate input into template source; pass it as locals and strip require from the context.
  • Use allowlists (protocol, host, path) rather than blocklists, and revalidate on every redirect hop.
  • Classify IP addresses by class, including tunneled prefixes, not by string matching.
  • Disable JS in headless engines, isolate the worker's network, and avoid loading real session cookies.
  • Return generic error messages to close the oracles.
  • Avoid unsafe parsers on user data (pickle.loads, eval, unserialize, preg_replace in loops).

Linha do tempo

  1. yokai.hakaisecurity.io ↗ · pub. 23/06/2026 16:45 · coletado 19/10 18:03 primeiro registro

    Nine 0-days in HTML-to-PDF libraries: ReportLab RCE, ReLaXed RCE, WeasyPrint SSRF/LFR, mPDF, Dompdf and TCPDF flaws

    ler a análise desta fonte

    Researcher Pedro Cruz (Hakai Security / The Hacking Club) disclosed nine vulnerabilities in widely used server-side PDF generation libraries, with a combined footprint of over 90 million monthly installs. The cases range from RCE (ReportLab, ReLaXed) through arbitrary file read and SSRF (WeasyPrint, Browsershot) to DoS and file/directory existence oracles (mPDF, Dompdf, TCPDF). The common cause is user input concatenated into templates or HTML and passed to a renderer that runs on the backend with filesystem, internal network and possibly cloud metadata access. The article also gives a six-step methodology for testing any "Export PDF" feature. No in-the-wild exploitation is reported.

    Technical Details

    • ReportLab (<4.4.8, 50.9M downloads/month): decode_label in reportlab/lib/utils.py calls pickle.loads(). It is reachable through the <onDraw name="_indexAdd" label="..."/> tag inside a Paragraph when the app uses SimpleIndex (getCanvasMaker()) or PageAccumulator, giving RCE from user-controlled text. Fixed in 4.4.8 (2026-01-15).
    • ReLaXed: user input concatenated into the Pug template source lets an attacker open a - ... code block, because require is exposed in the pug.render() context. This gives RCE as the ReLaXed process.
    • WeasyPrint <link rel="attachment" href="file://..."> gives local file read (CVE-2024-28184, credited to @nullie, not the author's discovery).
    • WeasyPrint <68.0 (CVE-2025-68616, CVSS 7.5): default_url_fetcher uses urllib.request.urlopen, which follows 301/302/307 redirects without re-entering a custom url_fetcher. This bypasses SSRF blocklists and exposed AWS STS credentials via 169.254.169.254. Advisory GHSA-983w-rhvv-gwmv.
    • Browsershot (spatie): the $unsafeProtocols blocklist misses UNC paths, so Chromium resolves \\localhost/etc/passwd as a local file (LFI).
    • mPDF: the manual SVG <!ENTITY> expander built on preg_replace allows a Billion Laughs DoS (482 bytes of input, ~1 GB RAM). A second issue is directory enumeration via LocalContentLoader::load warnings.
    • Dompdf (CVE-2026-55555, CVSS 6.3, CWE-770): @font-face src:url(file://...) causes OOM only when the file exists, creating a file existence oracle that also bypasses chroot. Fixed in 3.1.6+.
    • TCPDF: Image() error messages leak the full path when getimagesize() fails, so one request can test thousands of paths.
    • Methodology and bypasses: HTML injection test, JS execution test, tag-by-tag attacks, encoding, alternative protocols and tags, <base href>, redirects, IPv6 forms of the metadata IP (6to4, NAT64), and side-channels.

    Remediation

    • Update ReportLab to ≥4.4.8, WeasyPrint to ≥68.0 and Dompdf to ≥3.1.6.
    • Never concatenate input into template source; pass it as locals and strip require from the context.
    • Use allowlists (protocol, host, path) rather than blocklists, and revalidate on every redirect hop.
    • Classify IP addresses by class, including tunneled prefixes, not by string matching.
    • Disable JS in headless engines, isolate the worker's network, and avoid loading real session cookies.
    • Return generic error messages to close the oracles.
    • Avoid unsafe parsers on user data (pickle.loads, eval, unserialize, preg_replace in loops).