TeamCity agent-polling protocol authority validation¶
CISA added CVE-2026-63077 to the Known Exploited Vulnerabilities catalog on August 5, 2026. JetBrains describes the affected TeamCity boundary as unauthenticated remote code execution through the agent polling protocol before 2026.1.3 and 2025.11.7; the public record classifies the flaw as deserialization of untrusted data. This yields a durable CI-control-plane test: determine whether an unauthenticated network peer can reach object decoding on a server endpoint intended for build-agent coordination.
Primary records:
Route evidence first; inert lab decoding only
Do not send serialized exploit objects to an operational TeamCity server. On customer-approved production surfaces, stop at ownership, version, protocol reachability, and authentication-gating evidence. Exercise decoding only in a disposable TeamCity lab with a patched deserializer that accepts an inert marker and denies class loading, process creation, file access, and outbound network access.
1. Establish scope and product identity¶
Record every approved TeamCity authority before probing:
| Field | Evidence |
|---|---|
| authority | scheme, host, port, reverse-proxy path |
| deployment | on-premises server, test replica, or disposable lab |
| version | authenticated About page, package/image metadata, or administrator-provided build number |
| agent boundary | agent listener/polling authority and whether a proxy or load balancer fronts it |
| network position | internet-facing, partner/VPN-facing, internal, or isolated agent segment |
| approved depth | passive/version only, route/auth check, or lab deserializer instrumentation |
Do not infer TeamCity from a generic login page or favicon alone. Preserve at least two product signals, such as an administrator-confirmed asset plus TeamCity-specific headers, HTML, package metadata, or server logs.
Treat the fixed-version branches exactly as published. A 2025.11.x deployment must be compared with 2025.11.7; a 2026.1.x deployment must be compared with 2026.1.3. Do not flatten these into a guessed single semantic-version range, and do not infer that an undisclosed older or future branch is affected without a primary vendor statement.
2. Discover the polling boundary without guessing exploit routes¶
The public records identify the agent polling protocol, not a request path or wire payload. Do not invent an endpoint from third-party exploit snippets. Derive the real route and authority from one of these approved sources:
- a disposable agent registering with a disposable server while an intercepting recorder captures destination authority and request metadata;
- TeamCity server access logs for a synthetic lab agent;
- reverse-proxy configuration supplied by the owner;
- authenticated server or agent diagnostics with secrets redacted; or
- source/patch review for the exact deployed branch.
Record method, normalized path, content type, framing, connection reuse, and the first server handler reached. Redact agent tokens, cookies, authorization fields, build metadata, repository URLs, and environment variables. Do not replay a captured production agent credential.
This discovery step should produce a route map, not a payload:
external authority
-> reverse proxy / load balancer
-> TeamCity agent-polling route
-> authentication or agent-identity gate
-> parser / object decoder
-> dispatch handler
3. Build an authentication and parser-reachability matrix¶
Use an empty request or a protocol-valid, non-serialized control generated by the same disposable lab agent. Compare:
| Case | Credential state | Expected evidence |
|---|---|---|
| ordinary negative route | none | normal not-found behavior |
| polling route | none | rejected before parser/decoder |
| polling route | malformed synthetic credential | same pre-parser rejection |
| polling route | disposable valid lab agent | normal polling response |
| fixed lab build | none | rejection before decoder |
Capture status, response length/hash, redirect chain, connection behavior, server request ID, and the earliest handler observed in lab instrumentation. Avoid high-rate scanning: one negative control and one polling control per approved authority are enough to establish gating.
A route returning a TeamCity-shaped error is not by itself deserialization reachability. The relevant question is whether an unauthenticated request crosses the identity gate and reaches the decoder.
4. Instrument a denied deserializer in a disposable lab¶
Only when the assessment authorizes exploit-path validation, clone the affected architecture into an isolated lab with no production projects, agents, credentials, plugins, or network access. Patch or hook immediately before object decoding and replace the real decoder with a recorder that logs:
- request authentication state;
- route and protocol message type;
- content length and framing;
- selected decoder or serializer family;
- requested top-level type identifier, if one exists;
- whether dispatch would proceed; and
- a random inert marker hash.
The fixture must deny class resolution, constructors, callbacks, filesystem operations, process APIs, reflection-driven side effects, and egress. Generate the marker locally; do not use a gadget chain or a public RCE payload.
Run affected and fixed builds with the same recorder and controls. A bounded positive is:
unauthenticated synthetic poll
-> real polling route selected
-> authentication gate not enforced before decoding
-> patched decoder receives inert marker
-> fixed build rejects before that recorder
That proves the exposed deserialization boundary without code execution. If the affected build rejects before the recorder, do not report RCE based only on its version or the KEV entry.
5. Separate protocol exposure from application impact¶
Report the strongest observed boundary and no more:
| Evidence | Supported claim |
|---|---|
| affected version only | potentially affected deployment |
| polling route reachable without authentication | exposed agent control-plane surface |
| unauthenticated request reaches patched decoder | unauthenticated deserialization boundary |
| inert marker selects a denied type/dispatch path | attacker influence over decoder/dispatch selection |
| vendor advisory plus KEV status | public exploitation context, not local compromise proof |
Do not claim local compromise, build takeover, source-code access, secret theft, or arbitrary command execution unless separately proven under explicit lab authorization. KEV inclusion establishes known exploitation of the vulnerability in the wild; it does not prove that the assessed server was exploited.
October 6 follow-up: unvalidated Git submodule URL → server-side local repository read (CVE-2026-106219 / GHSA-j4qw-7f5f-rqvw, medium, TeamCity <2026.2.1)¶
TeamCity accepted Git submodule URLs without validation, letting a configured VCS root make the server read local repositories on the server — a project-write permission converts into cross-project source disclosure (every other repo checked out on that agent/server is a readable target).
- Durable axis: Git URL fields are SSRF with a
file:///local-path dialect. Thegit submodule add /srv/teamcity/.gitorext::shapes are the same config-supplied-URL → subprocess family as the Giteapicture-claim bare-http.Getleg and the June 17 repo-trust rules: when a build platform runs realgiton server-supplied config, testfile://, relative, andext::transport URLs against a marker repo outside your project, and check what a failed clone's error text reflects (path disclosure oracle on shared agents). - CI recon note for authorized engagements: a TeamCity/Bamboo/GitLab runner with per-project VCS configuration is a lateral source-read pivot — enumerate every URL-consuming field (repo URL, submodule URL, credentials-plugin refs, patch URLs) with a local-path canary before assuming network egress is the only outbound channel.
- Same wave's TeamCity Kotlin DSL sandbox escape (CVE-2026-106218 / GHSA-p8v4-6cfg-7hh7, 8.8, <2026.1.3 / 2025.11.7): DSL config code reaching server-side RCE confirms this page's standing claim that build configuration code is a code-execution surface with server identity — if you can edit Kotlin DSL config (a low-tier grant), assume server-level code execution until proven otherwise; DSL sandbox escapes recur across versions (adjacent to CVE-2026-63077's agent-side surface above). Prove only in a lab TeamCity with an inert marker (
touchin a temp dir), never on a shared CI.
Evidence bundle¶
Preserve:
TeamCity authority and deployment class:
Version source and branch comparison:
Polling route derivation source:
Reverse-proxy-to-origin mapping:
Unauthenticated control response hash:
Disposable-agent control response hash:
Earliest handler reached:
Affected-build recorder result:
Fixed-build recorder result:
Denied sink list:
Observed claim:
Excluded stronger claims:
Keep credentials and CI metadata out of screenshots and reports. Hash synthetic bodies where full content is unnecessary, and include the exact lab build/image digest so the parser result can be replayed.