Skip to content

CVE-2026-56700: Verified Reproduction

CVE-2026-56700: Grav CMS unsafe deserialization and command injection RCE

CVE-2026-56700 is verified against the affected target. Vulnerability class: RCE. This critical reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00233.

REPRO-2026-00233 RCE Jul 6, 2026 CVE entry .txt
Severity
CRITICAL
CVSS
9.8
Confidence
HIGH
Reproduced in
53m 20s
Tool calls
359
Spend
$17.17
01 · Overview

What Is CVE-2026-56700?

CVE-2026-56700 is a critical unsafe-deserialization vulnerability in Grav CMS's modern scheduler job queue, letting a forged serialized scheduler Job object execute an attacker-selected command when scheduler processing is triggered. Pruva reproduced it (reproduction REPRO-2026-00233).

02 · Severity & CVSS

CVE-2026-56700 Severity & CVSS Score

CVE-2026-56700 is rated critical severity, with a CVSS base score of 9.8 out of 10.

CRITICAL threat level
9.8 / 10 CVSS base
Weakness CWE-78 — Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')

Critical — the most severe class — typically remotely exploitable with severe impact. Treat as an emergency.

How to Reproduce CVE-2026-56700

$ pruva-verify REPRO-2026-00233
or curl -O https://www.pruva.dev/api/v1/reproductions/REPRO-2026-00233/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2026-56700

Remote code execution — reproduced
  • reached the target end-to-end
  • full exploit chain demonstrated
  • on the real production code path
  • high confidence
  • the upstream fix blocks the same trigger
Trigger

forged scheduler queue JSON file containing a base64 serialized Grav\Common\Scheduler\Job object; remote trigger is POST /scheduler/webhook

Attack chain
  1. POST /scheduler/webhook
  2. Scheduler::processWebhookTrigger()
  3. Scheduler::run()
  4. JobQueue::reconstructJob()
  5. unserialize(serialized_job)
  6. Job::run/system
Runnable proof: reproduction_steps.sh
Captured evidence: fixed composer 1fixed composer 2fixed filecache gapfixed jobqueue gapfixed scheduler 1fixed scheduler 2fixed server 1fixed server 2
How the agent worked 853 events · 359 tool calls · 53 min
53 minDuration
359Tool calls
200Reasoning steps
853Events
2Dead-ends
Agent activity over 53 min
Support
16
Hypothesis
2
Repro
599
Judge
45
Variant
186
0:0053:20

Root Cause and Exploit Chain for CVE-2026-56700

Versions: Grav CMS versions before 2.0.0-beta.2. The reproduction uses 2.0.0-beta.1 as the vulnerable version and 2.0.0-beta.2 as the fixed negative control.
  • Affected versions: Grav CMS versions before 2.0.0-beta.2. The reproduction uses 2.0.0-beta.1 as the vulnerable version and 2.0.0-beta.2 as the fixed negative control.
  • Risk level and consequences: critical. If an attacker can place or tamper with a scheduler queue item and trigger scheduler processing, the queue deserialization path can instantiate a forged scheduler Job and execute arbitrary commands in the PHP/Grav process context.

Impact Parity

  • Disclosed/claimed maximum impact: remote/API-triggered arbitrary code execution through unsafe deserialization in Grav components, including Scheduler\JobQueue.
  • Reproduced impact from this run: code execution. The proof crafts real serialized Grav\Common\Scheduler\Job payloads whose command writes marker files, starts a real Grav web server with the real scheduler-webhook plugin, and sends POST /scheduler/webhook to trigger Scheduler::processWebhookTrigger() -> Scheduler::run() -> JobQueue::popWithId() / reconstructJob().
  • Parity: full.
  • Not demonstrated: the reproduction focuses on the Scheduler\JobQueue vector. It does not separately exploit the FileCache or Session::getFlashObject unsafe unserialize sinks.

Root Cause

The vulnerable JobQueue::reconstructJob() path trusted the on-disk queue item field serialized_job and directly performed:

$job = unserialize(base64_decode((string) $item['serialized_job']));

