Skip to content

CONTRAST: Untrusted Deserialization from Request Body on "/api/addresses/import" page #11

@jason-at-contrast

Description

@jason-at-contrast

Vulnerability ID: LYZM-6GE9-8K14-4GLB

Application Name: CargoCats-contrast-cargo-cats-frontgateservice

Vulnerability Link: https://eval.contrastsecurity.com/Contrast/static/ng/index.html#/545a3bce-97c5-4732-af38-1ac459087b0a/applications/0f3e06e4-d3c5-47a3-a2ce-ce43b8dc3e17/vulns/LYZM-6GE9-8K14-4GLB

What Happened?

We tracked the following data from Request Body:

POST /api/addresses/import

[ HTTP body not captured ]

...which was accessed within the following code:

com.contrast.frontgateservice.controller.ApiController#importAddresses(), line 430

...and ended up being deserialized ({{#link}}https://docs.oracle.com/javase/7/docs/api/java/io/ObjectInputStream.html$$LINK_DELIM$$ObjectInputStream{{/link}}) with code that looks like this:

obj = stream.readObject()

What's the risk?

The application deserializes Java objects from an untrusted source. Because the source is untrusted, the application must consider that it may not be the expected type. An attacker could submit an object whose type is any Java class on the application's classpath.

Attackers could choose a class with a Serializable#readObject() method that can be re-purposed towards malicious ends. There have been several commonly available classes can be used to effect remote code execution.

Recommendation

There are only a few options for securing the deserialization of untrusted objects. The first, and most safe option, is to remove the deserializing of untrusted input completely. Although the recommendations today appear to be totally effective, it's worth noting that attacks against serialization have been getting more effective for many years. The consensus amongst security researchers is that developers should be moving away from Java's serialization when possible.

If serialization must occur on untrusted data, the ObjectInputStream must be hardened to ensure only the expected classes are being deserialized. Here is an example of vulnerable code:

InputStream untrustedStream = request.getInputStream();
ObjectInputStream in = new ObjectInputStream(untrustedStream);
Acme acmeObject = (Acme)in.readObject();

By overriding the ObjectInputStream#resolveClass() method, we can ensure that only the expected classes are deserialized. In the following secure example, only the Acme and String classes will be allowed to be deserialized. Anything else seen will cause a SecurityException to be found:

InputStream untrustedStream = request.getInputStream();
List<Class> safeClasses = Arrays.asList(new Class[] { Acme.class, String.class });
ObjectInputStream in = new ObjectInputStream(untrustedStream) {
protected Class<?> resolveClass(ObjectStreamClass desc) {
Class<?> clazz = null;
try {
clazz = super.resolveClass(desc);
if (clazz.isArray() || clazz.isPrimitive() || safeClasses.contains(clazz) ) {
return clazz;
}
} catch (ClassNotFoundException | IOException e) {
throw new SecurityException("Attempt to deserialize unauthorized class: " + clazz.getName());
}
}
};
Acme acmeObject = (Acme)in.readObject();

Check out our AI-generated Intelligent Remediation Guidance! https://eval.contrastsecurity.com/Contrast/static/ng/index.html#/545a3bce-97c5-4732-af38-1ac459087b0a/vulns/LYZM-6GE9-8K14-4GLB/overview/recommendation

First Event


Stack:
  org.apache.catalina.core.ApplicationPart.getInputStream(ApplicationPart.java:97)
  org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile.getInputStream(StandardMultipartHttpServletRequest.java:254)
  com.contrast.frontgateservice.controller.ApiController.importAddresses(ApiController.java:429)
  java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0()
  java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke()
  java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke()
  java.base/java.lang.reflect.Method.invoke()
  org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
  org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)
  org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
  org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
  org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
  org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
  org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1072)
  org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:965)
  org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
  org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
  javax.servlet.http.HttpServlet.service(HttpServlet.java:555)
  org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
  javax.servlet.http.HttpServlet.service(HttpServlet.java:623)
  org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:209)
  org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
  org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
  org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
  org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:337)
  org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:115)
  org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:81)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
  org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:122)
  org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:116)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
  org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:126)
  org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:81)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
  org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:109)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
  org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:149)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
  org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
  org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:219)
  org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:213)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
  org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103)
  org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
  org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90)
  org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75)
  org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
  org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:110)
  org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:80)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
  org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:55)
  org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
  org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:221)
  org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:186)
  org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)
  org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
  org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
  org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
  org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
  org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
  org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
  org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
  org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
  org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)

Last Event

Stack:
  java.base/java.io.ObjectInputStream.readObject()
  com.contrast.frontgateservice.controller.ApiController.importAddresses(ApiController.java:430)
  java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0()
  java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke()
  java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke()
  java.base/java.lang.reflect.Method.invoke()
  org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
  org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)
  org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
  org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
  org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
  org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
  org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1072)
  org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:965)
  org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
  org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
  javax.servlet.http.HttpServlet.service(HttpServlet.java:555)
  org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
  javax.servlet.http.HttpServlet.service(HttpServlet.java:623)
  org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:209)
  org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
  org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
  org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
  org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:337)
  org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:115)
  org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:81)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
  org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:122)
  org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:116)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
  org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:126)
  org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:81)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
  org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:109)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
  org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:149)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
  org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
  org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:219)
  org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:213)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
  org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103)
  org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
  org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90)
  org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75)
  org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
  org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:110)
  org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:80)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
  org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:55)
  org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
  org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:221)
  org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:186)
  org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)
  org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
  org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
  org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
  org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
  org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
  org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
  org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
  org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
  org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
  org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
  org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
  org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)

HTTP Request

POST http://cargocats.localhost:80/api/addresses/import HTTP/1.1
Accept: /
Accept-Encoding: gzip, deflate
Content-Length: 3328
Content-Type: multipart/form-data; boundary=e360a79f45704334435a4f0b03a33c64
Cookie: JSESSIONID=903F43FB86C14239EC9DB486B26F6436
Host: cargocats.localhost
User-Agent: python-requests/2.31.0
X-Forwarded-Host: cargocats.localhost
X-Forwarded-Port: 80
X-Forwarded-Proto: http
X-Forwarded-Scheme: http
X-Real-Ip: 10.1.5.76
X-Request-Id: 5190b4b86dcf33c9a2de87d44970a72c
X-Scheme: http

References

https://foxglovesecurity.com/2015/11/06/what-do-weblogic-websphere-jboss-jenkins-opennms-and-your-application-have-in-common-this-vulnerability/
https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html

Session ID: eeb7a07a32284bacbd32ec513b0b4147
artifactHash: b91aebb6

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions