- Application/component: JetBrains TeamCity
- Affected version: < 2025.07
- Score (CVSS 3.1): 7.7 High
- Vector: CVSS:3.1/AV:L/AC:L/PR:H/UI:R/S:C/C:H/I:H/A:L
- CWEs:CWE-23: Relative Path Traversal
During a more detailed analysis of JetBrains TeamCity, I identified a vulnerability.
Description
JetBrains TeamCity allows additional plugins to be installed from ZIP files. While the upload mechanism on Linux and macOS correctly protects against conventional Zip Slip attacks, a critical vulnerability exists on Windows: specially crafted file paths containing backslashes (..\) allow an Arbitrary File Write outside the plugin directory, including the ability to overwrite existing files.
The result is Remote Code Execution on the TeamCity host, without the plugin having to be enabled. Uploading it is sufficient.
This article explains the vulnerability, the technical background, and the Proof-of-Concept attack path.
Technical background
TeamCity recursively extracts uploaded plugin ZIP files on the server. To prevent Directory Traversal, TeamCity filters typical Unix path sequences such as:
../
/../
These filters work reliably on Linux and macOS.
On Windows, however, paths are interpreted using backslashes (\). This is where the problem lies:
..\..\..\webapps\ROOT\404.jsp
The current filtering logic does not detect this path, but the Windows file system API correctly interprets it as traversal during extraction.
An attacker can therefore write or overwrite files anywhere outside the plugin directory, provided that the TeamCity process has write permission.
Impact
- Arbitrary File Write on the host system
- Existing files can be overwritten
- No file extension restrictions (.jsp, .exe, .bat, .lnk, …)
- The plugin does not need to be enabled
- If TeamCity runs as SYSTEM on Windows, full system compromise is possible (JetBrains explicitly recommends against this!)
A particularly straightforward attack path is to overwrite a JSP file in the TeamCity web root and place a webshell.
Proof of Concept – attack chain
1. Creating the malicious ZIP file
The ZIP file must be created on Linux. Python on Windows normalizes backslashes to forward slashes, which prevents the exploit from working there.
Example entry in the ZIP:
..\..\..\webapps\ROOT\404.jsp
During extraction, the file is placed directly in the TeamCity web directory. In 7-Zip, it looks like this:

2. Uploading the plugin
The ZIP file is uploaded normally through “Upload plugin zip” in the TeamCity interface.

Important: The plugin does not need to be enabled. Extraction occurs as part of the upload.
3. Restarting TeamCity
After the server restarts, the manipulated file is loaded.

In my PoC, the default 404.jsp was overwritten.
4. Triggering the webshell
Requesting a nonexistent page executes the new 404.jsp—in my case, a simple JSP webshell.
This achieves Remote Code Execution on the host.

Exploit stealth
An attacker can discreetly integrate the payload into a legitimate plugin, such as a well-known community plugin. Because the ZIP file is extracted recursively, a single manipulated path entry anywhere in the archive is sufficient.
An administrator would not expect a plugin upload to modify the system directly, which makes this attack technique particularly deceptive.
Root Cause
The filtering logic validates only Unix path separators (/).
Windows path separators (\) are not normalized or checked correctly.
Mitigation
- Normalize backslashes to forward slashes before validating paths
- Restrict extracted paths to the destination directory
- Use secure ZIP extractors with canonical path checks
Because the vulnerability affects Windows exclusively, Linux and macOS are unaffected.
Note
All tests were performed on my own local instance and with test user data that I created myself.
JetBrains responded quickly, constructively, and professionally.
Timeline
- Reported to JetBrains on June 13, 2025
- Response received on June 13, 2025
- Issue fixed on July 7, 2025
- Patch released on July 20, 2025
- CVE entry published as CVE-2025-54531 on July 28, 2025
- Blog post with details released on January 25, 2026