Spring Web Flow past vulnerabilities research
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
- advisory: https://spring.io/security/cve-2017-4971
- version affected:
- Spring Web Flow
2.4.0 to 2.4.4
- Older unsupported versions are also affected
- 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:
Đọ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:
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
:
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:
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()
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:
Đ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()
:
Tại đây field
sẽ được parse expression bởi expressionParser
là BeanWrapperExpressionParser
như phần trên do app đã cấu hình setUseSpringBeanBinding
là true
.
Nếu không cấu hình thuộc tính này như advisory:
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:
Output dễ đoán:
mitigation
Bản fix 2.4.5
mặc định method addEmptyValueMapping()
sẽ dùng BeanWrapperExpressionParser
refs
CVE-2017-8039: Data Binding Expression Vulnerability in Spring Web Flow
- advisory: https://spring.io/security/cve-2017-8039
- version affected:
- Spring Web Flow
2.4.0 to 2.4.5
- Older unsupported versions are also affected
- Spring Web Flow
Theo advisory:
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()
:
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:
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()
duyệt expression và lấy ra từng property để kiểm tra xem property có nằm trong model đang muốn binding:
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!
- Use SimpleEvaluationContext in AbstractMvcView [SWF-1722]: https://github.com/spring-projects/spring-webflow/issues/897
- https://stackoverflow.com/questions/69820458/spel-how-to-protect-against-injection-attacks