java.lang.ArrayIndexOutOfBoundsException is thrown in XorCsrfTokenRequestAttributeHandler during attack Affects version spring-security 6.0.3 (I have not tested 6.1)

java.lang.ArrayIndexOutOfBoundsException: arraycopy: last destination index 36 out of bounds for byte[8]
        at org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler.xorCsrf(XorCsrfTokenRequestAttributeHandler.java:119) ~[spring-security-web-6.0.3.jar!/:6.0.3]
        at org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler.getTokenValue(XorCsrfTokenRequestAttributeHandler.java:99) ~[spring-security-web-6.0.3.jar!/:6.0.3]
        at org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler.resolveCsrfTokenValue(XorCsrfTokenRequestAttributeHandler.java:73) ~[spring-security-web-6.0.3.jar!/:6.0.3]
        at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:121) ~[spring-security-web-6.0.3.jar!/:6.0.3]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.9.jar!/:6.0.9]
        at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:185) ~[spring-security-web-6.0.3.jar!/:6.0.3]
        at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:172) ~[spring-security-web-6.0.3.jar!/:6.0.3]
        at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:133) ~[spring-security-web-6.0.3.jar!/:6.0.3]
        at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) ~[spring-security-web-6.0.3.jar!/:6.0.3]
        at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) ~[spring-security-web-6.0.3.jar!/:6.0.3]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.9.jar!/:6.0.9]
        at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:185) ~[spring-security-web-6.0.3.jar!/:6.0.3]
        at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:172) ~[spring-security-web-6.0.3.jar!/:6.0.3]
        at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:133) ~[spring-security-web-6.0.3.jar!/:6.0.3]

To reproduce modify csrf token values on client side (cookie based tokens)

Expected behavior getTokenValue should validate encoded token length and return null if value is incorrect. Generating stacktrace for exception is much more expensive and may impact performance.

Comment From: RahulKumarNitP

Can I work on this issue?

Comment From: jzheaux

@kevin2jordan, are you still interested in this issue? A PR would be most welcome!

Comment From: RahulKumarNitP

@jzheaux Have submitted the PR: https://github.com/spring-projects/spring-security/pull/13550 Would appreciate the PR review and any comments/feedback for the PR.

Comment From: maximilianschweitzer

This issue is not completely fixed yet. During an attack we still get

java.lang.ArrayIndexOutOfBoundsException: arraycopy: last destination index 36 out of bounds for byte[1]
    at org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler.xorCsrf(XorCsrfTokenRequestAttributeHandler.java:122)
    at org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler.getTokenValue(XorCsrfTokenRequestAttributeHandler.java:99)
    at org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler.resolveCsrfTokenValue(XorCsrfTokenRequestAttributeHandler.java:73)
    at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:121)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)
    at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91)

in version spring-security 6.2.1.

Specifically, the problem lies in the following line in the file XorCsrfTokenRequestAttributeHandler.java:

System.arraycopy(csrfBytes, 0, xoredCsrf, 0, csrfBytes.length);

Here, you're attempting to copy csrfBytes.length elements from csrfBytes into xoredCsrf. However, xoredCsrf is only as long as the smaller of the two arrays (randomBytes.length or csrfBytes.length), as determined by the preceding line:

byte[] xoredCsrf = new byte[len]; // len is the smaller of randomBytes.length or csrfBytes.length

This will cause an ArrayIndexOutOfBoundsException when csrfBytes.length is greater than len, because you're trying to copy more elements than xoredCsrf can hold.

To fix this, you should copy only len elements, not csrfBytes.length elements, since len is the length of the destination array xoredCsrf.

// Copy only len elements
System.arraycopy(csrfBytes, 0, xoredCsrf, 0, len);

Comment From: pyyx

java.lang.ArrayIndexOutOfBoundsException: arraycopy: last destination index 36 out of bounds for byte[0]
    at java.lang.System.arraycopy(Native Method) ~[?:?]
    at org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler.xorCsrf(XorCsrfTokenRequestAttributeHandler.java:119) ~[spring-security-web-6.1.8.jar:6.1.8]

In version 6.1.8, I had the same problem as @maximilianschweitzer .Hopefully this will be resolved

Comment From: kratosmy

Hi, @jzheaux, I have made a quick fix for this issue. Please kindly help review.

Comment From: sjohnr

@maximilianschweitzer thanks for reporting the additional finding. I am considering this a bug and plan to backport the fix to OSS supported branches (5.8.x, 6.2.x and 6.3.x) as well. See gh-15184.

@kratosmy thanks for submitting a PR. I will provide feedback on the PR.