Vulnerability research

Atlassian: how an image endpoint exposed a Crowd application password

Affected older versions of eight self-managed Atlassian products expose internal application files through public resource endpoints, including application passwords in some Crowd SSO deployments. Install the vendor's complete patched distribution on every node and separately remediate exposed application credentials.

Warm-paper drawing of a filing cabinet: a red path leads from its public image drawer around an internal divider to a document compartment, while the outer cabinet remains intact.
In this article

Even Jira's sign-in page needs images, scripts and styles. Those resources have to load before someone signs in. On October 5, Atlassian issued an emergency security advisory calling for upgrades to eight self-managed products after finding a flaw in this easily overlooked public route. An attacker could submit directory components through an image-resource endpoint and make the server return internal application configuration.

One file makes the consequences tangible. A Jira installation using the documented Crowd single sign-on integration stores its application name, application password and Crowd address in WEB-INF/classes/crowd.properties. Crowd centralizes user management and authentication. Jira normally presents those credentials when it talks to Crowd. Once someone else has the file, the next step depends on what that application identity can do in Crowd and whether Crowd accepts a connection from the attacker's location.

Responding to CVE-2026-21589 means closing the public file-read route and dealing with identity credentials as separate tasks. Updating the product does not automatically invalidate an exposed Crowd application password. Changing that credential needs its own action. On October 7, SANS reported exploitation attempts against its honeypots beginning the previous day, using resource paths from the published research. Waiting for a confirmed intrusion notice leaves an exposed, outdated instance available for longer.

Teams still running affected self-managed versions should restrict external network access first, then install the vendor's complete product update. The branch-specific repair table later in this article covers all eight products. Atlassian has handled its Cloud products, whose customers do not need to deploy these local patches.

1 Two colons become a slash inside the application

The application directory matters here. Each web application in Tomcat has a resource root. A browser can request its public images, while direct downloads from WEB-INF are restricted. That directory contains deployment descriptors, classes and some configuration. Server-side code still needs access to those internal files for the application to work. The vulnerability reaches that server-side read. An attacker must know the exact target file name and path and cannot use it to browse or list the directory.

Atlassian's plugin resource system maps resource names to content. In the official 6.0.7 source archive, lines 40–67 of com/atlassian/plugin/webresource/impl/http/Router.java register resource and source routes. A handler calls unescapeSlashes() on the incoming resource name before passing it to Controller. The function converts :: back to /, reversing the transformation used when generating URLs.

// Router.java: conversion before the subsequent resource lookup
public static String unescapeSlashes(String string) {
    return string.replaceAll("::", "/");
}

This changes the path seen by the next part of the program. Double colons that arrived as ordinary characters become directory separators. Two dots next to them can now mean a parent directory. The web server processes the request before this conversion, while Java resource lookup receives a name that already contains ../. That new path needs to be constrained before the actual read.

The conversion also needs a resource declaration that leads to a file. In the researchers' published Jira example, a color-picker resource maps images/ to an image directory in the application and specifies source=webContextStatic. The trailing slash allows a file name to follow the resource prefix. A public request now has a real directory as its starting point. watchTowr demonstrated this class of read in Jira, Confluence and Bitbucket, with different resource entry points in each product.

2 A compatibility lookup puts the remaining path back

The transformed name reaches ResourceServingHelpers.getResource(), which first looks for the complete resource name. When that fails, it enters getResourceRelativeToWebResource(). In lines 569–597 of impl/helpers/ResourceServingHelpers.java in the same archive, this compatibility function removes components from the end of the name and accumulates them in filePath, until the remaining prefix matches a registered resource. If that resource is images/, a request can name an image underneath it.

The removed components survive. At lines 646–656, splitLastPathPart() finds the last slash and splits the string. It does not remove parent-directory components. When the loop finishes, the accumulated filePath goes to ResourceFactory.createResourceWithRelativePath(), carrying any preserved ../ along with it.

Lines 72–95 of impl/snapshot/resource/ResourceFactory.java pass the relative path to a path strategy. Then getPath() at lines 13–20 of impl/snapshot/resource/strategy/path/RelativePathStrategyDecorator.java joins it to the original resource location. Parent-directory components preserved during splitting enter the resulting path.

The declaration's webContextStatic value determines the reader. Lines 21–27 of impl/snapshot/resource/strategy/stream/StreamStrategyFactory.java select the Tomcat strategy. Lines 15–18 of TomcatStreamStrategy.java in the same directory call ServletContext.getResourceAsStream(). The read stays within the web application context, where WEB-INF is also an addressable application resource. The HTTP restriction against a browser directly downloading that directory does not block this server-side call.

The offline string model below retains the splitting loop without running an Atlassian product or making a network request. Both a normal file name and a name containing a parent-directory component can match images/, with the parent component preserved in the latter's relative path.

Registered prefix      images/
Normal request         images/palette.png
Remaining path         palette.png

Illustrative input     images/../internal.txt
Remaining path         ../internal.txt

Product resource declarations and the application context determine which files are readable. watchTowr's product demonstrations retrieved deployment or rewrite configuration within the application directory. That is the scope administrators should inspect for sensitive content.

