- vừa được xem lúc

CVE-2022-22980 - Spring Data MongoDB remote code execution.

0 0 37

Người đăng: logbasex

Theo Viblo Asia

1. Prerequisites

  • Ubuntu GNOME
  • Spring Boot
  • MongoDB

2. Overview

CVE-2022-22980 là một lỗ hổng bảo mật của thư viện spring-data-mongodb có thể khiến attacker chạy bất kì câu lệnh nào trên máy chủ bằng việc truyền một đoạn mã độc vào user input để trigger thư viện thực thi nếu như ứng dụng Spring Boot + MongoDB sử dụng JSON based query methods với @Query, @Aggregation annotaion có chứa parameter expression SpEL - Spring Expression Language tương tự như sau:

	@Query("{ 'firstName' : ?#{?0} }") Customer findByFirstName(String firstName);

Đến đây bạn có thể thực hiện mở ứng dụng gnome-calculator trên Ubuntu GNOME bằng cách thực thi câu lệnh truy vấn như thế này:

 repository.findByFirstName("T(java.lang.Runtime).getRuntime().exec(\"gnome-calculator\")");

Do lổ hổng bảo mật này đã được report từ hơn 1 tháng trước (13/06/2022) nên nếu bạn sử dụng spring-data-mongodb depedency mới release gần đây (v.3.3.5 hay v3.4.1 trở lên) thì sẽ không khai thác được lỗ hổng này.

3. Exploitation Explain

Trước khi đi vào chi tiết, các bạn có thể xem toàn bộ source code ở đây

Vì lỗ hổng này liên quan trực tiếp đến cách Spring Boot framework xử lý SpEL query parameter của MongoDB interface repository, nên để hiểu chúng ta cần phải biết quá trình một câu truy vấn được xử lý ra sao.

  1. Khai báo CustomerRepository

  1. Tiến hành gọi câu truy vấn

Bạn có thể thấy, CustomerRepository là một proxy class (JdkDynamicAopProxy), có nghĩa rằng tại runtime (dynamic), Spring Boot framework đã tiến hành proxy CustomerRepository bean và khi bạn bạn tiến hành gọi câu truy vấn thì bạn phải gọi qua proxy class trước.

Khi tiến hành debug, nhìn qua callstack bạn sẽ thấy khá loạn vì Spring Boot framework gọi qua nhiều proxy class khác nhau, nhưng thực sự để mà nói thì source code quá lớn và phức tạp để chúng ta có thể hiểu một cách tường tận một workflows hoàn chỉnh nhưng có thể hiểu nôm na như sau (theo cách hiểu của mình):

Khi gọi đến phương thức findByFirstName() trong CustomerRepository, thì tất cả mọi lời gọi phương thức đều phải đi qua một handler duy nhất - đó chính là phương thức invoke().

Bên trong phương thức invoke() này, chúng ta sẽ thực thi các bước tiền xử lý trước khi tiến hành chọc thẳng vào database truy vấn dữ liệu, đơn cử như là StringBasedMongoQuery#createQuery(). Tuy nhiên trước khi gọi #createQuery() thì Spring Boot framework cần binding paramter vào câu query trước thông qua phương thức getBindingContext():

Tiếp theo là bước phân tích câu query và gán giá trị từ tham số.

