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

Author: Ve1ledJusticeCreated Sep 7, 2026Updated Sep 7, 2026

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=@<serialized Java object bytes>
        → 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)
<scriptTask> (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.

Source: flowable/flowable-engine