Spring Web Flow past vulnerabilities research

theme

Prefeace

Spring Web Flow xây dựng dựa trên Spring MVC và cho phép triển khai các “luồng” của ứng dụng web. Một luồng bao gồm một chuỗi ghi lại các bước người dùng thực hiện. Nó bao gồm nhiều yêu cầu HTTP, có trạng thái, xử lý dữ liệu giao dịch, có thể tái sử dụng, dynamic,…

Câu trên dịch từ overview của project khá thô nhưng căn bản để dễ hiểu thì có thể coi nó như view-state trong ASP.NET, lưu lại luồng hoạt động trên ứng dụng nhằm phục phụ nhiều yêu cầu nghiệp vụ khác nhau. Có thể kể đến như app shopping hay authentication flow - tiêu biểu là con CAS có sử dụng lib này mà mình vừa có bài phân tích, cũng chính là lý do mình quyết định tìm hiểu thêm lib này.

CVE-2017-4971: Data Binding Expression Vulnerability in Spring Web Flow

Lab debug dựa trên sample chính thức: https://github.com/spring-projects/spring-webflow-samples - commit: f2312608cb924666d5d068e8016c1727b006d372 hay spring-webflow version 2.4.4

Theo advisory: CVE-2017-4971

Đọc thì khá khó hiểu, nhưng nói chung để exploit được bug này cần 2 điều kiện không quá khó gặp. App chạy cấu hình default không set lại thuộc tính MvcViewFactoryCreator.setUseSpringBeanBinding thành true. Như sample trên thì mặc định đã set lại nên không dính: setUseSpringBeanBinding

Nguyên nhân do khi truy cập lần đầu /hotels/booking??hotelId=1 app sẽ tìm đến cấu hình booking-flow.xml và thực hiện khởi tạo các ViewFactory tương ứng với các view-state trong file. Trước khi tạo sẽ kiểm tra thuộc tính useSpringBeanBinding: useSpringBeanBinding

Nếu giá trị là true sẽ dùng BeanWrapperExpressionParser như hình, nếu không thì dùng parser default SpelExpressionParser.

Ngoài ra cần có 1 view-state không cấu hình binding như view reviewBooking (vẫn cần gán model="something..." để web flow thực hiện binding), còn như enterBookingDetails thì không được: view-state

Nguyên nhân do khi tiến hành bind parameters với model tại AbstractMvcView#bind(), với view-state là enterBookingDetails, do view này có cấu hình binding nên binderConfiguration != null, request đi vào addModelBindings() addModelBindings

và mapping property match với model đã cấu hình, không có giá trị nào ta kiểm soát được ở đây: property

Điều mình muốn là binderConfiguration == null - tương ứng với view-state là reviewBooking, khi đó request đi vào addDefaultMappings(). Lúc này method lấy ra field chính là parameter keys có prefix là _ đi vào addEmptyValueMapping(): prefix_

Tại đây field sẽ được parse expression bởi expressionParserBeanWrapperExpressionParser như phần trên do app đã cấu hình setUseSpringBeanBindingtrue. expressionParser

Nếu không cấu hình thuộc tính này như advisory: commented

false

expressionParser sẽ là SpelExpressionParser, RCE đơn giản với request sau đến view reviewBooking:

POST /booking_mvc/hotels/booking?execution=e2s2 HTTP/1.1
Host: devme4f.io:8080
Content-Length: 60
Content-Type: application/x-www-form-urlencoded
Cookie: JSESSIONID=01DFF10A46C8E55EB4668C28044381C6
Connection: close

_eventId_confirm=&_csrf=89ce2001-ec0f-4d08-8133-c6f4affd6394&_T(java.lang.Runtime).getRuntime().exec("calc.exe")

hitted: hitted

Output dễ đoán: Output

mitigation

Bản fix 2.4.5 mặc định method addEmptyValueMapping() sẽ dùng BeanWrapperExpressionParser fix_2.4.5

refs

CVE-2017-8039: Data Binding Expression Vulnerability in Spring Web Flow

Theo advisory: CVE-2017-8039

Thì do bản fix trước chỉ sửa mỗi method addEmptyValueMapping() nên vẫn còn method khác có thể lợi dụng được. Thật ra khi chưa xem CVE này thì trong quá trình debug mình đã thấy rồi, để thấy bản fix trước khá tạm bợ - fix cho xong. Anyway, method đó là addDefaultMapping(): addDefaultMapping

Nếu bạn để ý thì method này được gọi ngay dưới addEmptyValueMapping() mà mình đã show ở trên, khi parameter không có prefix _ thì sẽ vào đây, còn lại xử lý parse expression y chang: prefix

mitigation

Bản fix mới check nếu expressionParser là BeanWrapperExpressionParser sẽ pass, nếu không thì expression phải thỏa mãn hàm check checkModelProperty(): checkModelProperty

checkModelProperty() duyệt expression và lấy ra từng property để kiểm tra xem property có nằm trong model đang muốn binding: method_checkModelProperty

Tạm bỏ qua chuyện có bypass được hàm này không, bản latest 3.0.0 chuyển qua dùng SimpleEvaluationContext, cái được biết đến là safe khi chỉ hỗ trợ evaluate các expression đơn giản so với StandardEvaluationContext dùng được hầu hết java class, có bypass được method checkModelProperty khi dùng SpelExpressionParser thì inject cũng không làm được gì. SimpleEvaluationContext vẫn vulnerable với ReDoS nhưng nếu app chạy java version cao thì cũng không redos được!

SimpleEvaluationContext

refs