TeamCity Vulnerability: CVE-2023-42793 and CVE-2024-27198
TeamCity vulnerability guide: CVE-2023-42793, CVE-2024-27198, CVE-2024-27199 and CVE-2026-63077, and how to keep the build server off the internet.
On this page
- The TeamCity vulnerability timeline
- Why build servers get ransomed
- Why exposure is the risk multiplier
- Expose the webhook, not the server
- What TeamCity expects: the commit-hook endpoint
- Step by step with Webhook Relay
- The simpler alternative: let TeamCity poll
- Hardening that still matters
- Honest limits: when the relay does not help
- More self-hosted tools that are public only for webhooks
TeamCity On-Premises has had four unauthenticated or near-unauthenticated vulnerabilities that attackers exploited in the wild, and every one of them needs only network reach to the server. The most reliable fix besides patching is to stop exposing the server. Most TeamCity servers are public for one reason, receiving VCS webhooks, which an outbound relay can deliver without any inbound port.
This guide covers CVE-2023-42793, CVE-2024-27198, CVE-2024-27199 and CVE-2026-63077, then shows how to keep the server private and still trigger builds.
The TeamCity vulnerability timeline
Our Threat Watch records for these four are below. "Auth needed" means authentication before the attacker acts; none of these needs an account.
| CVE | What an attacker gets | Auth needed | Exploited in the wild | Fixed in |
|---|---|---|---|---|
| CVE-2023-42793, authentication bypass to RCE (CVSS 9.8) | Skip authentication, run code on the server, take administrative control | None | Yes. Microsoft attributed attacks to North Korean groups (Diamond Sleet, Onyx Sleet); CISA and partners attributed large-scale exploitation to Russia's SVR; CISA flags ransomware use | 2023.05.4 |
| CVE-2024-27198, authentication bypass to admin (CVSS 9.8) | Administrator actions such as creating admin users or tokens, server takeover | None | Yes. Mass exploitation reported from 2024-03-06; JetBrains reported ransomware encryption at two customers; CISA flags ransomware use | 2023.11.4 |
| CVE-2024-27199, path traversal (CVSS 7.3) | A limited set of protected pages and servlets: information disclosure and some changes such as replacing the HTTPS certificate | None | CISA KEV listing (added 2026-04-20) with a ransomware flag; no separate public report naming actors | 2023.11.4 |
| CVE-2026-63077, unauthenticated RCE via the agent polling protocol (CVSS 9.8) | Run operating system commands as the server process, exposing configuration, stored credentials and build pipelines | None | Yes. CISA KEV on 2026-08-05; JetBrains reported active exploitation on 2026-08-07 | 2025.11.7 and 2026.1.3 |
Affected versions: all On-Premises versions before 2023.05.4 for the first, through 2023.11.3 for the next two, and all versions before the listed fixes for the last. TeamCity Cloud was not affected by CVE-2023-42793 and needs no action for CVE-2026-63077. Vendor advisories: JetBrains on CVE-2023-42793, on CVE-2024-27198 and CVE-2024-27199, and on CVE-2026-63077.
Why build servers get ransomed
A build server holds the things an attacker wants: source code, deployment credentials, signing certificates and a mechanism that runs code on other machines. Administrative control of TeamCity is therefore close to administrative control of everything it builds and deploys.
The table shows the pattern. The flaws sit in authentication handling, in the server's request routing and in the agent protocol. They are reachable by anything that can open a TCP connection to the server. For CVE-2024-27198, GuidePoint suspected BianLian ransomware but could not say whether it or CVE-2023-42793 was the entry point. The common factor is not one bug, it is that the server was reachable.
Why exposure is the risk multiplier
Patching is mandatory, but the window between a fix and your upgrade is where mass exploitation happens. For CVE-2024-27198 the fix and technical details were published on 2024-03-04 and mass exploitation was reported two days later. A server that is not reachable from the internet cannot be hit during that window by an anonymous scanner.
So the question is why the server is public. For most teams the honest answer is that GitHub or GitLab must send a push event. That is a narrow requirement served by an entire public web application: login page, REST API, every plugin's endpoints and any pre-auth bug in the version you run. The same dynamic is covered for Jenkins in Securing Jenkins.
Expose the webhook, not the server
| Reachable from the internet | Should stay private |
|---|---|
| One public HTTPS input URL on the relay, accepting the VCS provider's deliveries | The TeamCity web UI and login |
The whole /app/rest/ API | |
| Plugin endpoints and the agent polling endpoint | |
| The server's own HTTPS port |
With Webhook Relay, the agent connects out from your network. The provider posts to the relay's public URL, and the relay streams the event down the agent's connection to an address only the agent can reach. TeamCity needs no public IP and no inbound firewall rule.
What TeamCity expects: the commit-hook endpoint
JetBrains documents the recommended commit-hook shape in Configuring VCS post-commit hooks for TeamCity. A hook sends a POST to:
<TeamCityServerURL>/app/rest/vcs-root-instances/commitHookNotification?locator=<vcsRootInstancesLocator>
Per that page:
- The request does not start builds. It makes TeamCity check the matching VCS roots for new changes, and your VCS triggers then start the builds.
- The locator should identify specific VCS root instances. JetBrains recommends matching on the repository URL through a nested
property:(name:url,value:...,matchType:equals,ignoreCase:true)locator, with colons and slashes in the URL escaped. - The request must authenticate. JetBrains lists a username with a personal access token, and its generic script sends
Authorization: Bearer <access-token>. The user needs the "View project and all parent projects" and "View build configuration settings" permissions on the projects that use the VCS root.
GitHub's push webhook does not carry a TeamCity locator or a TeamCity access token, so a function between the relay and TeamCity builds that request.
Step by step with Webhook Relay
This works for TeamCity when the relay agent runs on a host that can reach the server, for example http://teamcity.internal:8111. Replace the hostname, bucket name and function name with yours.
1. Create a dedicated TeamCity user and token. Give it only the two view permissions listed above, create a personal access token for it, and store that token as a secret connection in Webhook Relay. Do not use an administrator's token.
2. Install the agent next to TeamCity and log in using the CLI installation steps:
curl https://my.webhookrelay.com/webhookrelay/downloads/install-cli.sh | bash
relay login -k YOUR_TOKEN_KEY -s YOUR_TOKEN_SECRET
On a container host you can run the Docker agent instead, with RELAY_KEY, RELAY_SECRET and BUCKETS set.
3. Create a function that turns a GitHub delivery into the TeamCity call, using the request methods from the modify request page. Verify the GitHub HMAC signature first, as shown in the filter webhooks page, then rewrite the request:
const token = require("secret:teamcity-token")
// 1. Verify GitHub's X-Hub-Signature-256 over the raw body here (see the
// signature guide), and call r.stopForwarding() on mismatch.
// 2. Only pushes should prompt a check for changes
const eventType = r.headers["X-Github-Event"] || r.headers["x-github-event"]
if (eventType !== "push") {
r.stopForwarding()
return
}
// 3. Rewrite the request into TeamCity's commit-hook call
r.setMethod("POST")
r.setPath("/app/rest/vcs-root-instances/commitHookNotification")
r.setRawQuery("locator=<your escaped VCS root instance locator>")
r.setHeader("Authorization", "Bearer " + token)
Header, method, path and query setting are documented functions on the modify-request page; the locator value is yours to fill in following JetBrains' syntax. Read the final URL in the bucket logs to confirm the path is exactly the endpoint above. Keep the r.setPath line: the output points at TeamCity's base address, so the function setting the path on every delivery (and stopping everything that is not a push) is what prevents a sender from appending /app/rest/... or any other TeamCity path to the input URL. Never attach this output without the function.
4. Forward to TeamCity's base address with the function attached to the output:
relay forward --bucket teamcity-hooks --function teamcity-commit-hook http://teamcity.internal:8111
The command creates the bucket if needed and prints its public input URL. --function attaches a function to the output (flag reference). Use relay service install and relay service start, or the Docker agent, to keep it running.
5. Point GitHub at the printed URL with the push event, and set the same secret you verify in step 3. A successful call returns text like "Scheduled checking for changes for 1 VCS roots", per JetBrains' troubleshooting section. See the broader webhooks in CI/CD guide for the pattern.
6. Close the public port. Remove the public DNS name, port forward or load-balancer rule for TeamCity, and reach the UI over a VPN.
The simpler alternative: let TeamCity poll
JetBrains states that TeamCity periodically polls remote repositories targeted by each active VCS root to detect changes, and positions hooks as a way to cut polling load and speed up detection. If you can accept polling latency and your VCS root count is modest, you need no webhook and no inbound access. The relay path earns its place when you have many roots or need prompt builds.
Hardening that still matters
- Patch. Upgrade to 2023.05.4, 2023.11.4, 2025.11.7 or 2026.1.3 or later. Patch plugins fix only the CVEs JetBrains lists for them.
- Assume compromise if it was exposed and unpatched. Check for unexpected admin users and tokens, new plugins or build steps and scheduled tasks. Rotate every credential and signing key reachable from TeamCity. For CVE-2026-63077, JetBrains suggests searching logs for
com.thoughtworks.xstream.converters.ConversionExceptionand reviewing unauthorized agents, especially names starting withscan. - Authentication. Use a least-privilege token for the hook. See webhook security best practices.
- Signature verification. The relay changes where traffic enters, not whether to trust it. Verify GitHub's signature in the function, before forwarding, so unsigned requests get a 401 at the relay and never reach TeamCity; see how to verify a webhook signature.
- Filtering. Drop events that should not cause a check, such as everything except pushes.
- Least privilege. Run the server with minimum OS privileges, on a host separate from agents.
Honest limits: when the relay does not help
- Agents connect from outside. CVE-2026-63077 is in the agent polling protocol. The relay only helps if your build agents sit on the same private network as the server or reach it over a VPN. If agents must connect from the internet, patch and restrict access instead.
- The webhook path itself is vulnerable. Anything the relay forwards still reaches TeamCity. Verifying the signature at the relay limits that to requests GitHub actually signed, so an anonymous attacker cannot reach the handler. A flaw triggered by content inside a genuine delivery would still get through.
- People need the UI from outside. Use a VPN or identity-aware proxy; a webhook relay does not publish your UI.
- It does not replace patching. An internal attacker or a compromised agent can still use these flaws against an unpatched server.
More self-hosted tools that are public only for webhooks
- Jenkins vulnerabilities exploited in the wild
- n8n security after CVE-2025-68613
- Langflow vulnerabilities, from CVE-2025-3248 on
- Kestra security and CVE-2026-49869
For hosted deliveries to a private CI server in general, see receiving webhooks on localhost. To close the port today, create a free account.