No HMAC, signature, or class restriction prevented an attacker-planted/tampered queue item from supplying a serialized Grav\Common\Scheduler\Job. The Job object has an execution path that invokes its configured command/arguments, so a forged serialized job with command system and attacker-controlled arguments becomes a direct PHP object injection to command execution primitive.

The fixed version signs the serialized job blob with an HMAC derived from Grav's nonce key and only unserializes when the sibling serialized_job_hmac verifies. Missing or mismatched HMAC values fall through to the structured queue fields instead of trusting the serialized object. The advisory identifies the fix as Grav core commit c66dfeb5f for JobQueue/FileCache/Session/InstallCommand hardening, released in 2.0.0-beta.2.

Reproduction Steps

  1. Run bundle/repro/reproduction_steps.sh.
  2. The script reuses/clones the real Grav repository, checks out 2.0.0-beta.1 and 2.0.0-beta.2, installs Composer dependencies, installs/enables the real scheduler-webhook plugin, starts the real PHP/Grav web server, poisons the real scheduler queue with a forged serialized_job, and sends POST /scheduler/webhook to trigger scheduler processing over HTTP.
  3. Expected evidence: vulnerable 2.0.0-beta.1 creates bundle/repro/proof_vulnerable_1.txt and bundle/repro/proof_vulnerable_2.txt; fixed 2.0.0-beta.2 does not create fixed proof files and logs execution of the benign /bin/true fallback job instead.

Evidence

  • Runtime manifest: bundle/repro/runtime_manifest.json records entrypoint_kind="api_remote", service_started=true, healthcheck_passed=true, and target_path_reached=true.
  • Structured verdict: bundle/repro/validation_verdict.json records claim_outcome="confirmed", validated_surface="api_remote", and observed_impact_class="code_execution".
  • Vulnerable proof files:
    • bundle/repro/proof_vulnerable_1.txt contains GRAV_JOBQUEUE_WEBHOOK_RCE_vulnerable_1.
    • bundle/repro/proof_vulnerable_2.txt contains GRAV_JOBQUEUE_WEBHOOK_RCE_vulnerable_2.
  • Vulnerable HTTP evidence:
    • bundle/logs/vulnerable_webhook_response_1_body.json shows the webhook returned success and jobs_run: 1.
    • bundle/logs/vulnerable_scheduler_1.log and bundle/logs/vulnerable_scheduler_2.log show the forged job completed successfully with command system.
  • Fixed negative control:
    • bundle/logs/fixed_scheduler_1.log and bundle/logs/fixed_scheduler_2.log show the fixed version completed the benign fallback job (/bin/true) rather than the attacker-controlled serialized Job.
    • No proof_fixed_*.txt files are produced.
  • Patch-gap evidence:
    • bundle/logs/vulnerable_jobqueue_gap.log contains the vulnerable unsigned unserialize(base64_decode(...)) sink.
    • bundle/logs/fixed_jobqueue_gap.log contains the HMAC validation logic added in the fixed version.
  • Environment details:
    • Grav vulnerable tag: 2.0.0-beta.1 (26a2d519c59c620e2b0a54d0baf33889d7d5db0a).
    • Grav fixed tag: 2.0.0-beta.2 (f95b0ff51a655edcbcc060a3d74b43e3f20b9585).
    • Scheduler-webhook plugin commit used for the HTTP endpoint: recorded in bundle/logs/webhook_plugin_commit.log.

Recommendations / Next Steps

  • Upgrade Grav CMS to 2.0.0-beta.2 or later.
  • Preserve HMAC/integrity validation for every serialized queue/cache/session payload and reject or safely rebuild unsigned legacy data rather than unserializing it.
  • Prefer avoiding PHP object serialization for attacker-influenced persistent data. If serialization is unavoidable, use strict integrity checks, narrow allowed_classes, and schema-based reconstruction.
  • Add regression tests that insert an unsigned/tampered queue item with a serialized Job and assert that the scheduler does not execute it.
  • Review other flat-file write paths to ensure untrusted users cannot create scheduler queue, cache, or session payloads.