Bạn có thể thấy, với parameter expression đầu vào là T(java.lang.Runtime).getRuntime().exec(\"gnome-calculator\") thì sau khi được xử lý giá trị của biến expression vẫn như thế, điều đó có nghĩa là phương thức ParameterBindingJsonReader#bindableValueFor() không phát hiện ra được đây là một đoạn mã được truyền vào thay vì một giá trị thông thường.

Và lỗ hổng bảo mật xảy ra ở đây chính là khi gọi phương thức ParameterBindingJsonReader#evaluateExpression(), expression truyền vào được covert thành một UUNIXProcess và tiến hành việc mở ứng dụng gnome-calculator ngay sau đó.

Debug callstack:

bindableValueFor:389, ParameterBindingJsonReader (org.springframework.data.mongodb.util.json)
readBsonType:304, ParameterBindingJsonReader (org.springframework.data.mongodb.util.json)
decode:227, ParameterBindingDocumentCodec (org.springframework.data.mongodb.util.json)
decode:180, ParameterBindingDocumentCodec (org.springframework.data.mongodb.util.json)
createQuery:121, StringBasedMongoQuery (org.springframework.data.mongodb.repository.query)
doExecute:122, AbstractMongoQuery (org.springframework.data.mongodb.repository.query)
execute:107, AbstractMongoQuery (org.springframework.data.mongodb.repository.query)
invoke:-1, 1601756706 (org.springframework.data.repository.core.support.RepositoryMethodInvoker$RepositoryQueryMethodInvoker$$Lambda$580)
doInvoke:137, RepositoryMethodInvoker (org.springframework.data.repository.core.support)
invoke:121, RepositoryMethodInvoker (org.springframework.data.repository.core.support)
doInvoke:159, QueryExecutorMethodInterceptor (org.springframework.data.repository.core.support)
invoke:138, QueryExecutorMethodInterceptor (org.springframework.data.repository.core.support)
proceed:186, ReflectiveMethodInvocation (org.springframework.aop.framework)
invoke:80, DefaultMethodInvokingMethodInterceptor (org.springframework.data.projection)
proceed:186, ReflectiveMethodInvocation (org.springframework.aop.framework)
invoke:97, ExposeInvocationInterceptor (org.springframework.aop.interceptor)
proceed:186, ReflectiveMethodInvocation (org.springframework.aop.framework)
invoke:215, JdkDynamicAopProxy (org.springframework.aop.framework)
findByFirstName:-1, $Proxy44 (com.sun.proxy)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invokeJoinpointUsingReflection:344, AopUtils (org.springframework.aop.support)
invokeJoinpoint:198, ReflectiveMethodInvocation (org.springframework.aop.framework)
proceed:163, ReflectiveMethodInvocation (org.springframework.aop.framework)
invoke:137, PersistenceExceptionTranslationInterceptor (org.springframework.dao.support)
proceed:186, ReflectiveMethodInvocation (org.springframework.aop.framework)
invoke:215, JdkDynamicAopProxy (org.springframework.aop.framework)
findByFirstName:-1, $Proxy44 (com.sun.proxy)
run:65, AccessingDataMongodbApplication (com.logbasex.accessingdatamongodb)
callRunner:777, SpringApplication (org.springframework.boot)
callRunners:761, SpringApplication (org.springframework.boot)
run:310, SpringApplication (org.springframework.boot)
run:1312, SpringApplication (org.springframework.boot)
run:1301, SpringApplication (org.springframework.boot)
main:34, AccessingDataMongodbApplication (com.logbasex.accessingdatamongodb)

Vào một tuần sau khi lỗ hổng được report (20/06/2022), Spring Boot framework tung ra hai bản vá mới cho spring-data-mongodb dependency là v3.3.6v3.4.2. Vậy chúng ta hãy cùng xem source code đã thay đổi như thế nào nhé.

Biến expression không còn mang giá trị cũ nữa: #_QVar0, qua đó thì lỗ hổng đã được vá.

Fixed commit:

4. Workaround

  1. Sử dụng parameter array syntax:
    @Query("{ 'firstName' : ?#{?[0]} }") Customer findByFirstName(String firstName);
    
  2. Sử dụng custom repository.

5. References

  1. Spring Data MongoDB SpEL Expression Injection Vulnerability (CVE-2022-22980)
  2. CVE-2022-22980: Spring Data MongoDB SpEL Expression injection vulnerability through annotated repository query methods
  3. PoC CVE-2022-22980
  4. [CVE-2022-22980] Spring Data MongoDB SpEL Expression injection
  5. Code difference between vulnerable version and fixed version

Bình luận

Bài viết tương tự

- vừa được xem lúc

Tổng hợp các bài hướng dẫn về Design Pattern - 23 mẫu cơ bản của GoF

Link bài viết gốc: https://gpcoder.com/4164-gioi-thieu-design-patterns/. Design Patterns là gì. Design Patterns không phải là ngôn ngữ cụ thể nào cả.

0 0 302

- vừa được xem lúc

Học Spring Boot bắt đầu từ đâu?

1. Giới thiệu Spring Boot. 1.1.

0 0 277

- vừa được xem lúc

Cần chuẩn bị gì để bắt đầu học Java

Cần chuẩn bị những gì để bắt đầu lập trình Java. 1.1. Cài JDK hay JRE.

0 0 50

- vừa được xem lúc

Sử dụng ModelMapper trong Spring Boot

Bài hôm nay sẽ là cách sử dụng thư viện ModelMapper để mapping qua lại giữa các object trong Spring nhé. Trang chủ của ModelMapper đây http://modelmapper.org/, đọc rất dễ hiểu dành cho các bạn muốn tìm hiểu sâu hơn. 1.

0 0 194

- vừa được xem lúc

[Java] 1 vài tip nhỏ khi sử dụng String hoặc Collection part 1

. Hello các bạn, hôm nay mình sẽ chia sẻ về mẹo check String null hay full space một cách tiện lợi. Mình sẽ sử dụng thư viện Lớp StringUtils download file jar để import vào thư viện tại (link).

0 0 71

- vừa được xem lúc

Deep Learning với Java - Tại sao không?

Muốn tìm hiểu về Machine Learning / Deep Learning nhưng với background là Java thì sẽ như thế nào và bắt đầu từ đâu? Để tìm được câu trả lời, hãy đọc bài viết này - có thể kỹ năng Java vốn có sẽ giúp bạn có những chuyến phiêu lưu thú vị. DJL là tên viết tắt của Deep Java Library - một thư viện mã ng

0 0 139