CVE-2026-63077: Verified Reproduction
CVE-2026-63077: JetBrains TeamCity On-Premises unauthenticated RCE via agent polling protocol
CVE-2026-63077 is verified against the affected target. Vulnerability class: RCE. This critical reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00329.
What Is CVE-2026-63077?
CVE-2026-63077 is a critical-severity RCE vulnerability. Pruva has independently reproduced it and publishes a verified, runnable proof-of-concept (reproduction REPRO-2026-00329).
CVE-2026-63077 Severity
CVE-2026-63077 is rated critical severity.
Critical — the most severe class — typically remotely exploitable with severe impact. Treat as an emergency.
How to Reproduce CVE-2026-63077
pruva-verify REPRO-2026-00329 curl -O https://www.pruva.dev/api/v1/reproductions/REPRO-2026-00329/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-63077
- reached the target end-to-end
- full exploit chain demonstrated
- on the real production code path
- high confidence
- the upstream fix blocks the same trigger
HTTP bodies of POST /app/agents/v1/register (agentDetails XML) and POST /app/agents/v1/commands/error (XStream gadget XML), plus TeamCity-AgentSessionId/TeamCity-AgentCommandId headers
- POST /app/agents/v1/register (unauthenticated session issuance)
- POST /app/agents/v1/commands/error
- Error.fromXml
- unrestricted XStream deserialization
- HSQLMetadataStorage$SchemaMismatchException/BasicDataSource/HashAdapter/TiedMapEntry gadget
- HSQLDB SCRIPT drops .jspws webshell
- GET /<rand>.jspws
- Runtime.exec as tcuser
reproduction_steps.sh How the agent worked
Root Cause and Exploit Chain for CVE-2026-63077
JetBrains TeamCity On-Premises is vulnerable to unauthenticated remote code execution
(CWE-502, deserialization of untrusted data) in its agent polling protocol. The
server-side handler jetbrains.buildServer.agentServer.polling.Error.fromXml() (and the
sibling XStreamHolders in PollingRemoteAgentConnection, RunBuildCommandResult, and
NodesAwareLogMessagePersister) deserializes attacker-controlled HTTP request bodies with
an XStream instance configured with AnyTypePermission.ANY and only a small denylist.
An unauthenticated attacker first registers a synthetic build agent via
POST /app/agents/v1/register (which issues a valid TeamCity-AgentSessionId without any
credentials), then posts a crafted XStream XML document to
POST /app/agents/v1/commands/error. The embedded gadget chain starts an HSQLDB
connection whose connectionInitSqls drop a self-deleting .jspws webshell into the
TeamCity webroot; a single GET to that file executes an arbitrary OS command with the
privileges of the TeamCity server process.
- Package/component: JetBrains TeamCity On-Premises server (
webapps/ROOTwebapp, classes inserver-core.jar,common-impl.jar,messages.jar,web-core.jar). - Affected versions: all TeamCity On-Premises versions before 2025.11.7 / 2026.1.3 (verified vulnerable: 2025.11.6, build 208214; verified fixed: 2025.11.7).
- Risk: CVSS 3.1 9.8 Critical (AV:N/AC:L/PR:N/UI:N). Listed in CISA KEV (added 2026-08-05) with confirmed in-the-wild exploitation. Full server compromise: arbitrary OS command execution as the TeamCity server user, access to build secrets, source code, CI/CD pipeline integrity.
Impact Parity
- Disclosed/claimed maximum impact: unauthenticated remote code execution.
- Reproduced impact from this run: unauthenticated remote OS command execution
(
touch <marker>executed astcuser, the TeamCity server process user, inside the officialjetbrains/teamcity-server:2025.11.6-linuxcontainer), proven by the command-created marker file and by the one-shot JSPWS response token. - Parity: full.
- Not demonstrated: nothing material — the claim is unauthenticated RCE and exactly that was demonstrated, twice, through the real HTTP surface.
Root Cause
The agent polling protocol is served by
jetbrains.buildServer.controllers.agentServer.AgentPollingProtocolController
(web-core.jar), reachable under /app/agents/v1/... with no servlet-level
authentication: agent identity is established only by the TeamCity-AgentSessionId
header (<agentId>:<authorizationToken>), and a fresh valid session is handed out by the
unauthenticated register action to any caller
(createRegisteredAgentWithPollingConnection → registerAgent → session id in the
TeamCity-AgentSessionId response header).
For the commands/error sub-path, AbstractAgentCommandsRequestsProcessor. handleCommandIsFailedRequest executes:
Error error = Error.fromXml(StreamUtil.readTextFrom(request.getReader())); // <- sink
int n = Integer.parseInt(request.getHeader("TeamCity-AgentCommandId"));
Error.fromXml → XStreamWrapper.deserializeObject(xml, ourXStreamHolder).
jetbrains.buildServer.messages.XStreamHolder (messages.jar) configures its XStream as:
xstream.addPermission(AnyTypePermission.ANY);
xstream.denyTypes(new String[]{ "java.beans.EventHandler", "java.lang.ProcessBuilder",
"javax.imageio.ImageIO$ContainsFilter", "jdk.nashorn.internal.objects.NativeString",
"com.sun.corba.se.impl.activation.ServerTableEntry",
"com.sun.tools.javac.processing.JavacProcessingEnvironment$NameProcessIterator",
"sun.awt.datatransfer.DataTransferer$IndexOrderComparator", "sun.swing.SwingLazyValue"});
xstream.denyTypesByRegExp(/* LazyIterator, LazyEnumeration, GetterSetterReflection,
PrivilegedGetter, java.rmi, javax.crypto, ServiceNameIterator, JavaFX, BCEL */);
i.e. an "allow everything except a 2016-era blacklist" configuration. Bundled libraries (commons-collections 3.2.2, freemarker 2.3.31, commons-dbcp2/pool2, hsqldb, plus TeamCity's own classes) provide all the gadget classes needed for code execution.
The exploit gadget chain (identical to the in-the-wild chain captured by honeypots and documented by Rapid7):
linked-hash-mapentry value typed as TeamCity's ownjetbrains.buildServer.serverSide.metadata.impl.metadata.HSQLMetadataStorage$SchemaMismatchException(aThrowable, so it passes XStream 1.4.20's default hierarchy permission). Its declared fields instantiateHSQLStoragewith a DBCP2BasicDataSourcewhosedriverClassName=org.hsqldb.jdbc.JDBCDriver,url=jdbc:hsqldb:mem:<rand>, and three attacker-controlledconnectionInitSqls.- A
freemarker.ext.beans.HashAdapterwhosefalseModel.objectis an XStreamreference=to thatBasicDataSource, giving aMapview whoseget("connection")invokesBasicDataSource.getConnection()via FreeMarker bean introspection. - A
setcontainingorg.apache.commons.collections.keyvalue.TiedMapEntry(not covered by commons-collections 3.2.2'sreadObjectserialization guard) bound to that map with key"connection". DuringHashSetpopulation,TiedMapEntry.hashCode()→getValue()→map.get("connection")→BasicDataSource.getConnection()→ DBCP runs the three init SQL statements against the in-memory HSQLDB:CREATE TABLE,INSERT '<JSP scriptlet>', andSCRIPT '../webapps/ROOT/<rand>.jspws', which writes a polyglot SQL/JSP webshell into the TeamCity webroot. GET /<rand>.jspwscompiles and runs the scriptlet, which deletes itself and callsjava.lang.Runtime.getRuntime().exec(<attacker command>), printing a per-run token.
Fix (confirmed by decompiling the official fix_CVE_2026_63077.zip security patch plugin,
build limit max-build="222648"): the patch reflectively replaces every
XStreamHolder used by the polling protocol (PollingRemoteAgentConnection.myXStreamHolder,
Error.xStreamHolder, RunBuildCommandResult.ourXStreamHolder,
NodesAwareLogMessagePersister.xStreamHolder) with a wrapper whose getXStream() adds
NoTypePermission.NONE plus an explicit allowlist of ~100 jetbrains.buildServer.* data
classes. It also installs an AddToQueuePreprocessor that strips queued builds carrying
the teamcity.agent.internal.passwords.values parameter. Fixed releases 2025.11.7 /
2026.1.3 ship the same allowlist natively.
Reproduction Steps
bundle/repro/reproduction_steps.sh(self-contained; requires docker, python3, curl).- The script:
- pulls the pinned official images
jetbrains/teamcity-server@sha256:a435d8…4176(2025.11.6, vulnerable) and…@sha256:d3875b…56d8(2025.11.7, fixed); - starts both servers and drives the real first-run setup wizard over HTTP
(
/mnt/do/goNewInstallation→/mnt/do/goNewDatabase(internal HSQLDB) →/mnt/do/acceptLicenseAgreement) until the server leaves maintenance mode; - health-checks the attack surface by registering an agent without credentials and
verifying a
TeamCity-AgentSessionIdheader is issued; - runs the exploit (
bundle/repro/exploit_cve_2026_63077.py, vendored Rapid7 PoC) twice against the vulnerable server and twice against the fixed server, with per-run random markers; - requires, on the vulnerable server: exploit exit 0 and the marker file present inside the container (created by the TeamCity server process);
- requires, on the fixed server: exploit failure, no marker file, and
com.thoughtworks.xstream.security.ForbiddenClassExceptionin the server log (the exact IoC JetBrains names for a blocked exploit attempt).
- pulls the pinned official images
- Expected evidence:
[+] Command executed: touch /tmp/CVE_2026_63077_PWNED_<rand>for 2025.11.6,HTTP 404for the webshell on 2025.11.7, andRESULT: … CONFIRMED.
Evidence
bundle/logs/reproduction_steps.log— full orchestration log.bundle/logs/exploit_vulnerable.log— two successful exploit runs: register →TeamCity-AgentSessionId: <id>:<token>→/app/agents/v1/commands/errorHTTP 500 (deserialization side effects already committed) →GET /<rand>.jspwsHTTP 200 with the per-run response token.bundle/repro/marker_vulnerable.txt—ls -laof the marker file (ownertcuser) andidof the server process user inside the container.bundle/logs/teamcity_vuln_server.log— vulnerable server log containing thecom.thoughtworks.xstream.converters.ConversionExceptionIoC named in JetBrains' guidance.bundle/logs/exploit_fixed.log,bundle/logs/teamcity_fixed_server.log— fixed server: same requests,ForbiddenClassException×2, webshell GET → HTTP 404, no marker.bundle/repro/payload_vulnerable.xml— the exact attack XML generated for the run.bundle/repro/analysis/— patch-diff evidence: decompiled JetBrains security patch plugin classes, decoded allowlist, decompiledError/AgentPollingProtocolController/AbstractAgentCommandsRequestsProcessor/XStreamHolderfrom 2025.11.6, and the in-the-wild honeypot pcap (CVE-2026-63077-itw.pcap, BoredHackerBlog) showing the identical request sequence.- Environment: official Docker images on linux/amd64; TeamCity 2025.11.6 (build 208214) with bundled Tomcat 9.0.109 / JetBrains Runtime 21; no sanitizer, no instrumentation.
Recommendations / Next Steps
- Upgrade to TeamCity 2025.11.7 or 2026.1.3, or install JetBrains'
fix_CVE_2026_63077security patch plugin (2017.1+; restart required on 2017.1–2018.1). - Restrict network access to the server (the agent polling protocol is same-port HTTP(S)) to trusted build-agent networks.
- Detection: server logs containing
ConversionException(possible attempt/success) orForbiddenClassException(blocked attempt on patched servers); unexpected unauthorized agents (in-the-wild agents used names starting withscan); unexpected.jspws/.jspfiles underwebapps/ROOT. - The correct fix pattern is exactly what JetBrains shipped: never deserialize the polling
protocol with
AnyTypePermission.ANY; useNoTypePermission.NONE+ a strict allowlist.
Additional Notes
- Idempotency: the script recreates both containers from pinned image digests on every run and uses fresh random markers/tokens, so consecutive runs are independent.
- The exploit does not depend on the
TeamCity-AgentCommandIdvalue (deserialization happens before the header is parsed); any integer suffices. - On the vulnerable server the
/commands/errorrequest returns HTTP 500 after the gadget side effects have executed — the 500 is expected and matches the in-the-wild capture. - Exploit helper provenance:
bundle/repro/exploit_cve_2026_63077.pyis the public Rapid7 PoC (github.com/sfewer-r7/CVE-2026-63077), used unmodified; the same chain was independently captured in the wild (pcap inbundle/repro/analysis/). - The default
--webroot-relative ../webapps/ROOTis correct for the official Linux Docker image (JVM working directory/opt/teamcity/bin).
CVE-2026-63077 Reproduction Transcript
The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.
Full session Replay every step — scrub the timeline or play it back.
which docker; docker --version 2>/dev/null; java -version 2>&1 | head -3; python3 --version; df -h /workspace /tmp | tail -3; ls /pruva/project-cache 2>/dev/null/usr/bin/docker Docker version 29.1.3, build 29.1.3-0ubuntu4.1 pruva-command: 1: java: not found Python 3.14.4 Filesystem Size Used Avail Use% Mounted on tmpfs 2.4G 36K 2.3G 1% /workspace overlay 145G 33G 112G 23% / repo-mirrors
curl -s "https://hub.docker.com/v2/repositories/jetbrains/teamcity-server/tags/?name=2025.11&page_size=25" | jq -r '.results[].name' 2>/dev/null; echo ---; curl -s "https://hub.docker.com/v2/repositories/jetbrains/teamcity-server/tags/?name=2026.1&page_size=25" | jq -r '.results[].name' 2>/dev/null2025.11.7 2025.11.7-nanoserver-2022 2025.11.7-nanoserver-1809 2025.11.7-linux-arm64 2025.11.7-linux 2025.11.6 2025.11.6-nanoserver-1809 2025.11.6-nanoserver-2022
curl -sI https://download.jetbrains.com/teamcity/TeamCity-2025.11.6.tar.gz | head -8; echo ---; curl -sI https://download.jetbrains.com/teamcity/TeamCity-2025.11.7.tar.gz | head -8HTTP/2 302 content-type: text/html content-length: 138 location: https://download-cdn.jetbrains.com/teamcity/TeamCity-2025.11.6.tar.gz date: Sun, 23 Aug 2026 06:16:30 GMT x-geocode: NL server: nginx strict-transport-security: max-age=31536000; includeSubdomains;
Artifacts and Evidence for CVE-2026-63077
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix CVE-2026-63077
FAQ: CVE-2026-63077
Is CVE-2026-63077 exploitable?
How severe is CVE-2026-63077?
What type of vulnerability is CVE-2026-63077?
How can I reproduce CVE-2026-63077?
Is the CVE-2026-63077 reproduction verified?
References for CVE-2026-63077
Authoritative sources for CVE-2026-63077 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.