hacktricks/src/generic-methodologies-and-resources/python/bypass-python-sandboxes/reportlab-xhtml2pdf-triple-brackets-expression-evaluation-rce-cve-2023-33733.md
HackTricks News Bot fdb533d0f7 Add content from: University (HTB): Exploiting ReportLab CVE‑2023‑33733 to gai...
- Remove searchindex.js (auto-generated file)
2025-08-27 18:54:18 +00:00

6.5 KiB
Raw Blame History

ReportLab/xhtml2pdf [...] expression-evaluation RCE (CVE-2023-33733)

{{#include ../../../banners/hacktricks-training.md}}

This page documents a practical sandbox escape and RCE primitive in ReportLabs rl_safe_eval used by xhtml2pdf and other PDF-generation pipelines when rendering user-controlled HTML into PDFs.

CVE-2023-33733 affects ReportLab versions up to and including 3.6.12. In certain attribute contexts (for example color), values wrapped in triple brackets [ ... ] are evaluated server-side by rl_safe_eval. By crafting a payload that pivots from a whitelisted builtin (pow) to its Python function globals, an attacker can reach the os module and execute commands.

Key points

  • Trigger: inject [ ... ] into evaluated attributes such as within markup parsed by ReportLab/xhtml2pdf.
  • Sandbox: rl_safe_eval replaces dangerous builtins but evaluated functions still expose globals.
  • Bypass: craft a transient class Word to bypass rl_safe_eval name checks and access the string "globals" while avoiding blocked dunder filtering.
  • RCE: getattr(pow, Word("globals"))["os"].system("")
  • Stability: Return a valid value for the attribute after execution (for color, use and 'red').

When to test

  • Applications that expose HTML-to-PDF export (profiles, invoices, reports) and show xhtml2pdf/ReportLab in PDF metadata or HTTP response comments.
    • exiftool profile.pdf | egrep 'Producer|Title|Creator' → "xhtml2pdf" producer
    • HTTP response for PDF often starts with a ReportLab generator comment

How the sandbox bypass works

  • rl_safe_eval removes or replaces many builtins (getattr, type, pow, ...) and applies name filtering to deny attributes starting with __ or in a denylist.
  • However, safe functions live in a globals dictionary accessible as func.globals.
  • Use type(type(1)) to recover the real builtin type function (bypassing ReportLabs wrapper), then define a Word class derived from str with mutated comparison behavior so that:
    • .startswith('') → always False (bypass name startswith('') check)
    • .eq returns False only at first comparison (bypass denylist membership checks) and True afterwards (so Python getattr works)
    • .hash equals hash(str(self))
  • With this, getattr(pow, Word('globals')) returns the globals dict of the wrapped pow function, which includes an imported os module. Then: ['os'].system('').

Minimal exploitation pattern (attribute example) Place payload inside an evaluated attribute and ensure it returns a valid attribute value via boolean and 'red'.

exploit

  • The list-comprehension form allows a single expression acceptable to rl_safe_eval.
  • The trailing and 'red' returns a valid CSS color so the rendering doesnt break.
  • Replace the command as needed; use ping to validate execution with tcpdump.

Operational workflow

  1. Identify PDF generator
    • PDF Producer shows xhtml2pdf; HTTP response contains ReportLab comment.
  2. Find an input reflected into the PDF (e.g., profile bio/description) and trigger an export.
  3. Verify execution with low-noise ICMP
    • Run: sudo tcpdump -ni icmp
    • Payload: ... system('ping <your_ip>') ...
    • Windows often sends exactly four echo requests by default.
  4. Establish a shell
    • For Windows, a reliable two-stage approach avoids quoting/encoding issues:
      • Stage 1 (download):

exploit

 - Stage 2 (execute):

exploit

  • For Linux targets, similar two-stage with curl/wget is possible:

Notes and tips

  • Attribute contexts: color is a known evaluated attribute; other attributes in ReportLab markup may also evaluate expressions. If one location is sanitized, try others rendered into the PDF flow (different fields, table styles, etc.).
  • Quoting: Keep commands compact. Two-stage downloads drastically reduce quoting and escaping headaches.
  • Reliability: If exports are cached or queued, slightly vary the payload (e.g., random path or query) to avoid hitting caches.

Mitigations and detection

  • Upgrade ReportLab to 3.6.13 or later (CVE-2023-33733 fixed). Track security advisories in distro packages as well.
  • Do not feed user-controlled HTML/markup directly into xhtml2pdf/ReportLab without strict sanitization. Remove/deny [...] evaluation constructs and vendor-specific tags when input is untrusted.
  • Consider disabling or wrapping rl_safe_eval usage entirely for untrusted inputs.
  • Monitor for suspicious outbound connections during PDF generation (e.g., ICMP/HTTP from app servers when exporting documents).

References

{{#include ../../../banners/hacktricks-training.md}}