Java Code Auditing for Beginners 06: File Inclusion Vulnerabilities and Real-World Cases

Summary0x00 Preface. I created this series because online Java code-audit material is usually fragmented and unfriendly to beginners. I am also learning Java auditing, so the series records and summarizes that process. It is intended for readers with basic Java syntax knowledge and will cover…

javaJava Code Auditing

0x00 Preface

I created this series because online Java code-audit material is usually fragmented and unfriendly to beginners. I am also learning Java auditing, so the series records and summarizes the process.

This series is primarily forreaders with basic Java syntax knowledge. The series covers audit environments, SQL injection, XSS, SSRF, RCE, file inclusion, deserialization, classic Struts2 and WebLogic vulnerabilities, Fastjson, Jackson, and related case studies. The order may change, but the overall scope will remain. I hope the series proves useful.

The following articles are currently complete:

[Introduction to Java Code Auditing—01] Preparing for an Audit /codeaudit/588.html

[Introduction to Java Code Auditing—02] SQL Injection Principles and Case Study /codeaudit/600.html

[Introduction to Java Code Auditing—03] XSS Principles and Case Study /codeaudit/605.html

[Introduction to Java Code Auditing—04] SSRF Principles and Case Study /codeaudit/678.html

[Introduction to Java Code Auditing—05] RCE Principles and Case Study /codeaudit/759.html

0x01 Preface

Download the RCE test source:

https://github.com/cn-panda/JavaCodeAudit

Import the project to obtain the following directory structure:

1.png
2.png

The project was written to demonstrate local and remote file inclusion and includes a Spring Boot + Thymeleaf test application.

0x02 Vulnerability Principles

File inclusion is most common in PHP web applications. Functions such as include and require can load attacker-controlled files; regardless of suffix, PHP parses the included contents as PHP code.

An attacker could upload a web shell with a .txt or .jpg suffix and include it to obtain a shell.

Does Java have similar inclusion vulnerabilities? First examine Java's native ways to include files.

JSP supports static and dynamic file inclusion.

The first form is static inclusion:<%@include file="test.jsp"%>

Static inclusion means that the included parameter value—such as thefileparameter cannot be assigned dynamically: its declaration-time value is fixed at runtime. Static inclusion is therefore generally not vulnerable on its own, although interactions with other flaws remain possible.

After static inclusion comes dynamic inclusion, which has two forms:

TEXT
<jsp:include page="<%=file%>"></jsp:include>
<jsp:include page ="<%=file%>"/>
TEXT
<c:import url="<%= url%>"></c:import>

The first dynamic form is more complex than static inclusion. Static inclusion is an include directive with a single file attribute whose path may be relative or absolute, but not<%=...%>expression, but here file may use<%=...%>expression represented by it.

The second dynamic form is essentially the same as the first. The core library's<c:import>and <jsp:include> is also a request-time operation. It inserts other web resources into the current JSP page, with resources selected through url; this is also<c:import> only required attribute. Relative URLs are allowed and resolved against the current page URL.

For example, if the current page URL ishttp://127.0.0.1/admin/index.jsp. If the referenced URL is/user/edit.jsp, so the final resolved URL ishttp://127.0.0.1/admin/user/edit.jsp

If url begins with a slash, it is an absolute URL inside the local JSP container. If no context attribute is specified, such an absolute URL references a resource inside the current Servlet context. Ifcontext attribute explicitly specifies a context, the absolute local URL is resolved against that Servlet context. 

Of course,<c:import> is not limited to local content. It can be a complete URI with a protocol and hostname, and the protocol need not be HTTP. <c:import> url attribute can use java.net.URL any protocol supported by the class (that is,http, https, ftp, file,jar,mailto,netdoc)。

These characteristics meanDynamic inclusion can create file-inclusion vulnerabilities., but this differs greatly from PHP inclusion.For Java local file inclusion, the impact is limited to file read or download;normallydoes not cause command or code execution.. Java normally does not execute a non-JSP file as Java code. A JSP web shell could be accessed directly without inclusion, except perhaps where directory permissions make inclusion a theoretical bypass.

