[ 
https://issues.apache.org/jira/browse/WW-5705?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Lukasz Lenart updated WW-5705:
------------------------------
    Description: 
{{StrutsParameterAuthorizer#isAuthorized}} derives the root property of a 
parameter name by cutting at the first nesting character ({{.}}, {{[}} or 
{{(}}), then lower-cases its first character:

{code:java}int nestingIndex = indexOfAny(parameterName, NESTING_CHARS_STR);
String rootProperty = nestingIndex == -1 ? parameterName : 
parameterName.substring(0, nestingIndex);
String normalisedRootProperty = Character.toLowerCase(rootProperty.charAt(0)) + 
rootProperty.substring(1);
{code}

When the name _begins_ with a nesting character, {{nestingIndex}} is 0, 
{{substring(0, 0)}} yields an empty string, and {{charAt(0)}} throws 
{{StringIndexOutOfBoundsException}}. The exception escapes the interceptor.

h2. Reachability
* {{ParametersInterceptor}} is *not* affected: {{isAcceptableName()}} applies 
{{ACCEPTED_PATTERNS}}, which requires a leading word character, before 
authorization.
* The JSON plugin *is*: {{JSONInterceptor}} authorizes at 
{{isAcceptableNode()}}, whereas the accepted-pattern check runs later, in 
{{isAcceptableLeafName()}}, and only for leaf keys.
* The REST plugin's {{ContentTypeInterceptor}} authorizes body-derived paths 
with no accepted- or excluded-pattern check beforehand.

Reproduced with the container's own {{StrutsParameterAuthorizer}}, under the 
shipped default {{struts.parameters.requireAnnotations=true}}, with a JSON body 
whose root key is {{.foo}} - the exception escapes 
{{JSONInterceptor.intercept()}}.

h2. Affected versions

The expression dates from WW-5352 (6.4.0), but nothing reached it with such a 
name until WW-5624 wired the JSON and REST body channels through the authorizer 
in 7.2.0.

6.x is not affected: {{requireAnnotations}} has no default there, there is no 
{{ParameterAuthorizer}} at all, and {{ParametersInterceptor}} - its only caller 
- filters names through the accepted patterns first.

h2. Impact

A failed request. No authorization bypass, no amplification, nothing persists 
beyond the single request. Triaged as an ordinary robustness bug rather than a 
vulnerability.

h2. Fix

A name beginning with a nesting character names no root property, so 
authorization rejects it before normalisation rather than computing one.

The change is already included in the WW-5698 pull request 
https://github.com/apache/struts/pull/1872 (commit {{2f3ce9601}}), since that 
PR routes ModelDriven parameters through the same expression. It can be split 
into its own PR if the release notes should attribute it separately.

  was:
{{StrutsParameterAuthorizer#isAuthorized}} derives the root property of a 
parameter name by cutting at the first nesting character ({{.}}, {{[}} or 
{{(}}), then lower-cases its first character:

{code:java}
int nestingIndex = indexOfAny(parameterName, NESTING\_CHARS\_STR);
String rootProperty = nestingIndex == -1 ? parameterName : 
parameterName.substring(0, nestingIndex);
String normalisedRootProperty = Character.toLowerCase(rootProperty.charAt(0)) + 
rootProperty.substring(1);
{code}

When the name _begins_ with a nesting character, {{nestingIndex}} is 0, 
{{substring(0, 0)}} yields an empty string, and {{charAt(0)}} throws 
{{StringIndexOutOfBoundsException}}. The exception escapes the interceptor.

h4. Reachability
* {{ParametersInterceptor}} is *not* affected: {{isAcceptableName()}} applies 
{{ACCEPTED\_PATTERNS}}, which requires a leading {{\w+}}, before authorization.
* The JSON plugin *is*: {{JSONInterceptor}} authorizes at 
{{isAcceptableNode()}}, whereas the accepted-pattern check runs later, in 
{{isAcceptableLeafName()}}, and only for leaf keys.
* The REST plugin's {{ContentTypeInterceptor}} authorizes body-derived paths 
with no accepted- or excluded-pattern check beforehand.

Reproduced with the container's own {{StrutsParameterAuthorizer}}, under the 
shipped default {{struts.parameters.requireAnnotations=true}}, with a JSON body 
of {{{}{".foo":"x"}{}}} - the exception escapes {{JSONInterceptor.intercept()}}.

h4. Affected versions

The expression dates from WW-5352 (6.4.0), but nothing reached it with such a 
name until WW-5624 wired the JSON and REST body channels through the authorizer 
in 7.2.0.

6.x is not affected: {{requireAnnotations}} has no default there, there is no 
{{ParameterAuthorizer}} at all, and {{ParametersInterceptor}} - its only caller 
- filters names through the accepted patterns first.

h4. Impact

A failed request. No authorization bypass, no amplification, nothing persists 
beyond the single request. Triaged as an ordinary robustness bug rather than a 
vulnerability.

h4. Fix

A name beginning with a nesting character names no root property, so 
authorization rejects it before normalisation rather than computing one.

The change is already included in the WW-5698 pull request 
https://github.com/apache/struts/pull/1872 (commit {{2f3ce9601}}), since that 
PR routes ModelDriven parameters through the same expression. It can be split 
into its own PR if the release notes should attribute it separately.


> StringIndexOutOfBoundsException authorizing a parameter name that begins with 
> a nesting character
> -------------------------------------------------------------------------------------------------
>
>                 Key: WW-5705
>                 URL: https://issues.apache.org/jira/browse/WW-5705
>             Project: Struts 2
>          Issue Type: Bug
>    Affects Versions: 7.2.1, 7.3.0
>            Reporter: Lukasz Lenart
>            Priority: Major
>             Fix For: 7.4.0
>
>
> {{StrutsParameterAuthorizer#isAuthorized}} derives the root property of a 
> parameter name by cutting at the first nesting character ({{.}}, {{[}} or 
> {{(}}), then lower-cases its first character:
> {code:java}int nestingIndex = indexOfAny(parameterName, NESTING_CHARS_STR);
> String rootProperty = nestingIndex == -1 ? parameterName : 
> parameterName.substring(0, nestingIndex);
> String normalisedRootProperty = Character.toLowerCase(rootProperty.charAt(0)) 
> + rootProperty.substring(1);
> {code}
> When the name _begins_ with a nesting character, {{nestingIndex}} is 0, 
> {{substring(0, 0)}} yields an empty string, and {{charAt(0)}} throws 
> {{StringIndexOutOfBoundsException}}. The exception escapes the interceptor.
> h2. Reachability
> * {{ParametersInterceptor}} is *not* affected: {{isAcceptableName()}} applies 
> {{ACCEPTED_PATTERNS}}, which requires a leading word character, before 
> authorization.
> * The JSON plugin *is*: {{JSONInterceptor}} authorizes at 
> {{isAcceptableNode()}}, whereas the accepted-pattern check runs later, in 
> {{isAcceptableLeafName()}}, and only for leaf keys.
> * The REST plugin's {{ContentTypeInterceptor}} authorizes body-derived paths 
> with no accepted- or excluded-pattern check beforehand.
> Reproduced with the container's own {{StrutsParameterAuthorizer}}, under the 
> shipped default {{struts.parameters.requireAnnotations=true}}, with a JSON 
> body whose root key is {{.foo}} - the exception escapes 
> {{JSONInterceptor.intercept()}}.
> h2. Affected versions
> The expression dates from WW-5352 (6.4.0), but nothing reached it with such a 
> name until WW-5624 wired the JSON and REST body channels through the 
> authorizer in 7.2.0.
> 6.x is not affected: {{requireAnnotations}} has no default there, there is no 
> {{ParameterAuthorizer}} at all, and {{ParametersInterceptor}} - its only 
> caller - filters names through the accepted patterns first.
> h2. Impact
> A failed request. No authorization bypass, no amplification, nothing persists 
> beyond the single request. Triaged as an ordinary robustness bug rather than 
> a vulnerability.
> h2. Fix
> A name beginning with a nesting character names no root property, so 
> authorization rejects it before normalisation rather than computing one.
> The change is already included in the WW-5698 pull request 
> https://github.com/apache/struts/pull/1872 (commit {{2f3ce9601}}), since that 
> PR routes ModelDriven parameters through the same expression. It can be split 
> into its own PR if the release notes should attribute it separately.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to