flowable / flowable/flowable-engine
[SECURITY] Java deserialization of `type=serializable` REST variables (CVSS 8.0), unsandboxed deployable artifacts, order-column SQLi, HTTP-task SSRF
- Dominant language
- Java
- Stars
- 9.5k
- Forks
- 2.9k
- Avg merge
- 7h 8m
- Merged PRs (30d)
- 2
Description
> Consolidated to avoid flooding the tracker — happy to split on request. All findings verified dynamically by the reporter on the standard flowable-rest deployment (2026-09-02/03).
## Finding 1 — Unrestricted deserialization of `type=serializable` variables (High)
**Summary.** The REST API accepts multipart variables of `type=serializable`; the raw bytes pass through `ObjectInputStream.readObject()` on the engine host with **no class allow-list**. We confirmed `readObject` executes and reconstructs attacker-supplied objects. The shipped `-Djdk.serialFilter=maxarray=...;maxdepth=...` bounds graph **size only** — gadget classes on the engine classpath remain viable.
**Affected:** flowable-engine @ `1663e588179d` (flowable-app-rest module), standard flowable-rest deployment.
**CVSS v3.1 (self-assessed):** 8.0 — `AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H` (AC:H for gadget dependency; demo deployments with published credentials raise this materially).
**Attack Path**
```
Step 1: POST /runtime/tasks (create a task; any assignee)
Step 2: POST /runtime/tasks/{taskId}/variables (multipart)
name=serV1 type=serializable scope=local
file=@
→ stored, then read through ObjectInputStream on read-back
Step 3: GET /runtime/tasks/{taskId}/variables/serV1
→ server has executed readObject() and reconstructs the object
```
(Same sink on `/runtime/executions/{id}/variables` and `/runtime/process-instances/{id}/variables`.)
**Prerequisites:** REST API credentials. The official sample deployments ship well-known demo credentials (`rest-admin`), which makes this effectively unauthenticated RCE surface there.
**Proof of Concept (executed).** Uploading a serialized `java.util.Date` (epoch `1700000000000`) returned:
```json
HTTP 201 {"name":"serV1","scope":"local","type":"date","value":"2023-11-14T22:13:20Z"}
```
The server executed `readObject()` and **recognized the attacker-supplied object as a Date**, echoing its value — arbitrary bytes are deserialized server-side; a GET returns the same reconstructed value.
**Impact.** With any gadget present in the engine's large dependency surface: full RCE. Without: unexpected object construction side effects and resource exhaustion.
**Suggested fix.** Reject `type=serializable` on REST input (keep JSON-friendly types; opaque `binary` otherwise) and add a strict class allow-list `jdk.serialFilter` for any path that must deserialize.
## Finding 2 — Deployable process artifacts execute without a sandbox (High as deployed)
**Summary.** These are designed extension points; the risk is that no expression/script sandbox or allow-list exists and the REST demo profile makes deployment a single authenticated request. All of the following were **executed** on the engine host:
| Artifact | Result (observed) |
|---|---|
| `serviceTask flowable:type="shell"` (`command=echo`) | OS command executed; stdout returned as process variable (`out=SH_RCE_OK`) |
| `` (JSR-223) | script evaluated (`execution.setVariable("sv","SCRIPT_1337")`) |
| `serviceTask flowable:class="java.lang.Thread"` | class loaded + instantiated (type-check error proves `Class.forName`) |
| `flowable:expression="${1337+10}"` (BPMN), DMN `outputEntry` `${1337+10}`, CMMN + Event Registry delegate expressions | JUEL evaluated (`1347` returned in `returnVariables`) |
**Suggested fix.** Provide an opt-in sandbox profile for deployments where engine admins are not OS-trusted: disabled shell tasks by config, restricted `ScriptEngine` lookup, blocked type resolution in expressions.
## Finding 3 — `orderAscendingColumn` / `orderDescendingColumn` SQL injection (Medium)
`GET /management/tables/{table}/data?orderAscendingColumn=ID_'` → HTTP 500 with a raw H2 driver syntax error; ordering by `KEY_` vs `NAME_` yields different result orders — a workable error/boolean-based injection over the authenticated management API.
**Suggested fix:** validate the column identifier against the table's actual columns; never interpolate the raw parameter.
## Finding 4 — HTTP service task `requestUrl` expression SSRF (Medium)
A deployed HTTP service task whose `requestUrl` resolves from a process variable makes the engine issue a GET to an arbitrary URL at process start. Verified: a loopback listener captured the engine's `GET /V27_SSRF`. Combined with Finding 2's deployment path this is an internal-network probing primitive.
**Suggested fix:** URL validation / egress policy for HTTP tasks.
---
*All steps executed by the reporter against a live deployment. AI tools were used to help discover and prepare these findings. Reported 2026-09-07; suggesting a 90-day coordinated disclosure window — happy to coordinate on GHSA/CVE.*
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by tracing the flowable-rest paths for multipart serializable variables, deployment artifacts, management table ordering, and HTTP service task requestUrl handling, using the listed REST endpoints and observed behaviors as reproduction points. Done means the maintainers agree on scoped fixes for all four findings and each behavior has regression verification; coordinate disclosure separately.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, sql
- Domain
- api, backend, databases, security
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100