GHSA-wrvh-rcmr-9qfc: @strapi/plugin-users-permissions leaks 3rd party authentication tokens and authentication bypass
SummaryBy combining two vulnerabilities (an `Open Redirect` and `session token sent as URL query parameter`) in Strapi framework is its possible of an unauthenticated attacker to bypass authentication mechanisms and retrieve the 3rd party tokens. The attack requires user interaction (one click).ImpactUnauthenticated attackers can leverage two vulnerabilities to obtain an 3rd party token and the bypass authentication of Strapi apps.Technical detailsVulnerability 1: Open RedirectDescriptionOpen redirection vulnerabilities arise when an application incorporates user-controllable data into the target of a redirection in an unsafe way. An attacker can construct a URL within the application that causes a redirection to an arbitrary external domain.In the specific context of Strapi, this vulnerability allows the SSO token to be stolen, allowing an attacker to authenticate himself within the application.RemediationIf possible, applications should avoid incorporating user-controllable data into redirection targets. In many cases, this behavior can be avoided in two ways:Remove the redirection function from the application, and replace links to it with direct links to the relevant target URLs.Maintain a server-side list of all URLs that are permitted for redirection. Instead of passing the target URL as a parameter to the redirector, pass an index into this list.If it is considered unavoidable for the redirection function to receive user-controllable input and incorporate this into the redirection target, one of the following measures should be used to minimize the risk of redirection attacks:The application should use relative URLs in all of its redirects, and the redirection function should strictly validate that the URL received is a relative URL.The application should use URLs relative to the web root for all of its redirects, and the redirection function should validate that the URL received starts with a slash character. It should then prepend <span dir="">http://yourdomainname.com</span> to the URL before issuing the redirect.Example 1: Open Redirect in <span dir="">/api/connect/microsoft</span> via `$_GET["callback"]`Path: <span dir="">/api/connect/microsoft</span>Parameter: `$_GET["callback"]`Payload:Final payload:User clicks on the link:
![c1](https://github.com/strapi/strapi/assets/30262080/c1944cf8-2ef0-4214-ba9e-d4aad10d85ba)Look at the intercepted request in Burp and see the redirect to Microsoft:![c0](https://github.com/strapi/strapi/assets/30262080/0c3d9289-432c-46ac-a7e3-eafe15f02483)Microsoft check the cookies and redirects to the original domain (and route) but with different GET parameters.Then, the page redirects to the domain controlled by the attacker (and a token is added to controlled the URL):![c2](https://github.com/strapi/strapi/assets/30262080/009e3898-1ccf-4ee4-9c29-496ff6b302d0)The domain originally specified (https://google.fr) as `$_GET["callback"]` parameter is present in the cookies. So <span dir="">\<TARGET\></span> is using the cookies (`koa.sess`) to redirect.![c3](https://github.com/strapi/strapi/assets/30262080/4c25cb6c-c9e8-4c2d-aa61-1ad1442e5f4d)`koa.sess` cookie:The vulnerability seems to come from the application's core:File: [<span dir="">packages/plugins/users-permissions/server/controllers/auth.js</span>](https://github.com/strapi/strapi/blob/develop/packages/plugins/users-permissions/server/controllers/auth.js)And more specifically:Possible patch:`_.get(ctx, 'query.callback')` = `$_GET["callback"]` and `_.get(ctx, 'session')` = `$_COOKIE["koa.sess"]` (which is `{"grant":{"provider":"microsoft","dynamic":{"callback":"https://XXXXXXX/"}},"_expire":1701275652123,"_maxAge":86400000}`) so `_.get(ctx, 'session.grant.dynamic.callback')` = `https://XXXXXXX/`.The route is clearly defined here:File: [<span dir="">packages/plugins/users-permissions/server/routes/content-api/auth.js</span>](https://github.com/strapi/strapi/blob/develop/packages/plugins/users-permissions/server/routes/content-api/auth.js)File: [<span dir="">packages/plugins/users-permissions/server/services/providers-registry.js</span>](https://github.com/strapi/strapi/blob/develop/packages/plugins/users-permissions/server/services/providers-registry.js)If parameter `$_GET["callback"]` is defined in the GET request, the assignment does not evaluate all conditions, but stops at the beginning. The value is then stored in the cookie `koa.sess`:`koa.sess`=`eyJncmFudCI6eyJwcm92aWRlciI6Im1pY3Jvc29mdCIsImR5bmFtaWMiOnsiY2FsbGJhY2siOiJodHRwczovL2FkbWluLmludGUubmV0YXRtby5jb20vdXNlcnMvYXV0aC9yZWRpcmVjdCJ9fSwiX2V4cGlyZSI6MTcwMTI3NTY1MjEyMywiX21heEFnZSI6ODY0MDAwMDB9`Which once base64 decoded become `{"grant":{"provider":"microsoft","dynamic":{"callback":"https://<TARGET>/users/auth/redirect"}},"_expire":1701275652123,"_maxAge":86400000}`.The signature of the cookie is stored in cookie `koa.sess.sig`:`koa.sess.sig`=`wTRmcVRrn88hWMdg84VvSD87-_0`File: [<span dir="">packages/plugins/users-permissions/server/bootstrap/grant-config.js</span>](https://github.com/strapi/strapi/blob/develop/packages/plugins/users-permissions/server/bootstrap/grant-config.js)Vulnerability 2: Session token in URLDescriptionApplications should not send session tokens as URL query parameters and use instead an alternative mechanism for transmitting session tokens, such as HTTP cookies or hidden fields in forms that are submitted using the POST method.Example 1: SSO token transmitted within URL (`$_GET["access_token"]`)Path: <span dir="">/api/connect/microsoft</span>Parameter: `$_GET["callback"]`When a callback was called, the 3rd party token was transmitted in an insecure way within the URL, which could be used to increase the impact of the Open Redirect vulnerability described previously by stealing the SSO token.Weaponized payload:With a web server specially developed to exploit the vulnerability listening on <span dir="">\<C2\>:8080</span>,  it is possible to retrieve a JWT token allowing authentication on Strapi.A user is on his browser when he decides to click on a link sent to him by e-mail.![c4](https://github.com/strapi/strapi/assets/30262080/c6e22fa1-14a4-4c76-a832-d07305f265b6)The attacker places the malicious link in the URL bar to simulate a victim's click.![c5](https://github.com/strapi/strapi/assets/30262080/4da28c5b-6501-4f93-9041-9917a2b070e6)The server specially developed by the attacker to show that the vulnerability is exploitable, recovers the user's SSO token.Everything is invisible to the victim.![c6](https://github.com/strapi/strapi/assets/30262080/58db0a31-3b3b-4648-958b-953eba88bf87)Because the victim didn't change to another Web page.![c7](https://github.com/strapi/strapi/assets/30262080/ab4dd6f9-02e1-42c9-9142-434db865f0d3)The attacker can use the SSO token to authenticate himself within the application and retrieve a valid JWT token enabling him to interact with it.![c8](https://github.com/strapi/strapi/assets/30262080/aab8d22f-5f0e-4a67-85a8-2e333df9b84b)DetailsGet the JWT token with the `access_token`First of all, thanks to the SSO token, you authenticate yourself and get a JWT token to be able to interact with the various API routes.Request (HTTP):Response (HTTP):Request API routes using the JWT tokenThen reuse the JWT token to request the API.Request (HTTP):Response (HTTP):POC (Web server stealing SSO token and retrieving JWT token then bypassing authentication)
CVSS v3.1: 7.1