Java normally does not parse non-JSP files as Java. Container features can override this—for example, Tomcat AJP Ghostcat (CVE-2020-1938) can parse arbitrary suffixes as JSP and lead to RCE.

Also note that static and dynamic inclusion differ inwhen they executeis substantially different.Static inclusion occurs during translation.. The included and containing pages are combined and compiled into a single result. By contrast,Dynamic inclusion occurs during request processing.. JSP forwards the request—not redirects—to the included page, writes its result to the browser, then resumes the original page. The included file and containing page are compiled separately by the JSP compiler.

With that background, examine the example we wrote.

First, local file inclusion:

JAVA
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
 String file = request.getParameter("file");
%>

<jsp:include page="<%=file%>"></jsp:include>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
 <title>JSP File Include Test (local)</title>
</head>
<body>
File Include Test Page. <br>
</body>
</html>

When we setfileparameter value todata.txt, the file contents are returned:

3.png

There is a limitation: the included path must be under the current web root, and the file must be text, such asjsptxtand similar text files; images and other binary files are unsupported.

Attempting to access an image resource produces an error:

4.png

and cannot parse .java files, for the reasons above:

5.png

Next, remote inclusion:

JAVA
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
 <title>Remote File Include Test</title>
 <%  String url = request.getParameter("url"); %>
 <c:import url="<%=url%>"></c:import>

 <meta http-equiv="pragma" content="no-cache">
 <meta http-equiv="cache-control" content="no-cache">
 <meta http-equiv="expires" content="0">
</head>
<body>
This is my JSP page. <br>
</body>
</html>

Remote inclusion can include both remote and local files:

6.png
7.png

including image resources (the image is not parsed, but its bytes are present):

8.png

Resolve a remote file:

9.png
10.png

For deeper exploitation, create an XSS payload on a testing platform, embed its JavaScript in a remote HTML page, and access it through the inclusion flaw:

11.png

The cookie was retrieved successfully.

12.png

The file and netdoc protocols can also read arbitrary files:

13.png
14.png

Is that all Java file inclusion can do?

No. Earlier I mentioned <c:import> url attribute can use java.net.URLany protocol supported by the class, including jar. The jar protocol is commonly used in XXE and also works in Java file inclusion.

The exploitation approach is similar toK0rz3ntechnique mentioned by the researcherUpload files through the jar protocolworks similarly: use a temporary file and trigger an error to disclose its path. Because of time, I performed only a basic test:

15.png
16.png

The temporary file exists in the directory but is quickly deleted. With an appropriate technique (K0rz3ntechnique mentioned by the researcher) may keep the file on the target longer. How to exploit the upload remains an open question for further thought.

Beyond native Java inclusion, framework-level inclusion issues exist. Here is one example.

JSP file inclusion as usually understood means a controllable<jsp:include<c:importresource-reference attribute. PHP file inclusion parses arbitrary files as PHP. Extending that principle, Spring Boot Thymeleaf template injection can, in one sense, be viewed as file inclusion. A classic example:

JAVA
@GetMapping("/admin")
public String path(@RequestParam String language) {
    return "language/" + language + "/admin";
}

This is controller code from a Spring Boot project. The Thymeleaf directory is:

17.png

The code is essentially a language selector. Chinese readers receivelanguageargument iscn; for English readers, setslanguageargument isen. The code itself appears sound, but the use of a Thymeleaf template creates the issue.

In Spring Boot + Thymeleaf, a controllable view name causes a vulnerability. After a controller returns, Spring asks Thymeleaf to locate and render a template; during lookup, the parameter can execute as SpEL and produce RCE. This article only summarizes the rendering flow:

  • createView() creates a View from the view name
18.png
  • renderFragment() resolves a template name from the view name
19.png

So followrenderFragment()See how the template name is parsed:

