|
Task Description
Vulnerability Title: LaTeX Injection via Billing Invoice Annotation Allows Server-Side Arbitrary File Read
Severity: Critical (CVSS 9.1)
Affected Endpoint: POST https://admin.alwaysdata.com/billing/annotate/
Summary
The billing annotation feature allows authenticated users to add text notes to their invoice transactions. This annotation is rendered into a PDF invoice using xelatex without any input sanitization or LaTeX command escaping. An attacker is able to inject LaTeX commands (such as \input{/path/to/file}) into the annotation field, causing the server to read arbitrary files accessible to the www-data user and embed their contents into the generated PDF.
Steps to Reproduce
1. Log into https://admin.alwaysdata.com with a valid account. 2. Navigate to Billing in the sidebar. 3. Ensure you have at least one transaction (add credit if needed to generate one). 4. Open the transaction and add an annotation with the following payload:
\input{/etc/hostname} 5. Save the annotation. 6. Download the invoice PDF for that transaction. 7. Open the PDF — the server's hostname (overlord-core) appears embedded in the invoice text, confirming server-side file read.
To read files containing special characters (underscores, hashes, dollar signs), use:
{\catcode`\_=12\catcode`\^=12\catcode`\#=12\catcode`\$=12\catcode`\%=12\input{/etc/passwd}}
This disables LaTeX's special character handling and includes the raw file content in the PDF.
Confirmed file reads: - /etc/hostname → overlord-core - /etc/machine-id → c23a410ea94a4695a56726c80307ad0e - /etc/debian_version → 12.14 - /etc/passwd → all 42 lines of system accounts
And many more
Impact
- Arbitrary file read on the central management server (overlord-core) as www-data. - Attacker can read Django application source code, configuration files, and potentially credentials stored under /data/www/production/. - Any authenticated user (including free-tier accounts) can exploit this — no special privileges required. - The annotation field has a 255-character limit, but \input{/path} payloads are short enough to fit easily.
Root Cause
In the invoice PDF template (invoice_pdf.tex), the annotation is rendered with autoescape disabled and no LaTeX-specific escaping:
{% autoescape off %} transaction.annotations.first
The PDFAnnotationForm accepts any text in the annotation CharField with no validation or filtering of LaTeX commands. When _generate_pdf() is called, it runs xelatex -halt-on-error on the template, executing any LaTeX commands present in the annotation.
Remediation
1. Sanitize annotation input — strip or escape LaTeX special characters and commands (\, {, }, $, #, ^, _, %, ~) before saving the annotation. A whitelist approach (allow only alphanumeric + basic punctuation) is safest. 2. Use a LaTeX escape filter in the template — replace transaction.annotations.first with a custom |latex_escape template filter that escapes all LaTeX control characters. 3. Sandbox xelatex — run PDF generation with –no-shell-escape and disable \input/\include/\read/\openin commands via a restricted TeX configuration or by using –shell-restricted mode. 4. Run xelatex in a container with no access to sensitive files — mount only the template directory, not the entire filesystem.
Video Proof of concept is attached
|