Additional Notes

  • Idempotency confirmation: bundle/repro/reproduction_steps.sh was executed twice consecutively and produced the same vulnerable/fixed divergence both times.
  • The proof uses the real Grav product, real Composer dependencies, real scheduler-webhook plugin, real local HTTP requests, and real JobQueue::reconstructJob() deserialization. It does not mock Grav classes or reimplement the sink.
  • The queue-file placement models the advisory precondition for the JobQueue vector: attacker-planted or tampered queue data. The remote/API boundary is the real webhook trigger that causes the product to process that queue item.

CVE-2026-56700 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:001:14
0:00
session startedaccounts/fireworks/models/kimi-k2p7-code · CVE-2026-56700 · REPRO-20
0:03
0:05
web search
0:08
0:09
0:10
0:11
0:13
0:23
0:23
extract_facts
no facts extracted
0:25
0:25
0:25
supportrepro
1:00
1:03
1:03
1:03
1:05
1:05
1:05
1:05
1:07
1:07
1:08
1:08
1:10
1:11
web search
1:14

Artifacts and Evidence for CVE-2026-56700

Scripts, logs, diffs, and output captured during the reproduction.

bundle/repro/reproduction_steps.sh20.5 KB
bundle/repro/rca_report.md6.5 KB
bundle/repro/validation_verdict.json0.9 KB
bundle/repro/runtime_manifest.json4.2 KB
bundle/repro/proof_vulnerable_1.txt0.0 KB
bundle/repro/proof_vulnerable_2.txt0.0 KB
bundle/logs/fixed_composer_1.log7.7 KB
bundle/logs/fixed_composer_2.log7.7 KB
bundle/logs/fixed_filecache_gap.log0.4 KB
bundle/logs/fixed_health_1_body.txt0.0 KB
bundle/logs/fixed_health_1_headers.txt0.5 KB
bundle/logs/fixed_health_2_body.txt0.0 KB
bundle/logs/fixed_health_2_headers.txt0.5 KB
bundle/logs/fixed_jobqueue_gap.log0.4 KB
bundle/logs/fixed_queue_files_after_1.txt0.1 KB
bundle/logs/fixed_queue_files_after_2.txt0.1 KB
bundle/logs/fixed_scheduler_1.log0.4 KB
bundle/logs/fixed_scheduler_2.log0.4 KB
bundle/logs/fixed_scheduler_health_1_body.json0.3 KB
bundle/logs/fixed_scheduler_health_1_headers.txt0.4 KB
bundle/logs/fixed_scheduler_health_2_body.json0.3 KB
bundle/logs/fixed_scheduler_health_2_headers.txt0.4 KB
bundle/logs/fixed_server_1.log0.6 KB
bundle/logs/fixed_server_2.log0.6 KB
bundle/logs/fixed_session_gap.log0.4 KB
bundle/logs/fixed_webhook_curl_1.log0.0 KB
bundle/logs/fixed_webhook_curl_2.log0.0 KB
bundle/logs/fixed_webhook_request_1.txt0.3 KB
bundle/logs/fixed_webhook_request_2.txt0.3 KB
bundle/logs/fixed_webhook_response_1_body.json0.1 KB
bundle/logs/fixed_webhook_response_1_headers.txt0.4 KB
bundle/logs/fixed_webhook_response_2_body.json0.1 KB
bundle/logs/fixed_webhook_response_2_headers.txt0.4 KB
bundle/logs/payload_fixed_1.generate.log0.0 KB
bundle/logs/payload_fixed_2.generate.log0.0 KB
bundle/logs/payload_vulnerable_1.generate.log0.0 KB
bundle/logs/payload_vulnerable_2.generate.log0.0 KB
bundle/logs/vulnerable_composer_1.log7.7 KB
bundle/logs/vulnerable_composer_2.log7.7 KB
bundle/logs/vulnerable_filecache_gap.log0.1 KB
bundle/logs/vulnerable_health_1_body.txt0.0 KB
bundle/logs/vulnerable_health_1_headers.txt0.5 KB
bundle/logs/vulnerable_health_2_body.txt0.0 KB
bundle/logs/vulnerable_health_2_headers.txt0.5 KB
bundle/logs/vulnerable_jobqueue_gap.log0.1 KB
bundle/logs/vulnerable_queue_files_after_1.txt0.1 KB
bundle/logs/vulnerable_queue_files_after_2.txt0.1 KB
bundle/logs/vulnerable_scheduler_1.log0.3 KB
bundle/logs/vulnerable_scheduler_2.log0.3 KB
bundle/logs/vulnerable_scheduler_health_1_body.json0.3 KB
bundle/logs/vulnerable_scheduler_health_1_headers.txt0.4 KB
bundle/logs/vulnerable_scheduler_health_2_body.json0.3 KB
bundle/logs/vulnerable_scheduler_health_2_headers.txt0.4 KB
bundle/logs/vulnerable_server_1.log0.6 KB
bundle/logs/vulnerable_server_2.log0.6 KB
bundle/logs/vulnerable_session_gap.log0.1 KB
bundle/logs/vulnerable_webhook_curl_1.log0.0 KB
bundle/logs/vulnerable_webhook_curl_2.log0.0 KB
bundle/logs/vulnerable_webhook_request_1.txt0.3 KB
bundle/logs/vulnerable_webhook_request_2.txt0.3 KB
bundle/logs/vulnerable_webhook_response_1_body.json0.1 KB
bundle/logs/vulnerable_webhook_response_1_headers.txt0.4 KB
bundle/logs/vulnerable_webhook_response_2_body.json0.1 KB
bundle/logs/vulnerable_webhook_response_2_headers.txt0.4 KB
bundle/logs/webhook_plugin_commit.log0.0 KB
bundle/logs/webhook_plugin_fetch.log0.1 KB
bundle/repro/payload_fixed_1.b642.3 KB
bundle/repro/payload_fixed_1.generate.log0.0 KB
bundle/repro/payload_fixed_2.b642.3 KB
bundle/repro/payload_fixed_2.generate.log0.0 KB
bundle/repro/payload_vulnerable_1.b642.3 KB
bundle/repro/payload_vulnerable_1.generate.log0.0 KB
bundle/repro/payload_vulnerable_2.b642.3 KB
bundle/repro/payload_vulnerable_2.generate.log0.0 KB
bundle/repro/queue_fixed_1.json2.6 KB
bundle/repro/queue_fixed_2.json2.6 KB
bundle/repro/queue_vulnerable_1.json2.7 KB
bundle/repro/queue_vulnerable_2.json2.7 KB
08 · How to Fix

