flowable / flowable/flowable-engine

[SECURITY] Java deserialization of `type=serializable` REST variables (CVSS 8.0), unsandboxed deployable artifacts, order-column SQLi, HTTP-task SSRF

Aperta
#4,272 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub
Lingua principale
Java
Stelle
9.5k
Fork
2.9k
Merge medio
7h 8m
PR unite (30g)
2

Descrizione

> 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.*

Guida per i contributori

Nessuna guida per i contributori indicizzata per questo repository

Direzione di ricerca

Inizia tracciando il flusso di flowable-rest per le multipart serializable variables, i deployment artifacts, l’ordinamento delle management tables e la gestione di requestUrl delle HTTP service tasks, usando i REST endpoints elencati e i comportamenti osservati come punti di riproduzione. Il lavoro è completato quando i maintainer concordano su correzioni circoscritte per tutti e quattro i findings e ogni comportamento dispone di una verifica di regressione; coordina separatamente la divulgazione.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
java, sql
Ambito
api, backend, databases, security
Tipo di issue
Bug
Difficoltà
5/5
Tempo stimato
Più di una settimana
Stato di attività
Attiva
Chiarezza
Abbastanza chiara
Idoneità per principianti
25/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.