JAVA
    protected void renderFragment(Set<String> markupSelectorsToRender, Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        String templateName;
        Set<String> markupSelectors, processMarkupSelectors;
        ServletContext servletContext = getServletContext();
        String viewTemplateName = getTemplateName();
        ISpringTemplateEngine viewTemplateEngine = getTemplateEngine();

      ...

            try {
                fragmentExpression = (FragmentExpression)parser.parseExpression((IExpressionContext)context, "~{" + viewTemplateName + "}");
            } catch (TemplateProcessingException e) {
                throw new IllegalArgumentException("Invalid template name specification: '" + viewTemplateName + "'");
            }

            FragmentExpression.ExecutedFragmentExpression fragment = FragmentExpression.createExecutedFragmentExpression((IExpressionContext)context, fragmentExpression);

            templateName = FragmentExpression.resolveTemplateName(fragment);
            markupSelectors = FragmentExpression.resolveFragments(fragment);
            Map<String, Object> nameFragmentParameters = fragment.getFragmentParameters();

            if (nameFragmentParameters != null) {

                if (fragment.hasSyntheticParameters())
                {

                    throw new IllegalArgumentException("Parameters in a view specification must be named (non-synthetic): '" + viewTemplateName + "'");
                }

                context.setVariables(nameFragmentParameters);
            }
        }

       ...
	 }

Here, the template name (viewTemplateName) is concatenated "~{" + viewTemplateName + "}", then usesparseExpressionparses it. Continue followingparseExpressionshows that the parameter is preprocessed and ultimately executed through SpEL.

So setlanguageparameter to a chosen SpEL expression to achieve RCE:

20.png

Suppose a flaw uploads a non-JSP file to a controllable location and the controller logic is:

JAVA
@GetMapping("/test")
public String test(@RequestParam String path) {
    return "/" + path;
}

This can produce the following scenario:

Settest.htmlupload it to the template directorytemplateroot directory: test.htmlIts contents are:

HTML
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
 xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Hello World!</title>
</head>
<body>
<h1 th:inline="text">Hello</h1>
<p th:text="@{__${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec('whoami').getInputStream()).next()}__}"></p>
</body>
</html>

Visiting it reproduces PHP-style file inclusion:

21.png

I have encountered this scenario, but arbitrary uploads of non-JSP files to controllable locations are uncommon, so it is not broadly representative.

Common Thymeleaf expressions include:

${…}: variable expression *{…}: selection expression #{…}: message expression @{…}: link expression ~{…}: fragment expression

If a parameter inside a template file is controllable:

22.png

and the controller logic is:

JAVA
@GetMapping("/page")
public String path(@RequestParam String exp, Model model) {
    model.addAttribute("exp", exp);
    return "exp";
}

can likewise achieve RCE:

23.png

The Thymeleaf issue here follows file-inclusion principles, but this class has a specific name: SSTI, or server-side template injection.

I categorize it as file inclusion because I have seen a real example. The brief discussion may help readers consider similar flaws in other frameworks.

How should this class of vulnerability be fixed or avoided?

0x03 Remediation

JSP inclusion has limited impact and is extremely rare because imported resources are almost always hard-coded.

Template frameworks such as Thymeleaf are more likely to expose this issue. The following discusses Thymeleaf remediation; other engines should be fixed according to their own behavior.

As noted above,createView()creates the corresponding View from a view name.View. Inside this method, Thymeleaf applies special handling toredirect:andforward:is handled specially:

24.png

Follow the RedirectView class and observe:

25.png

The logic uses the returned value to choose between a redirect (redirect:) or a request forward (forward:), then calls the native Servlet redirect or forward method. This avoids the SpEL execution path and therefore the vulnerability.

When the view name is controllable, code using the following approaches is unaffected:

  • Use@ResponseBodyannotation

  • rerturn contents when processed byredirect:orforward:prefix

  • parameter containsHttpServletResponse; response has already been handled

  • Use the latest Thymeleaf release, which fixes both controllable view names and controllable preprocessing variables.

JSP file inclusion is difficult to find. Most CVEs I found were arbitrary file reads, and prior real cases cannot be published, so no case is included here.

0x05 Conclusion

This article was delayed for a long time and contains more conceptual exploration than extensive case material. I hope it still helps beginners.

0x06 References

https://vulncat.fortify.com/zh-cn/detail?id=desc.dataflow.dotnet.dangerous_file_inclusion https://xz.aliyun.com/t/3357 http://x2y.pw/2020/11/15/Thymeleaf-template-vulnerability-analysis/ https://waylau.gitbooks.io/thymeleaf-tutorial/content/docs/standard-expression-syntax.html