How to Fix CVE-2026-56700

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2026-56700 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2026-56700

What access does an attacker need to exploit CVE-2026-56700?

The attacker needs the ability to place or tamper with a scheduler queue item (the serialized_job field) and to trigger scheduler processing, which this reproduction does through the real HTTP scheduler-webhook endpoint.

Are other Grav components affected besides the scheduler job queue?

The advisory also describes unsafe unserialize() calls in Framework\Cache\Adapter\FileCache and Session handling that deserialize untrusted data without restricting allowed classes, plus a separate command injection path. This reproduction specifically demonstrates the Scheduler\JobQueue path.

How severe is CVE-2026-56700?

It is rated critical: if an attacker can place or tamper with a scheduler queue item and trigger scheduler processing, the deserialization path can instantiate a forged Job and execute arbitrary commands in the PHP/Grav process context.

How can I reproduce CVE-2026-56700?

Download the verified script from this page and run it in an isolated environment against Grav CMS 2.0.0-beta.1. Craft a serialized Grav\Common\Scheduler\Job payload whose command writes a marker file, place it in a serialized_job queue entry, start a real Grav server with the scheduler-webhook plugin, and POST to /scheduler/webhook to trigger processing; confirm 2.0.0-beta.2 no longer executes the forged job.
11 · References

References for CVE-2026-56700

Authoritative sources for CVE-2026-56700 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.