Eclipse EDC: Anyone can add any verifiable credential to the Identity Hub, jeopardizing the availability and integrity of the entire connector
Basic information
Project name: Eclipse EDC
Project id: technology.edc
What are the affected versions?
I observed the vulnerability in version 0.3.1.
Maybe older or newer versions are affected as well.
Details of the issue
When running the MinimumViableDataspace example (mvd-legacy tag version) (which is using EDC version 0.3.1), I discovered an issue / vulnerability related to IdentityHub and the authentication among the connectors.
Permalink: https://github.com/eclipse-edc/MinimumViableDataspace/tree/067232f6416872548658f7ed6106694a412babff
The Connector used in MVD includes (among others) the following dependencies:
- org.eclipse.edc:identity-did-service
- org.eclipse.edc:identity-hub-credentials-verifier
Connectors authenticate to each other using (self-issued) JWTs that are signed by the sender's private key. The JWT is checked by the receiver by looking up the public key from the sender's DID-document (here via did:web, the sender's DID is noted as subject/issuer in the JWT) and verifying the signature (i.e., check whether the sender is really in possession of the private key associated with that DID). Then, the receiver fetches verifiable credentials (VC) from the sender's IdentityHub to retrieve the claims. The verifiable credentials returned by the sender's IdentityHub are verified by the receiver. One of the checks is that the subject of a verifiable credential must match with the connector's DID. If there is (at least) one verifiable credential with a subject unequal to the connector's DID stored in the IdentityHub, the verification and thereby the authentication fails completely (instead of just leaving out that unrelated VC/claims).
The Identity Hub (at least in this version) does not have any authentication or authorization in place to check who is allowed to add VCs and there is also no check that only VCs with a suitable subject (matching the connector's DID) are added.
So anyone who can access the Identity Hub API can push any Verifiable Credential to the IdentityHub.
The Identity Hub API is publicly accessible and its address is listed in the publicly accessible DID document. The Dataspace Authority (i.e., the "registration service") will push a membership credential as part of the onboarding process and other connectors reach out to the Identity Hub of other connectors to fetch the VCs.
This results in an easy to exploit vulnerability that threatens availability and integrity.
Anyone can push a VC with a different subject (that does not match with the connector's DID) to a connector's Identity Hub. As a result, that connector is no longer able to authenticate to other connectors because the authentication will always fail, because the Identity Hub's response includes that VC (with a subject that does not match with the connector's DID) that causes the whole verification to fail.
The Connector can no longer lookup other Connectors' catalogs. Negotiations are no longer possible. One cannot initiate new data transfers.
The connector is no longer able to interact with other connectors.
There is no endpoint to remove VCs from the Identity Hub. The only way to recover from this attack is to manually remove the malicious VC from the database of the IdentityHub (in case one uses the Identity Hub with persistence). In case of in-memory IdentityHub, one needs to restart the IdentityHub (but then all VCs will be lost). Onboarding (to receive the membership credential again) cannot be triggered again as the connector is still listed as already onboarded.
Nevertheless, an attacker could add a malicious VC again without much effort.
I would grade this vulnerability as a
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:H/VA:H/SC:N/SI:N/SA:H
9.0 / Critical
- AV Network, because the (unprotected) Identity Hub endpoint is exposed to the internet.
- AC Low, because creating a VC with different subject is easy, the request to add a VC to Identity Hub is easy and the address of a connector's IdentityHub is easy to retrieve. The POC payload provided in this report can be used as is. To attack another IdentityHub, attackers only need to send the request (as is) to another Identity Hub instance. The address can be found in the connector's public DID document.
- AT Present, as a vulnerable connector needs to use / include "org.eclipse.edc:identity-did-service" for IAM and " org.eclipse.edc:identity-hub-credentials-verifier".
- PR none, because the (unprotected) Identity Hub endpoint is exposed to the internet.
- UI none, no additional user interaction required
- VC none, as no information is leaked
- VI high, as the content of the Identity Hub is modified by an unauthorized party (lack of auth) resulting in serious consequences for the system.
- VA high, as the connector can no longer authenticate to other connectors / systems in the data space. The connector and the assets might be part of business critical processes. A company (and customers) might depend on the availability of this system.
- SC none, as no information is leaked
- SI none, as no information is modified in subsequent systems.
- SA high, the connectors and the assets might be part of business critical processes. Subsequent systems might depend on the availability of the vulnerable system.
The major issues are:
In IdentityHub:
- Missing authentication and authorization when pushing new VCs to the identity hub.
- Missing check in Identity Hub that only VCs with a suitable subject are allowed to be pushed/stored.
In Connector:
- Unfortunate handling of VCs with non-matching subject during verification of the VCs / fetching the claims (maybe they should be sorted out / ignored instead of failing the whole authentication)
Related Code parts
Why does the authentication among the connectors fail if the sender has a VC with non-matching subject in its Identity Hub?
- There is a check for exact match of the VC subject with the DID and if verification of only one VC fails, the whole
verification of the JWT bearer token fails.
- DecentralizedIdentityService.java#L101-L106
- IdentityHubCredentialsVerifier.java#L71-L99
- IdentityHubCredentialsVerifier.java#L103-L119
- IdentityHubCredentialsVerifier.java#L129
- JwtCredentialEnvelopeVerifier.java#L44-L47
- JwtEnvelopeVerifier.java#L34
- DidJwtCredentialsVerifier.java#L75-L92
- IdentityHubCredentialsVerifier.java#L119
- AggregatedResult.java#L35
- IdentityHubCredentialsVerifier.java#L94
Why does the Identity Hub accept "CollectionsWrite" operation requests from unauthenticated requesters?
- There is no AuthN/AuthZ implemented for that.
Why does the Identity Hub accept VCs with subjects different from the Connector's DID?
- There is no check implemented for that.
- Maybe Identity Hub should support being used / shared for multiple connector instances... But then: Why is there no option to query the Identity Hub for VCs with a specific subject only?
Steps to reproduce
- Checkout and setup MVD using "mvd-legacy" tag version.
./gradlew build -x test
./gradlew -DuseFsVault="true" :launchers:connector:shadowJar
./gradlew -DuseFsVault="true" :launchers:registrationservice:shadowJar
export MVD_UI_PATH=<provide proper value>
- You might want to adjust the
EDC_CATALOG_CACHE_EXECUTION_PERIOD_SECONDS
settings insystem-tests/docker-compose.yml
to a value of 60 or so to have the logs not spammed that much -
docker compose --profile ui -f system-tests/docker-compose.yml up --build
to start MVD
- Check the catalog browser of
company1
connector instance in the dashboard (http://localhost:7080), notice the assets displayed (one can see assets in the catalog browser of the Data Dashboard). Maybe wait 60s for another crawler run to see more. - Push a VC with different subject to
company1
connector's Identity Hub (see example request below, to insert such a malicious VC to the Identity Hub ofcompany1
connector) - That connector can no longer authenticate to other connectors. Wait for the next crawler runs. See errors in logs. Catalog Browser of attacked connector is eventually empty after some crawler runs (the connector can no longer retrieve the catalogs of other connectors).
Request to push a VC with non-matching subject to Identity Hub
POST request to http://localhost:7171/api/identity/identity-hub
Payload/Body (application/json):
{
"messages": [
{
"data": "ZXlKaGJHY2lPaUpTVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SjJZeUk2ZXlKQVkyOXVkR1Y0ZENJNld5Sm9kSFJ3Y3pvdkwzZDNkeTUzTXk1dmNtY3ZNakF4T0M5amNtVmtaVzUwYVdGc2N5OTJNU0lzSW1oMGRIQnpPaTh2ZHpOcFpDNXZjbWN2YzJWamRYSnBkSGt2YzNWcGRHVnpMMnAzY3kweU1ESXdMM1l4SWl3aWFIUjBjSE02THk5eVpXZHBjM1J5ZVM1c1lXSXVaMkZwWVMxNExtVjFMMlJsZG1Wc2IzQnRaVzUwTDJGd2FTOTBjblZ6ZEdWa0xYTm9ZWEJsTFhKbFoybHpkSEo1TDNZeEwzTm9ZWEJsY3k5cWMyOXViR1F2ZEhKMWMzUm1jbUZ0WlhkdmNtc2pJbDBzSW5SNWNHVWlPbHNpVm1WeWFXWnBZV0pzWlVOeVpXUmxiblJwWVd3aVhTd2lhV1FpT2lKb2RIUndjem92TDJOdmJYQnNhV0Z1WTJVdWJHRmlMbWRoYVdFdGVDNWxkUzkyTVMxemRHRm5hVzVuTDJOeVpXUmxiblJwWVd3dGIyWm1aWEp6THpRNVlXVTBZV1JrTFdKbU1EZ3ROREE0TXkxaE9HSmxMV0U1TURZM1ltRTJOMk13TnlJc0ltbHpjM1ZsY2lJNkltUnBaRHAzWldJNlkyOXRjR3hwWVc1alpTNXNZV0l1WjJGcFlTMTRMbVYxT25ZeExYTjBZV2RwYm1jaUxDSnBjM04xWVc1alpVUmhkR1VpT2lJeU1ESTBMVEEzTFRJMlZEQTVPakV3T2pVM0xqQTVNRm9pTENKbGVIQnBjbUYwYVc5dVJHRjBaU0k2SWpJd01qUXRNVEF0TWpSVU1EazZNVEE2TlRjdU1Ea3dXaUlzSW1OeVpXUmxiblJwWVd4VGRXSnFaV04wSWpwN0luUjVjR1VpT2lKbmVEcGpiMjF3YkdsaGJtTmxJaXdpYVdRaU9pSm9kSFJ3Y3pvdkwySmhhMlYxY0M1cGJ5OW5ZV2xoTFhndGJISnVMbXB6YjI0aUxDSm5lRHBwYm5SbFozSnBkSGtpT2lKemFHRXlOVFl0WVRrMk0yTXlPREkyWTJFd1kyVTVNamhtTm1ZMFlqSTRZakZtWVRKbU16QTVNR0U0T0dWbVpXSXhaRE5pWXpkbVpUVXhaR000TkRZM05HVXlPVFEzWkNJc0ltZDRPbWx1ZEdWbmNtbDBlVTV2Y20xaGJHbDZZWFJwYjI0aU9pSlNSa000TnpnMU9rcERVeUlzSW1kNE9uWmxjbk5wYjI0aU9pSXlNaTR4TUNJc0ltZDRPblI1Y0dVaU9pSm5lRHBzWldkaGJGSmxaMmx6ZEhKaGRHbHZiazUxYldKbGNpSjlmU3dpYVdGMElqb3hOekl4T1RnMU1EVTNMQ0psZUhBaU9qRTNNamszTmpFd05UY3NJbWx6Y3lJNkltUnBaRHAzWldJNlkyOXRjR3hwWVc1alpTNXNZV0l1WjJGcFlTMTRMbVYxT25ZeExYTjBZV2RwYm1jaUxDSnpkV0lpT2lKb2RIUndjem92TDJOdmJYQnNhV0Z1WTJVdWJHRmlMbWRoYVdFdGVDNWxkUzkyTVMxemRHRm5hVzVuTDJOeVpXUmxiblJwWVd3dGIyWm1aWEp6THpRNVlXVTBZV1JrTFdKbU1EZ3ROREE0TXkxaE9HSmxMV0U1TURZM1ltRTJOMk13TnlJc0ltRjFaQ0k2SW1oMGRIQnpPaTh2WTI5dGNHeHBZVzVqWlM1c1lXSXVaMkZwWVMxNExtVjFMM1l4TFhOMFlXZHBibWN2WTNKbFpHVnVkR2xoYkMxdlptWmxjbk12TkRsaFpUUmhaR1F0WW1Zd09DMDBNRGd6TFdFNFltVXRZVGt3TmpkaVlUWTNZekEzSW4wLmtqbGp5SlFzNlNxSV9vUnkxWjkwTUIwekdWSmtvSUdaQU4xR1RCdUNlcTAyX0dVNkJkRGJnamU5MGlpRkQwRlFOQUpabWpYUXZnQndTWGtuRUtZWHlBYlBKU2hjQzZrN2V1MVpGX1ZzbU5VeWdtUFBHOXUzaEtCdkRzcVFzdzhKam5DeVFFWXNsRk5XTENfcnpDZmJiRFpSR3JWQy1pRFpya0s5cmVQZ0hhRmtxV1FPMnNURU9lZDFfQTRCRGw1ZWxpUmpWMGxaNzRsaU1GcmtBd3FVS0ZEdk5KUE9QQkpvY1dQRWVHQkUxczZwb3pyN1ZUTHNsWW5DZzYyZ3JSWGdIZUZva0hkZWZXTlJDemdSRjJ6UTUxWWcwRkZtdVI1NFNaaGkzbUZ4NWZWMFI4ZmRvY3d0cGIwVDR0cENMZFlPdFRWVHkyZWhWT0ZwZmhkYWNMOEZNZw==",
"descriptor": {
"method": "CollectionsWrite",
"dataFormat": "application/vc+jwt",
"dateCreated": 1718880898,
"recordId": "test1234"
}
}
]
}
Logs (excerpt)
company2 | DEBUG 2024-08-05T09:49:11.0300388 DSP: Unauthorized: Failed to get verifiable credentials: Failure verifying JWT token: JWT sub claim has value https://compliance.lab.gaia-x.eu/v1-staging/credential-offers/49ae4add-bf08-4083-a8be-a9067ba67c07, must be did:web:did-server:company1
company1 | Caused by: org.eclipse.edc.spi.EdcException: {"@type":"dspace:CatalogError","dspace:code":"401","dspace:reason":"Token validation failed.","@context":{"dct":"https://purl.org/dc/terms/","edc":"https://w3id.org/edc/v0.0.1/ns/","dcat":"https://www.w3.org/ns/dcat/","odrl":"http://www.w3.org/ns/odrl/2/","dspace":"https://w3id.org/dspace/v0.8/"}}
Do you know any mitigations of the issue?
One could use a Web Application Firewall (WAF) or so in front of the Identity Hub API that blocks "CollectionsWrite" operation requests to the IdentityHub API (i.e., prevent adding new VCs to the Identity Hub).