In partnership with

TL;DR: Anyone who can reach an unpatched TeamCity build server over HTTP runs commands on it, no credentials needed. JetBrains patched on July 27 and said it knew of no exploitation; CISA listed it as exploited on August 5, and a working exploit is public. Also inside: the npm and Actions defaults that changed under us this year, and the one n8n key that signs sessions as well as encrypting secrets.

Every setting we configured sits on top of a default we never saw.

Attack of the Week: the allowlist that only made the list longer

JetBrains, "Critical Security Issue Affecting TeamCity On-Premises (CVE-2026-63077)", Daniel Gallo [2026-07-27], reported by Antoni Tremblay. Technical analysis: Rapid7, Stephen Fewer [2026-08-07].

TeamCity is JetBrains' build server, the machine that compiles code and ships releases. Its build agents are the worker machines that run the jobs and report back to it over HTTP.

When an agent reports a failed command, the server takes the XML in that report and rebuilds it into Java objects. It uses a library called XStream to do that. Rebuilding objects out of XML a stranger sent you is a known route to code execution, so TeamCity gave XStream a list of classes it was allowed to build. That list is the allowlist.

XStream already had a list of its own.

What went wrong. Rapid7 puts it in one sentence: "TeamCity treats the configured classes as an allowlist, but XStream evaluates them alongside its earlier default permissions."

That built-in list is broad, and it has been a whitelist since 1.4.18: the Map, Map.Entry, Collection and Throwable hierarchies, among others. allowTypes() unions with what's there. Making an allowlist exclusive takes NoTypePermission.NONE first, which clears the list before anything is added. TeamCity 2026.1.2 never called it, though the patch does, behind a property that defaults to on.

Throwable stayed reachable, and that's the door.

Through the door. An HSQLMetadataStorage$SchemaMismatchException walks in, because it's a Throwable. It carries a BasicDataSource, which XStream rejects when the XML names it directly. It arrives anyway: XStream follows declared field types through the exception's synthetic outer-class reference and never checks the denied type again.

Then it fires. The attacker's SQL rides in connectionInitSqls on that datasource. FreeMarker's HashAdapter exposes it, TiedMapEntry.hashCode() fires when XStream repopulates the HashSet holding it, and that calls getConnection().

What lands. DBCP runs the SQL. HSQLDB's SCRIPT command writes a SQL/JSP polyglot, one file that reads as both, into the webroot with a .jspws extension, which routes to Jasper and compiles on request while dodging the auth that guards .jsp.

Two unauthenticated requests get us there: POST /app/agents/v1/register for a session ID, then the malicious XML to POST /app/agents/v1/commands/error carrying that session ID. A third, also unauthenticated, fetches the file.

What it costs. Per JetBrains' August 7 follow-up, exploitation "could expose TeamCity data, configurations, and stored credentials ... and potentially compromise the integrity of build artifacts and downstream CI/CD pipelines," depending on what the server process is allowed to do. In practice: the deploy credentials, and the signing keys if the server holds them.

CVSS 9.8, JetBrains as CNA, CWE-502. Scope is unchanged (S:U), so the 9.8 scores the server alone; the downstream reach is JetBrains' own assessment. Affected: everything below 2025.11.7, and the 2026.1 line below 2026.1.3. TeamCity Cloud needs nothing.

11 days. July 27, the advisory: "we are not aware of any active exploitation of this vulnerability." August 5, CISA adds it to KEV. August 7, Gallo posts again: "we have received reports of active exploitation, as well as attempted exploitation, targeting unpatched TeamCity servers." Rapid7 published the chain the same day, and Fewer's proof-of-concept takes the server URL as its only argument.

Ship This Week. Upgrade to 2025.11.7 or 2026.1.3. If we can't, the security patch plugin covers 2017.1 and later; older than that, upgrading is the only route.

Then put it behind a VPN or allowlisted ingress, which is JetBrains' own guidance. It costs us: cloud and contractor agents need a route in, inbound VCS webhooks stop, and the allowlist has to cover our own operators, because the web UI answers on that same listener.

Federal civilian agencies had a three-day clock under BOD 26-04, plus forensic triage. The rest of us get the reasoning free: CISA scored it automatable, total impact, already exploited. Patch first, then work out whether we also have an incident.

A five-stage infographic of CVE-2026-63077 in JetBrains TeamCity, headed by four figures: 9.8 CVSS, 3 HTTP requests, 0 credentials needed, 11 days to confirmed exploitation. Stage 1, an agent registers itself with no login, stopped by a VPN or allowlisted ingress. Stage 2, TeamCity's allowlist gets added to XStream's defaults rather than swapped for them, so the Throwable hierarchy stays open, stopped by the patch. Stage 3, one allowed error object hides a denied database connector carrying attacker SQL. Stage 4, rebuilding that object opens the database connection and the SQL runs, seen in teamcity-server.log. Stage 5, that SQL writes a web page into the webroot with a .jspws extension that skips the authentication guarding .jsp, seen in teamcity-javaLogging. A third request, also unauthenticated, runs the command as the server.

The chain, stage by stage, and what breaks each one.

Rule of the Week

Rapid7 published log samples alongside the chain, so we can tell a real attempt from routine noise. 15 minutes, from <TeamCity Server Home>/logs.

Did someone try? ConversionException alone is XStream's generic "couldn't read that XML" failure, and JetBrains only says it "may indicate an attempted or successful exploit." The cause underneath is the discriminator:

grep -F 'BasicDataSource wrapped into f.e.b.BooleanModel' teamcity-server.log

