GHSA-5j9r-fx4x-rpvg: High severity pip/rucio vulnerability
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:
python lib/rucio/web/rest/flaskapi/v1/credentials.py account = request.headers.get('X-Rucio-Account', default=None) # L165 (attacker-controlled) ... result = getsignedurl(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:
python lib/rucio/gateway/credential.py authresult = permission.haspermission(issuer=account, vo=vo, # L57 action='getsignedurl', kwargs=kwargs, ...)
and the permission function is root-only:
python lib/rucio/core/permission/generic.py def permgetsignedurl(issuer, kwargs, session): return isroot(issuer) # isroot(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 beforerequest hook validates the token and sets request.environ['issuer'] from the token — but nothing ever compares X-Rucio-Account to it:
python lib/rucio/web/rest/flaskapi/v1/common.py (requestauthenv) 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.getsignedurl 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.
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
pip/rucioto a version that resolves this vulnerability.Fixed in 41.0.0rc1 - Upgrade
Upgrade
pip/rucioto a version that resolves this vulnerability.Fixed in 38.6.0 - Upgrade
Upgrade
pip/rucioto a version that resolves this vulnerability.Fixed in 35.9.0
Event History
Frequently Asked Questions
What does an attacker need to exploit this issue?
The attacker needs any valid Rucio authentication token, including one belonging to a legitimately owned low-privilege account. They can then supply X-Rucio-Account: root to satisfy the root-only permission check.
What can an attacker do with a successfully obtained signed URL?
They can obtain a presigned URL for read, write, or delete operations. The URL is signed using the affected RSE's stored S3, GCS, or Swift credentials.
Is the requested object restricted to data the token holder is otherwise allowed to access?
No. The endpoint can mint a signed URL for an arbitrary object because authorization is evaluated using the client-controlled account header rather than the token-authenticated account.
Does exploitation require another user to take an action?
No user interaction is required. The attacker can make the request directly over the network using their valid token and a forged account header.