CVE-2026-58273
Rucio: Authorisation Bypass on REST API endpoint for signing URLs
Rucio is a scientific data management framework. From 1.16.1 until 35.9.0, 38.6.0, and 41.0.0rc1, the /credentials/signurl endpoint reads X-Rucio-Account instead of the account stored in request.environ['issuer'] by token authentication. Any authenticated low-privilege user can supply root as the header value and pass the root-only get_signed_url permission check. The server then returns a presigned read, write, or delete URL for an attacker-selected object using stored S3, GCS, or Swift credentials, and X-Rucio-VO can select another tenant in multi-VO deployments. This permits data disclosure, modification, deletion, and cross-tenant access on cloud-backed RSEs. This issue is fixed in versions 35.9.0, 38.6.0, and 41.0.0rc1.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- Low
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
8.1
Rucio: Authorisation Bypass on REST API endpoint for signing URLs
Rucio is a scientific data management framework. From 1.16.1 until 35.9.0, 38.6.0, and 41.0.0rc1, the /credentials/signurl endpoint reads X-Rucio-Account instead of the account stored in request.environ['issuer'] by token authentication. Any authenticated low-privilege user can supply root as the header value and pass the root-only get_signed_url permission check. The server then returns a presigned read, write, or delete URL for an attacker-selected object using stored S3, GCS, or Swift credentials, and X-Rucio-VO can select another tenant in multi-VO deployments. This permits data disclosure, modification, deletion, and cross-tenant access on cloud-backed RSEs. This issue is fixed in versions 35.9.0, 38.6.0, and 41.0.0rc1.
- Record
CVE-2026-58273- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-639
- Updated
- 2026-11-06
EUVDEUVD-2026-117819
8.1
Rucio is a scientific data management framework. From 1.16.1 until 35.9.0, 38.6.0, and 41.0.0rc1, the /credentials/signurl endpoint reads X-Rucio-Account instead of the account stored in request.environ['issuer'] by token authentication. Any authenticated low-privilege user can supply root as the header value and pass the root-only get_signed_url permission check. The server then returns a presigned read, write, or delete URL for an attacker-selected object using stored S3, GCS, or Swift credentials, and X-Rucio-VO can select another tenant in multi-VO deployments. This permits data disclosure, modification, deletion, and cross-tenant access on cloud-backed RSEs. This issue is fixed in versions 35.9.0, 38.6.0, and 41.0.0rc1.
- Record
EUVD-2026-117819- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - Updated
- 2026-11-06
GitHub AdvisoriesGHSA-5j9r-fx4x-rpvg
8.8
rucio has an Authorization Bypass on REST API endpoint for signing URLs
Summary
The /credentials/signurl endpoint decides authorization from the client-supplied X-Rucio-Account HTTP header instead of the authenticated token's account. Any client holding any valid auth token (even a low-privilege account they legitimately own) can send X-Rucio-Account: root to pass the root-only permission check and have the Rucio server mint a presigned read / write / delete URL for an arbitrary object, signed with the RSE's stored S3 / GCS / Swift credentials.
Details
SignURL.get() takes the authorization principal from a request header and
never uses the token-authenticated identity:
# lib/rucio/web/rest/flaskapi/v1/credentials.py
account = request.headers.get('X-Rucio-Account', default=None) # L165 (attacker-controlled)
...
result = get_signed_url(account, rse=rse, service=service, # L194
operation=operation, url=url, lifetime=lifetime, vo=vo)
The gateway then uses that header value as the issuer for the permission check:
# lib/rucio/gateway/credential.py
auth_result = permission.has_permission(issuer=account, vo=vo, # L57
action='get_signed_url', kwargs=kwargs, ...)
and the permission function is root-only:
# lib/rucio/core/permission/generic.py
def perm_get_signed_url(issuer, kwargs, session):
return _is_root(issuer) # _is_root(x) -> x.external == 'root'
InternalAccount('root', vo).external == 'root' unconditionally, so the gate passes whenever the header is root. The endpoint is on AuthenticatedBlueprint, whose before_request hook validates the token and sets request.environ['issuer'] from the token — but nothing ever compares X-Rucio-Account to it:
# lib/rucio/web/rest/flaskapi/v1/common.py (request_auth_env)
flask.request.environ['issuer'] = auth.get('account') # L168 (the real account; unused by SignURL)
This is anomalous: every other authenticated handler derives the account from request.environ['issuer']; credentials.py is the only one that trusts the header. Additionally, core.credential.get_signed_url never checks that the supplied url's host/bucket belongs to the target RSE, so even a legitimate root caller gets an arbitrary-object signing oracle. In multi-VO the VO is also read from the X-Rucio-VO header, allowing cross-tenant targeting.
Proof of concept
Request (any valid token + spoofed account):
GET /credentials/signurl?rse=<cloudRSE>&svc=s3&op=delete&url=https://victim-bucket.s3.amazonaws.com/important/dataset.root HTTP/1.1
X-Rucio-Auth-Token: <any valid token, even a low-privilege account>
X-Rucio-Account: root
Accept: application/octet-stream
Response: 200 OK with a presigned DELETE URL the attacker replays directly against the storage backend.
Impact
A low-privilege Rucio user can read, overwrite, and delete arbitrary objects on any cloud-backed (S3/GCS/Swift) RSE using the server's stored credentials — enabling data exfiltration, tampering, and destruction, and cross-tenant access in multi-VO deployments.
Credit
Reported by seankohjs. Found via static analysis + manual verification.
- Record
GHSA-5j9r-fx4x-rpvg- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-639
- Updated
- 2026-11-02
NVD
8.1
Rucio is a scientific data management framework. From 1.16.1 until 35.9.0, 38.6.0, and 41.0.0rc1, the /credentials/signurl endpoint reads X-Rucio-Account instead of the account stored in request.environ['issuer'] by token authentication. Any authenticated low-privilege user can supply root as the header value and pass the root-only get_signed_url permission check. The server then returns a presigned read, write, or delete URL for an attacker-selected object using stored S3, GCS, or Swift credentials, and X-Rucio-VO can select another tenant in multi-VO deployments. This permits data disclosure, modification, deletion, and cross-tenant access on cloud-backed RSEs. This issue is fixed in versions 35.9.0, 38.6.0, and 41.0.0rc1.
- Record
CVE-2026-58273- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-639
- Updated
- 2026-11-06
OSV
8.1
Rucio: Authorisation Bypass on REST API endpoint for signing URLs
Rucio is a scientific data management framework. From 1.16.1 until 35.9.0, 38.6.0, and 41.0.0rc1, the /credentials/signurl endpoint reads X-Rucio-Account instead of the account stored in request.environ['issuer'] by token authentication. Any authenticated low-privilege user can supply root as the header value and pass the root-only get_signed_url permission check. The server then returns a presigned read, write, or delete URL for an attacker-selected object using stored S3, GCS, or Swift credentials, and X-Rucio-VO can select another tenant in multi-VO deployments. This permits data disclosure, modification, deletion, and cross-tenant access on cloud-backed RSEs. This issue is fixed in versions 35.9.0, 38.6.0, and 41.0.0rc1.
- Record
CVE-2026-58273- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-639
- Updated
- 2026-11-07
OSVGHSA-5j9r-fx4x-rpvg
8.8
rucio has an Authorization Bypass on REST API endpoint for signing URLs
Summary
The /credentials/signurl endpoint decides authorization from the client-supplied X-Rucio-Account HTTP header instead of the authenticated token's account. Any client holding any valid auth token (even a low-privilege account they legitimately own) can send X-Rucio-Account: root to pass the root-only permission check and have the Rucio server mint a presigned read / write / delete URL for an arbitrary object, signed with the RSE's stored S3 / GCS / Swift credentials.
Details
SignURL.get() takes the authorization principal from a request header and
never uses the token-authenticated identity:
# lib/rucio/web/rest/flaskapi/v1/credentials.py
account = request.headers.get('X-Rucio-Account', default=None) # L165 (attacker-controlled)
...
result = get_signed_url(account, rse=rse, service=service, # L194
operation=operation, url=url, lifetime=lifetime, vo=vo)
The gateway then uses that header value as the issuer for the permission check:
# lib/rucio/gateway/credential.py
auth_result = permission.has_permission(issuer=account, vo=vo, # L57
action='get_signed_url', kwargs=kwargs, ...)
and the permission function is root-only:
# lib/rucio/core/permission/generic.py
def perm_get_signed_url(issuer, kwargs, session):
return _is_root(issuer) # _is_root(x) -> x.external == 'root'
InternalAccount('root', vo).external == 'root' unconditionally, so the gate passes whenever the header is root. The endpoint is on AuthenticatedBlueprint, whose before_request hook validates the token and sets request.environ['issuer'] from the token — but nothing ever compares X-Rucio-Account to it:
# lib/rucio/web/rest/flaskapi/v1/common.py (request_auth_env)
flask.request.environ['issuer'] = auth.get('account') # L168 (the real account; unused by SignURL)
This is anomalous: every other authenticated handler derives the account from request.environ['issuer']; credentials.py is the only one that trusts the header. Additionally, core.credential.get_signed_url never checks that the supplied url's host/bucket belongs to the target RSE, so even a legitimate root caller gets an arbitrary-object signing oracle. In multi-VO the VO is also read from the X-Rucio-VO header, allowing cross-tenant targeting.
Proof of concept
Request (any valid token + spoofed account):
GET /credentials/signurl?rse=<cloudRSE>&svc=s3&op=delete&url=https://victim-bucket.s3.amazonaws.com/important/dataset.root HTTP/1.1
X-Rucio-Auth-Token: <any valid token, even a low-privilege account>
X-Rucio-Account: root
Accept: application/octet-stream
Response: 200 OK with a presigned DELETE URL the attacker replays directly against the storage backend.
Impact
A low-privilege Rucio user can read, overwrite, and delete arbitrary objects on any cloud-backed (S3/GCS/Swift) RSE using the server's stored credentials — enabling data exfiltration, tampering, and destruction, and cross-tenant access in multi-VO deployments.
Credit
Reported by seankohjs. Found via static analysis + manual verification.
- Record
GHSA-5j9r-fx4x-rpvg- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-639
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
rucioPyPI |
1.16.1 up to 35.9.0 | 35.9.0 |
rucioPyPI |
36.0.0rc1 up to 38.6.0 | 38.6.0 |
rucioPyPI |
39.0.0rc1 up to 41.0.0rc1 | 41.0.0rc1 |
rucioPyPI |
1.16.1 up to 35.9.0, plus listed versions 1.16.21.16.31.17.01.17.11.17.2 Show 45 moreShow less1.17.2.post11.17.51.17.61.17.6.post11.17.6.post21.17.71.17.81.17.8.post11.17.8.post21.18.01.18.11.18.31.18.41.18.51.18.5.post11.18.61.18.6.post11.18.71.18.81.18.8.post11.18.91.19.0.post21.19.11.19.31.19.41.19.4.post11.19.4.post21.19.51.19.61.19.71.19.7.post11.19.81.20.01.20.0rc11.20.11.20.1.post11.20.101.20.111.20.121.20.131.20.141.20.151.20.161.20.21.20.3 |
35.9.0 |
rucioPyPI |
36.0.0rc1 up to 38.6.0, plus listed versions 36.0.036.0.0.post136.0.0.post236.0.0rc236.0.0rc3 Show 35 moreShow less36.0.0rc436.0.0rc536.1.036.2.036.3.036.4.036.5.037.0.037.0.0rc137.0.0rc237.0.0rc337.0.0rc437.1.037.1.0.post137.2.037.3.037.4.037.5.037.6.037.7.037.7.138.0.038.0.0rc138.0.0rc238.0.0rc338.1.038.2.038.3.038.4.038.5.038.5.138.5.238.5.338.5.438.5.5 |
38.6.0 |
rucioPyPI |
39.0.0rc1 up to 41.0.0rc1, plus listed versions 39.0.039.0.0rc239.1.039.2.039.3.0 Show 14 moreShow less39.3.139.4.039.4.139.4.240.0.040.0.0rc140.1.040.1.140.1.240.2.040.3.040.4.040.4.140.4.2 |
41.0.0rc1 |
History
| Recorded | Changed | Sources |
|---|---|---|
| references | CVE List | |
| references | OSV | |
| aliases | EUVD, NVD | |
| description, facts, products, references, severity, title | CVE List | |
| first indexed | GitHub Advisories, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-58273 is a high vulnerability (CVSS 8.1) in Rucio, published October 28, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.56%, the 46th percentile. Fixed in rucio 35.9.0.
References
github.com/rucio/rucio/security/advisories/GHSA-5j9r-fx4x-rpvg
github.com/rucio/rucio/commit/64c0e6a6a93ae88427ff3b7d77532ce08c8a69ec
github.com/rucio/rucio/commit/8bdc38994a8766658d0314d36afc104c9242c96f
github.com/rucio/rucio/commit/8d9ba31514e6f5dca32b8c1db4738582cb37ec3a
github.com/rucio/rucio/releases/tag/35.9.0
github.com/rucio/rucio/releases/tag/38.6.0
Left out here: 274 versions. The JSON record has them.