Two scoring bodies looked at the same Atlassian bug and published two different numbers. Atlassian rated CVE-2026-21589 at CVSS 4.0 9.3, critical. NVD rated it at CVSS 3.1 7.5, high. Whichever number your vulnerability management tool imported on October 5 decided whether this landed in your patch queue or in a report nobody opened. CISA settled the argument on October 15 by adding it to the Known Exploited Vulnerabilities catalog with a federal due date of October 18 and a forensic triage requirement attached.
The bug is an unauthenticated file read in eight self-hosted Atlassian products. watchTowr Labs published the analysis on October 6, one day after the vendor advisory. What they pulled out of a lab Jira instance is the reason this article exists, and it is not in the advisory.
Whether this one is yours
If your Jira, Confluence or Bitbucket runs on atlassian.net, stop here. Atlassian patched Cloud itself and found no evidence of exploitation there, and the advisory asks Cloud customers for no action.
Everyone else should read the version floors from the NVD record, because they are unusually wide. Bitbucket Data Center is affected from 4.6.0. Confluence Data Center from 5.10.0. Jira Software Data Center from 7.1.0, Jira Service Management from 3.1.0, Bamboo from 7.0.1, Crowd from 2.11.0. Crucible and Fisheye are in scope with no floor given. Confluence 5.10 shipped in 2016, which means roughly ten years of builds carry this, and the shared library that holds the defect is common to all of them.
One group inside that set has a worse problem than the rest: anyone whose Atlassian stack authenticates through Atlassian Crowd. The file that makes this a critical rather than an interesting read lives only in those deployments. If that describes you, the patch is the start of the work rather than the end of it.
How a pair of colons becomes a path traversal
The defect sits in atlassian-plugins-webresource, a library shared across the Data Center products, which is why one CVE covers eight of them. watchTowr found it by diffing library version 6.0.7 against 6.0.8.
Atlassian's Router.java carries a pair of helpers, escapeSlashes() and unescapeSlashes(), that swap the / character for a doubled colon and back again. Plugin resource routes shaped like /resources/:completeKey/*resourceName pass the user-controlled resource name into the serving helpers, and a deprecated path, getResourceRelativeToWebResource(), splits off the last path part and hands the remainder to a factory that reads files off disk. A traversal written with doubled colons instead of slashes travels through that chain intact and arrives as a real relative path at the read.
The practical consequence is that the payload contains no ../ and no slash at all. watchTowr's working request against Jira used a color-picker image resource:
GET /download/resources/jira.webresources:color-picker-popup/images/..::..::..::..::..::WEB-INF::web.xml
Confluence takes the same shape under a dashboard-actions resource path, and Bitbucket under an avatar resource path. Three constraints bound it. The read stays inside the Tomcat web application context, so this does not reach /etc/shadow or a database on another volume. There is no directory listing, so the attacker needs the exact filename. And the route has to match a plugin resource that resolves to a fixed base directory. Those limits are real, and they are also why the advisory's phrasing undersells the impact: once an attacker knows which filenames to ask for, the constraint stops mattering.
The filename that matters is crowd.properties
Atlassian's advisory says only that "in some configurations, there may be sensitive files present that increase your risk." It never names one. watchTowr did.
WEB-INF/web.xmlreads on Jira and Confluence. Useful for fingerprinting, harmless on its own.WEB-INF/urlrewrite.xmlreads on Bitbucket, which blocksweb.xmlby other means.WEB-INF/classes/crowd.propertiesreads on any instance wired to Crowd for authentication. That file holds the Crowd application name, the Crowd server URL, and the application password in cleartext.
With that third file, the researchers authenticated to Crowd's user-management REST API as the application, created a user, and added it to jira-administrators. No exploit was needed for the second step, because an application password is a valid credential and Crowd behaved exactly as designed. A Crowd configuration that restricts application access by source IP blocks that follow-on, which is worth knowing if you have one and worth building if you do not.
Here is the part that no patch addresses. An upgrade closes the read. It does not change the password that was already readable. If an internet-facing Jira, Confluence or Bitbucket with a Crowd integration answered requests between October 5 and the day you upgraded, the correct assumption is that the application password in crowd.properties is known to someone else, and the correct response is to replace it and audit Crowd for accounts and group memberships created in that window.
Exploitation is not theoretical. VulnCheck added the CVE to its own exploited-vulnerability list on October 7 after observing activity against Bamboo Data Center, per Infosecurity Magazine, and CISA followed on October 15. Nobody has published what the attackers took.
Hunt the logs before the retention window closes
Atlassian's detection guidance is correct and easy to apply wrongly. The advisory tells you to URL-decode each access log request line, up to two passes, and then look for .. immediately adjacent to /, \ or a doubled colon. The two decode passes are the part most teams will skip, and skipping them is how a clean grep produces false confidence: a request encoded as %252e%252e survives one decode as %2e%2e and only becomes .. on the second.
Confluence writes its Tomcat access log to <install-directory>/logs/conf_access_log<date>.log through an AccessLogValve declared in <install-directory>/conf/server.xml, and the documented valve ships with maxDays="30". The other products use the same Tomcat mechanism under their own install directories. Thirty days from the October 5 advisory runs out in the first week of November, so the evidence for the window that matters is still on disk today and will not be in three weeks. Copy it off before you hunt it.
# 1. Preserve first. Access logs rotate on maxDays=30 by default.
for P in /opt/atlassian/confluence /opt/atlassian/jira /opt/atlassian/bitbucket; do
[ -d "$P/logs" ] && tar czf "/evidence/$(hostname)-$(basename $P)-access.tgz" \
-C "$P" logs --wildcards '*access*log*' 2>/dev/null
done
# 2. Raw pass: catches the unencoded traversal in the request path.
grep -aniE '(\.\.|%2e%2e)(::|%3a%3a|/|%2f|\\|%5c)' /evidence/*/logs/*access*log* \
| grep -aiE '/download/resources/|/_/download/resources/'
The decode pass the raw grep misses
Run this second. It normalises each line twice, the way the advisory asks, and then applies the same adjacency test. Point it at the copies in /evidence, not at live logs.
#!/usr/bin/env python3
# CVE-2026-21589: two-pass URL decode, then test for '..' adjacent to / \ or ::
import glob, re, sys
from urllib.parse import unquote
HIT = re.compile(r'\.\.(?:/|\\|::)|(?:/|\\|::)\.\.')
RES = re.compile(r'/(?:_/)?download/resources/', re.I)
for path in glob.glob(sys.argv[1] if len(sys.argv) > 1 else '/evidence/**/*access*log*',
recursive=True):
with open(path, errors='replace') as fh:
for n, line in enumerate(fh, 1):
decoded = unquote(unquote(line))
if HIT.search(decoded) and RES.search(decoded):
print(f'{path}:{n}: {line.rstrip()}')
A hit tells you a request was made. The HTTP status code on the same log line tells you whether the file came back: a 200 against a WEB-INF path is a successful read, and a 404 or a 403 is a failed attempt worth recording anyway. Treat absent or disabled access logging as a finding rather than as a clean result, because the valve can be removed from server.xml and on many installs nobody has checked.
watchTowr also released a detection artefact generator that answers the simpler question of whether a given Jira, Confluence or Bitbucket host is still vulnerable. It reports a vulnerable or probably-not-vulnerable verdict and stops short of retrieving file contents, which makes it safe to point at your own production instance to validate that the upgrade took.
Builds that close it, and the mitigation if you cannot get there today
Atlassian no longer ships binary patches. Fixes arrive only in maintenance releases, so the remediation is a version upgrade on every node of every affected product:
- Confluence Data Center: 9.2.26 or 10.2.19
- Jira Software Data Center: 9.12.40, 10.3.26 or 11.3.12
- Jira Service Management Data Center: 5.12.40, 10.3.26 or 11.3.12
- Bitbucket Data Center: 9.4.26, 10.2.8 or 10.5.1
- Bamboo Data Center: 10.2.24 or 12.1.12
- Crowd Data Center: 6.3.7, 7.0.3, 7.1.7 or 7.2.4
- Crucible and Fisheye: 4.9.15
For the gap before the maintenance window, the advisory gives three options and they stack. Pull internet-facing instances behind the VPN, which costs you remote contributor access and buys the most. Add the vendor's regex to your WAF or reverse proxy; its stated intent is to block .. immediately adjacent to /, \ or a doubled colon, including the percent-encoded and double-encoded forms. On Confluence, Jira, Bamboo and Crowd, enable the Tomcat RewriteValve rule from the advisory. Bitbucket takes a urlrewrite.xml rule instead that returns 404 on a match, and needs a restart after.
Test whichever mitigation you choose against the detection generator rather than trusting the configuration reload. A rewrite rule that silently failed to load looks identical to one that is working, right up until it does not matter any more.
Treat crowd.properties as public until you prove otherwise
Self-hosted Atlassian tends to sit in the same blind spot as the print server and the backup console: a box that one team depends on, that nobody patches on a cadence, and that is reachable from the internet because a contractor needed Jira from a hotel in 2021. The federal due date on this one is October 18, and the only reason that date is interesting to a small business is that it tells you how CISA reads the exploitation evidence.
Work it in this order today. Inventory every Data Center instance, including the test Confluence that was stood up for a migration and never retired. Pull the access logs off each one before the thirty-day rotation takes them, then run both passes. Upgrade to the builds above. If any instance uses Crowd and was reachable from outside, replace the application password, review Crowd for users and group memberships created since October 5, and check the membership of jira-administrators by hand.
The hard call is the one the logs cannot answer: an instance with access logging disabled and a Crowd integration gives you no way to prove a negative. We work that problem with clients often enough to have a view on it, and the honest answer is to rotate the credential anyway. If you want a second set of eyes on which of your instances were exposed and what a reachable crowd.properties would have cost you, that is worth an hour this week rather than an incident next month.
Not sure which of your Atlassian instances were reachable before you patched?
We help organizations work out whether an actively exploited server was read before the fix landed, and which credentials to replace when the logs cannot prove a negative. Book a session with our team to scope your self-hosted Atlassian estate.