Did it land? The dropped webshell surfaces in teamcity-javaLogging-<date>.log as an HSQLDB error naming the file it wrote:

grep -F .jspws teamcity-javaLogging-*.log

The exploit deletes the file, so the log is all that survives. One honest limit: Rapid7's sample is HSQLDB refusing to overwrite, so treat a hit as confirmation and a miss as inconclusive.

Did the patch catch it? A patched server rejects the payload at deserialization and logs the class by name:

grep -F 'HSQLMetadataStorage$SchemaMismatchException' teamcity-server.log

Copy all three exactly. The -F keeps the dots literal, the single quotes keep the shell off that $, and the *.log glob has to stay bare. A hit on the third means the patch held.

Then the agents list. Registering an agent is step one of the exploit, so open Agents | Unauthorized agents and read it. JetBrains flags names beginning with scan, though a rename defeats that, and the date shown doesn't track the attempt. Scope from our log timestamps.

Do this week. Run the first grep. It tells us whether this is a patch window or an incident.

A defensive infographic for CVE-2026-63077 in two halves. Three control cards, each mapped to the attack stage it stops: patching to 2025.11.7 or 2026.1.3, or the security patch plugin for 2017.1 and later, closes stages 2 and 3; a VPN or allowlisted ingress blocks stage 1 and must cover operators because the web UI shares the listener; reading the Unauthorized agents page in the TeamCity UI detects stage 1. Then a dark panel with three log searches. Did someone try: the string BasicDataSource wrapped into f.e.b.BooleanModel in teamcity-server.log. Did it land: .jspws in teamcity-javaLogging, where a hit confirms but a miss does not clear you. Did the patch catch it: the schema-mismatch class name, which lands in a ForbiddenClassException meaning the payload was rejected, and which an unpatched server resolves silently, so it should not be hunted first. A closing note says to use grep -F and to single-quote the third search.

Three controls mapped to the stages they stop, then the three searches that say whether it already ran.

Defender's Corner

Two change how builds behave. npm v12 disables install scripts by default, and blocks git and remote URL dependencies too, which removes the install-time execution registry malware leans on. Dependabot now waits three days before opening a version-update PR; security updates stay immediate.

Two quieter ones. actions/checkout stopped checking out untrusted fork code on pull_request_target, and on workflow_run when the upstream event is a pull request, unless we opt back in. And a high-impact npm account goes read-only for 72 hours after an email change or a 2FA recovery.

One stays opt-in: staged publishing holds a package until someone approves it with 2FA. It's off unless someone changed how CI publishes, and it's what makes a stolen CI token survivable.

So, the opt-outs. Did someone turn off the Dependabot cooldown for being noisy? Does CI still publish with a long-lived npm token instead of trusted publishing?

And check how we pin actions/checkout. The backport reached floating major tags like @v4 and skipped everything more specific. SHA, minor and patch pins all missed it, so the repos that pinned hardest need the upgrade.

Agent Bench: find the build servers we forgot

None of us has a current CI inventory. That's why there's an eight-year-old TeamCity somewhere still answering on 8111.

Point an agent at our IaC, DNS and cloud inventory, and ask for four lists:

  1. Every CI or build server, its version, and whether it's reachable from the internet.

  2. Every credential each one holds: deploy keys, cloud roles, registry tokens, signing material.

  3. Which have been upgraded in the last year, and which haven't.

  4. What downstream trusts artifacts from each.

What we walk away with: the servers that can reach production and haven't been patched since someone left.

Carry the failure mode: agents read version strings out of config files that no longer match what's deployed. Confirm each against the running server.

Try it this week: run list 1. A build server answering on a public IP is a finding by itself, whatever version it claims.

Also on the Radar

GitGuardian found 4,576 n8n API tokens in public GitHub commits across 1,255 hostnames. 896 instances were reachable at the time; 321 accepted at least one, and many looked like owner or admin tokens. That buys workflow-edit rights, and from there CVE-2026-25053 in the Git node abuses git's --pathspec-from-file handling to read files a line at a time.

Point it at the config and N8N_ENCRYPTION_KEY comes out. Pair that with the credential database and everything decrypts offline. If N8N_USER_MANAGEMENT_JWT_SECRET was never set, that same value signs the sessions too. Patch to 1.123.10 or 2.5.0 for the Git node, set the JWT secret yourself for the rest, then rotate: 129 instances were running a key already leaked on GitHub.

A connected client could override the Consul backend address through a request header, so the server sent its API traffic, and any configured credentials, wherever the client pointed. The same advisory carries CVE-2026-16326, cross-tenant credential reuse in stateless mode. Affected 0.1.0 through 0.1.3, fixed in 0.1.4, and the 0.1.4 changelog also swapped an incomplete localhost TLS bypass for an explicit --insecure-no-tls flag. The bypass used to be implicit. Upgrade, then rotate any ACL token a 0.1.x server held.

The TeamCity allowlist only added to what XStream already permitted. npm ran install scripts because it always had, and n8n signed sessions with the encryption key because splitting it was opt-in. The default was there first every time. So what are our build servers already willing to accept?

R.K.

Sponsored content appears below

100+ Claude Code hacks to ship code 10X faster

Top engineers at Anthropic and OpenAI say AI now writes 100% of their code.

If you're not using AI, you're spending 40 hours doing what they do in 4.

These 100+ Claude Code hacks fix that and help you ship 10x faster.

Sign up for The Code and get:

  • 100+ Claude Code hacks used by top engineers — free

  • The Code newsletter — learn the latest AI tools, tips, and skills to code faster with AI in 5 minutes a day