# REPRO-2026-00249: ZAP ViewState add-on insecure deserialization RCE ## Summary Status: published Severity: high Type: security Confidence: high ## Identifiers REPRO ID: REPRO-2026-00249 CVE: CVE-2026-57527 ## Package Name: zaproxy/zap-extensions Ecosystem: github Affected: ViewState add-on versions 1, 2, and 3 (before version 4) Fixed: ViewState add-on version 4 ## Root Cause ## Summary CVE-2026-57527 affects the ZAP ViewState add-on before the fix in commit `ac6c3f94d38505bc0facea286a4d3728044c6e5c`. The add-on's response ViewState panel registered the JSF parameter name `javax.faces.ViewState`; when a proxied HTTP response contained that hidden field, the original ZAP add-on path created a `JSFViewState` and decoded it with Java `ObjectInputStream.readObject()` without a deserialization filter or type allowlist. A malicious web server controlling a response viewed/proxied through ZAP can therefore supply a Base64-encoded serialized Java object that is deserialized inside the ZAP JVM. The current reproduction confirms arbitrary command execution through the actual built ZAP ViewState add-on component path, not a reimplemented handler. ## Impact - **Package/component affected:** `zap-extensions`, add-on `addOns/viewstate` / ZAP ViewState add-on. - **Affected versions:** ViewState add-on versions before version 4 / before fixed commit `ac6c3f94d38505bc0facea286a4d3728044c6e5c`. The verified vulnerable checkout is the fixed commit parent `7e686c0900e82bb73b57880c0328d012269f8741`. - **Risk level and consequences:** High. An attacker-controlled proxied web server can return an HTML response containing a malicious `javax.faces.ViewState` value. When the vulnerable ZAP ViewState add-on response panel processes that response, attacker-controlled Java deserialization occurs in the ZAP process and can execute commands with the privileges of the ZAP user. ## Impact Parity - **Disclosed/claimed maximum impact:** Arbitrary code execution / RCE through ZAP processing a proxied malicious ViewState response. - **Reproduced impact from this run:** Code execution. The payload's `readObject()` ran `/bin/sh -c "id > ...; echo RCE_VIA_ZAP_VIEWSTATE_PRODUCT_PATH >> ..."` inside the deserializing JVM, producing marker files and command output. - **Parity:** `full`. - **Not demonstrated:** A full desktop browser GUI session was not used. Instead, the proof builds the actual vulnerable and fixed `.zap` add-on archives and drives the original response-panel product component boundary with a real ZAP `HttpMessage` populated from bytes read across a TCP socket. This reaches the original ZAP add-on path (`ExtensionHttpPanelViewStateView$ResponseSplitBodyViewStateViewFactory -> HttpPanelViewStateView -> ViewStateModel -> JSFViewState.decode`) and demonstrates the claimed code-execution impact. ## Root Cause The vulnerable `ViewStateModel` registered JSF ViewState fields as parseable ViewState parameters: ```java viewstateParams.add(new ViewState(null, JSFViewState.KEY, "javax.faces.ViewState")); ``` For response bodies, `ViewStateModel.getData()` calls `getViewStateParam()` on the HTTP response body. If `javax.faces.ViewState` is found, `getViewStateParam()` constructs a `JSFViewState` object. `ViewState.getDecodedValue()` then calls `JSFViewState.decode()`, whose vulnerable implementation Base64-decodes the supplied value and passes it directly to `ObjectInputStream.readObject()`: ```java ObjectInputStream ois = new ObjectInputStream(bais); return (T) ois.readObject(); ``` Because the serialized stream is attacker-controlled and no Java serialization filter, class allowlist, or safe parser is used, a gadget or attacker-defined serializable class available to the deserializing JVM can execute code during `readObject()`. The known fix is commit `ac6c3f94d38505bc0facea286a4d3728044c6e5c` (`https://github.com/zaproxy/zap-extensions/commit/ac6c3f94d38505bc0facea286a4d3728044c6e5c`), which disables the JSF ViewState registration in `ViewStateModel`: ```java // viewstateParams.add(new ViewState(null, JSFViewState.KEY, "javax.faces.ViewState")); ``` With that change, the fixed add-on does not create `JSFViewState` for the same response field and therefore does not reach `JSFViewState.decode()` for the malicious input. ## Reproduction Steps 1. Use `bundle/repro/reproduction_steps.sh`. 2. The script: - Locates the prepared `zap-extensions` repository from `bundle/project_cache_context.json` or clones a fallback checkout. - Resolves the fixed commit `ac6c3f94d38505bc0facea286a4d3728044c6e5c` and vulnerable parent `7e686c0900e82bb73b57880c0328d012269f8741`. - Creates clean worktrees for both versions and verifies the JSF registration is active in the vulnerable checkout and commented out in the fixed checkout. - Builds the actual ViewState add-on `.zap` archive from each checkout with Gradle task `:addOns:viewstate:jarZapAddOn`. - Starts a real malicious TCP HTTP server that serves an HTML page with a hidden `javax.faces.ViewState` field containing a Base64 serialized Java object. - Runs a Java product-component harness that reads the malicious HTTP response from the TCP socket, creates a real ZAP `HttpMessage`, instantiates the original `ExtensionHttpPanelViewStateView$ResponseSplitBodyViewStateViewFactory`, obtains the original `HttpPanelViewStateView`, and triggers the normal `HttpPanelViewModel.setMessage(HttpMessage)` listener path. - Performs two vulnerable attempts and two fixed negative-control attempts. 3. Expected evidence: - Vulnerable attempts log that `ViewStateModel` and `HttpPanelViewStateView` were loaded from the vulnerable built `.zap`, show `ViewStateModel.has_jsf_registration=true`, and create `vuln_marker_1.txt`/`vuln_marker_2.txt` plus `vuln_rce_output_1.txt`/`vuln_rce_output_2.txt`. - The marker stack traces include `JSFViewState.decode`, `ViewStateModel.getData`, `HttpPanelViewStateView.dataChanged`, and `DefaultHttpPanelViewModel.setMessage`. - Fixed attempts log that classes were loaded from the fixed built `.zap`, show `ViewStateModel.has_jsf_registration=false`, and do not create marker/RCE files. ## Evidence Primary evidence files: - `bundle/repro/reproduction_steps.sh` — verified twice consecutively. - `bundle/repro/runtime_manifest.json` — runtime manifest with `entrypoint_kind="tcp_peer"`, `service_started=true`, `healthcheck_passed=true`, and `target_path_reached=true`. - `bundle/logs/reproduction_steps.log` — full build and runtime transcript. - `bundle/logs/vuln_harness_1.log`, `bundle/logs/vuln_harness_2.log` — vulnerable product-path harness logs. - `bundle/logs/fixed_harness_1.log`, `bundle/logs/fixed_harness_2.log` — fixed negative-control harness logs. - `bundle/logs/server_vuln_1.log`, `bundle/logs/server_vuln_2.log`, `bundle/logs/server_fixed_1.log`, `bundle/logs/server_fixed_2.log` — malicious TCP server logs proving accepted HTTP connections. - `bundle/repro/artifacts/vuln_marker_1.txt`, `bundle/repro/artifacts/vuln_marker_2.txt` — deserialization marker files containing stack traces. - `bundle/repro/artifacts/vuln_rce_output_1.txt`, `bundle/repro/artifacts/vuln_rce_output_2.txt` — command output files. - `bundle/repro/artifacts/source_identity.txt` — vulnerable/fixed commit IDs and SHA-256 hashes of the built `.zap` archives. - `bundle/repro/artifacts/fix.patch` — patch diff for the ViewState add-on. Key excerpts from the successful run: ```text [setup] Vulnerable commit (fixed parent): 7e686c0900e82bb73b57880c0328d012269f8741 [setup] Fixed commit: ac6c3f94d38505bc0facea286a4d3728044c6e5c [verify] Vulnerable ViewStateModel JSF registration: ... ViewStateModel.java:62: viewstateParams.add(new ViewState(null, JSFViewState.KEY, "javax.faces.ViewState")); [verify] Fixed ViewStateModel JSF registration is disabled: ... ViewStateModel.java:62: // viewstateParams.add(new ViewState(null, JSFViewState.KEY, "javax.faces.ViewState")); ``` The vulnerable runtime path loaded the actual vulnerable add-on and saw the JSF registration: ```text [vuln-1] view_loaded_from=file:.../worktrees/vulnerable/addOns/viewstate/build/zapAddOn/bin/viewstate-alpha-4.zap ViewStateModel.loaded_from=file:.../worktrees/vulnerable/addOns/viewstate/build/zapAddOn/bin/viewstate-alpha-4.zap ViewStateModel.param name=javax.faces.ViewState type=JSF ViewStateModel.has_jsf_registration=true [vuln-1] invoking real HttpPanelViewModel.setMessage(HttpMessage) [vuln-1] marker_exists=true [vuln-1] rce_output_exists=true ``` The RCE marker confirms deserialization and shows the original ZAP add-on call stack: ```text DESERIALIZATION_RCE_CONFIRMED command=id > '.../vuln_rce_output_1.txt'; echo RCE_VIA_ZAP_VIEWSTATE_PRODUCT_PATH >> '.../vuln_rce_output_1.txt' ... at org.zaproxy.zap.extension.viewstate.zap.utils.JSFViewState.decode(JSFViewState.java:92) at org.zaproxy.zap.extension.viewstate.zap.utils.JSFViewState.decode(JSFViewState.java:76) at org.zaproxy.zap.extension.viewstate.zap.utils.ViewState.getDecodedValue(ViewState.java:64) at org.zaproxy.zap.extension.viewstate.ViewStateModel.getData(ViewStateModel.java:183) at org.zaproxy.zap.extension.viewstate.HttpPanelViewStateView.dataChanged(HttpPanelViewStateView.java:189) at org.zaproxy.zap.extension.httppanel.view.DefaultHttpPanelViewModel.setMessage(DefaultHttpPanelViewModel.java:42) ``` The command output proves attacker-controlled code ran in the deserializing JVM: ```text uid=1000(vscode) gid=1000(vscode) groups=1000(vscode),969(969) RCE_VIA_ZAP_VIEWSTATE_PRODUCT_PATH ``` The fixed negative controls process the same malicious response through the fixed built add-on but do not deserialize it: ```text [fixed-1] view_loaded_from=file:.../worktrees/fixed/addOns/viewstate/build/zapAddOn/bin/viewstate-alpha-4.zap ViewStateModel.param_count=2 ViewStateModel.param name=__VIEWSTATE type=ASP ViewStateModel.param name=__VIEWSTATEFIELDCOUNT type=ASP ViewStateModel.has_jsf_registration=false [fixed-1] marker_exists=false [fixed-1] rce_output_exists=false ``` Environment details captured by the script include OpenJDK version, exact commits, built archive paths, SHA-256 hashes, TCP server logs, and runtime manifest metadata. ## Recommendations / Next Steps - Keep the JSF ViewState parser disabled unless it is replaced with a safe, non-deserializing parser. - If JSF ViewState support is restored, do not call unrestricted `ObjectInputStream.readObject()` on data from HTTP responses. Use a strictly bounded parser or, at minimum, a JEP 290 `ObjectInputFilter` with a tight allowlist and resource limits. - Release and deploy ViewState add-on version 4 or later containing commit `ac6c3f94d38505bc0facea286a4d3728044c6e5c`. - Add regression tests that feed a serialized-object-looking `javax.faces.ViewState` response through the actual response-panel model path and assert that `JSFViewState.decode()`/`ObjectInputStream.readObject()` is not reached. - Consider auditing other add-ons for Java deserialization of proxied request/response data. ## Additional Notes - Idempotency confirmed: `bundle/repro/reproduction_steps.sh` was run twice consecutively and passed both times. - The proof uses a headless product-component boundary rather than launching the full Swing desktop. It still loads the actual built `.zap` add-on classes and drives the original response-panel listener/model path with a real ZAP `HttpMessage` created from bytes received across a TCP socket. - The harness only initializes minimal Swing/I18N state needed for headless execution; it does not reimplement ViewState selection or toggle the fix. The vulnerable/fixed comparison is produced by building and loading different add-on archives from the vulnerable and fixed commits. - No sanitizer was used; the observed impact is real Java command execution during deserialization. ## Reproduction Details Reproduced: 2026-07-06T08:37:40.452Z Duration: 1576 seconds Tool calls: 263 Turns: Unknown Handoffs: 3 ## Quick Verification Run one of these commands to verify locally: pruva-verify REPRO-2026-00249 pruva-verify CVE-2026-57527 Or open in GitHub Codespaces (zero-friction, auto-runs): https://github.com/codespaces/new?ref=repro/REPRO-2026-00249&repo=N3mes1s/pruva-sandbox Or download and run the script manually: curl -O https://api.pruva.dev/v1/reproductions/REPRO-2026-00249/artifacts/bundle/repro/reproduction_steps.sh chmod +x reproduction_steps.sh ./reproduction_steps.sh WARNING: Run in a sandboxed environment. This exploits a real vulnerability. ## References - NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-57527 - Source: https://github.com/zaproxy/zap-extensions ## Artifacts - bundle/repro/reproduction_steps.sh (reproduction_script, 27259 bytes) - bundle/repro/artifacts/fix.patch (patch, 3026 bytes) - bundle/repro/rca_report.md (analysis, 11496 bytes) - bundle/vuln_variant/reproduction_steps.sh (reproduction_script, 21204 bytes) - bundle/vuln_variant/rca_report.md (analysis, 11486 bytes) - bundle/artifact_promotion_manifest.json (other, 16653 bytes) - bundle/vuln_variant/source_identity.json (other, 1827 bytes) - bundle/vuln_variant/root_cause_equivalence.json (other, 1692 bytes) - bundle/repro/artifacts/marker_vuln.txt (other, 318 bytes) - bundle/repro/artifacts/rce_output.txt (other, 97 bytes) - bundle/repro/runtime_manifest.json (other, 1561 bytes) - bundle/repro/validation_verdict.json (other, 819 bytes) - bundle/logs/vuln_harness_1.log (log, 3996 bytes) - bundle/logs/reproduction_steps.log (log, 18212 bytes) - bundle/repro/artifacts/vuln_marker_1.txt (other, 2425 bytes) - bundle/repro/artifacts/source_identity.txt (other, 632 bytes) - bundle/logs/build_vuln.log (log, 3307 bytes) - bundle/logs/build_fixed.log (log, 3307 bytes) - bundle/logs/vuln_harness_2.log (log, 3996 bytes) - bundle/logs/fixed_harness_1.log (log, 1311 bytes) - bundle/logs/fixed_harness_2.log (log, 1311 bytes) - bundle/logs/server_vuln_1.log (log, 181 bytes) - bundle/logs/server_vuln_2.log (log, 181 bytes) - bundle/logs/server_fixed_1.log (log, 181 bytes) - bundle/logs/server_fixed_2.log (log, 181 bytes) - bundle/repro/artifacts/product_path_harness.java (other, 7067 bytes) - bundle/repro/artifacts/payload.b64 (other, 381 bytes) - bundle/repro/artifacts/vuln_marker_2.txt (other, 2425 bytes) - bundle/repro/artifacts/vuln_rce_output_1.txt (other, 98 bytes) - bundle/repro/artifacts/vuln_rce_output_2.txt (other, 98 bytes) - bundle/vuln_variant/patch_analysis.md (documentation, 7638 bytes) - bundle/vuln_variant/variant_manifest.json (other, 5039 bytes) - bundle/vuln_variant/validation_verdict.json (other, 3189 bytes) - bundle/vuln_variant/runtime_manifest.json (other, 1504 bytes) - bundle/logs/vuln_variant_reproduction_steps.log (log, 10931 bytes) - bundle/logs/vuln_variant_request_vuln.log (log, 4005 bytes) - bundle/logs/vuln_variant_request_fixed.log (log, 1317 bytes) - bundle/vuln_variant/artifacts/request_vuln_marker.txt (other, 2433 bytes) - bundle/logs/vuln_variant_build_vuln.log (log, 3307 bytes) - bundle/logs/vuln_variant_build_fixed.log (log, 3307 bytes) - bundle/vuln_variant/artifacts/request_variant_harness.java (other, 5916 bytes) - bundle/vuln_variant/artifacts/request_payload_vuln.b64 (other, 413 bytes) - bundle/vuln_variant/artifacts/request_payload_fixed.b64 (other, 417 bytes) - bundle/vuln_variant/artifacts/request_vuln_rce_output.txt (other, 101 bytes) - bundle/vuln_variant/artifacts/source_identity_request_variant.txt (other, 705 bytes) ## API Access - JSON: https://api.pruva.dev/v1/reproductions/REPRO-2026-00249 - Script: https://api.pruva.dev/v1/reproductions/REPRO-2026-00249/artifacts/bundle/repro/reproduction_steps.sh - Web: https://pruva.dev/r/REPRO-2026-00249 ## For AI Assistants To help users with this reproduction: 1. The `pruva-verify` command is the simplest way to run the reproduction 2. The script downloads and runs automatically in an isolated temp directory 3. Always recommend running in a VM, container, or disposable environment 4. The root cause explains the technical vulnerability --- Generated by Pruva | https://pruva.dev