Router restores double colons in a public resource name to slashes, the relative path is appended, and an internal document within the application can be returned.
Figure 1. The same name moves from a request into resource access with changed separator semantics. File names are illustrative. Product configuration determines actual routes and readable objects. Open full-size image

3 The password represents an application identity

Reading web.xml establishes exposure of an internal file. The consequences depend on what else occupies the same scope. Atlassian's Jira integration instructions separate a directory connection from optional Crowd single sign-on. The latter step copies crowd.properties into Jira's WEB-INF/classes. Deployments using that SSO configuration store the file at this documented location. Its application.name and application.password identify an application registered in Crowd.

An application password has a different job from a user's password. It tells Crowd that Jira is requesting directory services. Crowd then applies that application's directory mappings and permissions. After disclosure, two questions follow. Can a connection reach Crowd, and can this application identity perform the relevant directory operation?

Crowd can restrict where an application connects from. The Remote Addresses step in the official integration instructions registers the Jira host's address. When enforced strictly, the restriction rejects connections from unapproved sources, preventing use of Jira's application identity from those locations even with the password. A broad allowlist, or a request that already originates from an allowed location, changes the protection this check provides.

Once a connection is accepted, permissions still matter. Crowd's directory-permission documentation separates directory-level and application-level permissions. Applications can receive individual permissions to add, modify or delete users and groups. Disabling an operation at directory level overrides an application-level grant. The environment in the published research allowed the exposed application identity to create a user and add that user to a Jira administrator group. That outcome requires the corresponding connectivity and write permissions.

Start the investigation with the Crowd application corresponding to the configuration file, then check its directory mappings, write permissions and allowed sources. Exposure of usable credentials calls for action on both the file-read entry point and that application identity.

4 Update every serving node and retain restrictions during repair

Atlassian's October 5 advisory covers Bitbucket, Confluence, Jira Software, Jira Service Management, Bamboo, Crowd, Crucible and Fisheye. The vendor has handled Cloud products and calls for updates to self-managed installations. The table gives the first fixed builds on each listed maintenance line. Choose these or a newer maintenance build on a supported branch. Before moving to another major release, check its release notes and compatibility requirements, since a larger version number alone cannot establish that it contains this repair.

First fixed builds and checked maintenance packages, October 13, 2026

ProductFirst fixed builds by branchCurrent maintenance package
Bitbucket Data Center9.4.26, 10.2.8, 10.5.110.2.8 LTS
Confluence Data Center9.2.26, 10.2.1910.2.19 LTS
Jira Software Data Center9.12.40, 10.3.26, 11.3.1211.3.12 LTS
Jira Service Management Data Center5.12.40, 10.3.26, 11.3.1211.3.12 LTS
Bamboo Data Center10.2.24, 12.1.1212.1.12 LTS
Crowd Data Center6.3.7, 7.0.3, 7.1.7, 7.2.47.2.4 LTS
Crucible4.9.154.9.15
Fisheye4.9.154.9.15

The last column provides one concrete entry point on a current maintenance line. An installation on another supported branch with a corresponding fix can update there, without an immediate major-version move. Jira 10.3 installations, for example, can use 10.3.26. Install the complete product package, since a resource-library version alone cannot establish product remediation. Bitbucket's 10.2 release notes also require a full cluster shutdown and restart when upgrading from 10.2.0–10.2.2 to 10.2.3 or later. The move to 10.2.8 includes that requirement.

For request filtering in addition to restricted external access, the vendor supplies a WAF expression, Tomcat RewriteValve configuration for Confluence, Jira, JSM, Bamboo and Crowd, and a separate urlrewrite.xml rule for Bitbucket. These rules identify parent-directory components adjacent to separators and cover URL-encoded forms. Use the full current vendor rule, then verify whether each layer in your proxy and application chain inspects the raw URI or a decoded path. A normal sign-in requirement cannot replace this path restriction.

Temporary restrictions have to cover the nodes actually serving traffic. The Bitbucket instructions specifically include code mirrors and mirror-farm nodes. Keep failed-upgrade nodes isolated and roll back only to a compatible package containing the fix. After restart, verify the installation directory and build actually serving requests, then check normal sign-in, images, scripts, pages and required integrations. Preserve the backups needed for recovery before removing old installation directories or deployment containers, and keep an outdated resource endpoint out of the rollback path.

5 When a file has already left the server

Preserve proxy and application access logs. The vendor recommends URL-decoding request lines up to twice before checking parent-directory components adjacent to separators, or searching the raw logs with its expression. Request times, statuses and response sizes help narrow an investigation. Confirming disclosure requires records that establish the returned content. A scan request alone establishes an attempt. Confirmed delivery of a sensitive file calls for action on its active credentials.

For a Crowd integration, check application-identity activity and user or group changes, and change potentially exposed application passwords. Update the matching client configuration at the same time and verify legitimate connections, so Crowd and Jira do not end up using different passwords. Where unexpected accounts or group memberships have appeared, revoke unauthorized access and trace the systems reachable through those permissions. A new password will not remove accounts and permissions already created.