Skip to content

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

Description

@Ve1ledJustice

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:

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions