This is a land mine buried behind the front desk, not a bomb in the parking lot
CVE-2026-8135 is an insecure deserialization flaw in Concrete CMS ExpressEntryList that affects 9.5.0 and earlier and is fixed in 9.5.1. A user who already has enough Concrete privileges to add blocks can abuse the REST API's JSON handling to satisfy the _fromCIF === true gate, store a malicious serialized payload in the block's filterFields column, and get code execution when an administrator later views or edits that block.
The vendor's *technical* severity is fair for impact but too hot for patch triage across a large estate. The decisive reality check is the prerequisite stack: this is PR:H, requires a Concrete role that can add blocks, depends on a specific block path, and only becomes RCE after admin interaction with the poisoned block state. That's post-initial-access abuse with a narrower exposed population than a typical internet-reachable RCE.
4 steps from start to impact.
Obtain a privileged Concrete session
- Authenticated access to the target Concrete CMS instance
- Role permissions sufficient to add blocks to at least one area
- This is not unauthenticated remote exploitation
- Many enterprises heavily restrict CMS admin access behind SSO, VPN, IP allowlists, or delegated workflows
- If only a tiny central web team holds these rights, reachable population drops fast
<=9.5.0, but they cannot confirm exploitable role assignments. IAM, SSO, and Concrete audit logs are more useful than perimeter scanning here.Poison ExpressEntryList via REST API
curl, or Burp Suite, the attacker submits block data through the REST API path so json_decode() turns the JSON string "true" into a PHP boolean and bypasses the intended _fromCIF safeguard. That lets them write attacker-controlled serialized data into the filterFields database column for the ExpressEntryList block.- Target uses the vulnerable
ExpressEntryListblock controller - Attacker can reach the REST/API workflow used to save block configuration
- This is a product-specific bug path, not a generic CMS primitive
- The attacker still needs the exact block workflow and parameter shape
- Not every Concrete deployment uses Express features or this block type operationally
Wait for an administrator to touch the booby-trapped block
- An administrator later views or edits the poisoned block
- No user interaction in the CVSS sense, but there is still an operational trigger requirement
- Dormant or rarely managed blocks may never execute
- If content and platform admins are separated, the trigger window can be long
Execute gadget chain as the web server user
- A working gadget chain exists in the target runtime/application set
- PHP execution context has access to filesystem secrets, DB credentials, or deployment artifacts
- Real post-exploit blast radius depends on containerization, read-only filesystems, SELinux/AppArmor, and secret handling
- If the site is well isolated, the attacker may get app compromise without easy host breakout
The supporting signals.
| In-the-wild status | No public evidence of active exploitation found in the sources reviewed; CISA ADP in the OpenCVE record marks exploitation as none. |
|---|---|
| KEV status | Not KEV-listed as of the current CISA KEV catalog review. |
| EPSS | 0.00232 (~0.232%) from the user-supplied intel, which is low and consistent with a niche, privilege-heavy path rather than a broadly weaponized internet bug. |
| Proof-of-concept availability | No authoritative public PoC repository surfaced in the reviewed sources. Public writeups describe the primitive, but I did not find a primary-source exploit release from the reporter or vendor. |
| Vendor scoring | User-supplied vendor baseline is HIGH 7.2 with CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H; the vendor's published CVE/release-note text also shows a CVSS v4.0 8.9 HIGH path, reflecting the same high impact but different scoring model. |
| Affected versions | Concrete CMS 9.5.0 and earlier are listed as affected in the CVE record. |
| Fixed version | Upgrade to 9.5.1. I found no authoritative distro backport advisory in the reviewed sources, so treat upstream 9.5.1 as the fixed target unless your package maintainer says otherwise. |
| Trigger mechanics | The exploit chain is authenticated and stored: abuse REST API JSON parsing to satisfy _fromCIF === true, write a serialized payload into filterFields, then wait for an admin view/edit to trigger deserialization. |
| Scanning/exposure data | No trustworthy internet-scale, version-precise exposure count was available from reviewed sources. Inference: banner-based Shodan/Censys discovery for Concrete CMS is likely noisy, and exploitability still depends on app role assignments plus use of the ExpressEntryList path. |
| Disclosure and credit | Disclosed on 2026-05-21; credited to Nguyễn Văn Thiện via HackerOne report H1 3643372 in vendor-linked records. |
noisgate verdict.
The decisive downgrade factor is attacker position: this bug requires already-authenticated, high-privilege access inside Concrete CMS, not anonymous reachability from the internet. The impact is full server-side code execution, but the reachable population is narrowed by role assignment, feature usage, and a stored-trigger workflow that depends on later admin interaction.
Why this verdict
- Downgrade for
PR:H: the attacker already needs a privileged Concrete CMS account with block-creation rights, which implies prior compromise, insider abuse, or risky delegated access. - Downgrade for narrow exposure: only deployments using the vulnerable
ExpressEntryListpath and allowing that role to configure blocks are meaningfully exposed. - Downgrade for trigger friction: the stored payload still needs a later admin view/edit event, so this is not a one-shot wormable RCE.
- Keep at MEDIUM, not LOW: once triggered, the result is real server-side code execution in the web app context, which can expose secrets, data, and adjacent infrastructure.
Why not higher?
This is not an unauthenticated edge-service bug and not even a low-privileged authenticated bug. Requiring high privileges inside the CMS, plus a specific feature path, plus a later admin-trigger event compounds downward pressure on severity for enterprise patch scheduling.
Why not lower?
Calling it LOW would ignore the fact that successful exploitation crosses an important boundary: CMS admin misuse becomes server-side code execution. That materially increases blast radius beyond normal content-management abuse, especially on flat web stacks with accessible secrets and writable deployment paths.
What to do — in priority order.
- Tighten block-creation privileges — Restrict who can add or edit blocks, especially on internet-facing sites and agency-managed instances. For a MEDIUM verdict there is no mitigation SLA — go straight to the 365-day remediation window, but this is the single best compensating control while you work through patching.
- Review delegated admin accounts — Audit SSO groups, local Concrete accounts, and third-party agency users for anyone who can manage blocks or Express content. Remove stale access now; there is no mitigation SLA for MEDIUM, but this sharply cuts the only realistic attacker population before the vendor fix is applied within the 365-day remediation window.
- Inspect for suspicious serialized block state — Search block configuration data, especially
filterFieldsassociated withExpressEntryList, for unexpected serialized payloads or anomalous admin/API edits. Use this as a hygiene and threat-hunting measure during the 365-day remediation window if you cannot patch immediately. - Harden the PHP runtime boundary — Use least-privilege filesystem permissions, separate service accounts, and host/container controls so a web-app RCE has less room to pivot. Even without a mitigation SLA for MEDIUM, this reduces post-exploit blast radius while you complete remediation inside 365 days.
- Generic perimeter WAF rules won't reliably stop this because the exploit lives in authenticated application logic and stored block configuration, not a simple edge signature.
- MFA alone helps prevent account takeover but does nothing against a malicious or already-authenticated privileged user.
- Version-only vulnerability scans identify potentially affected hosts but do not tell you whether the exploitable role/feature combination exists on that site.
Crowdsourced verification payload.
Run this on the target Concrete CMS host or a mounted application filesystem, not from a network scanner. Invoke it as bash verify_concrete_cve_2026_8135.sh /var/www/html (or your Concrete install root); no root is required, but the script needs read access to the app files and php in PATH if it falls back to the CLI version check.
#!/usr/bin/env bash
# verify_concrete_cve_2026_8135.sh
# Checks whether a local Concrete CMS installation appears vulnerable to CVE-2026-8135.
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN, 3=usage/runtime error
set -u
TARGET="${1:-}"
if [[ -z "$TARGET" ]]; then
echo "Usage: $0 /path/to/concretecms"
exit 3
fi
if [[ ! -d "$TARGET" ]]; then
echo "UNKNOWN - target directory not found: $TARGET"
exit 2
fi
version=""
# Try composer.lock first
if [[ -f "$TARGET/composer.lock" ]]; then
version=$(php -r '
$f=$argv[1];
$j=json_decode(file_get_contents($f), true);
if (!is_array($j)) exit(1);
$pkgs=array_merge($j["packages"] ?? [], $j["packages-dev"] ?? []);
foreach ($pkgs as $p) {
if (($p["name"] ?? "") === "concretecms/core" || ($p["name"] ?? "") === "concretecms/concretecms") {
echo $p["version"] ?? "";
exit(0);
}
}
exit(1);
' "$TARGET/composer.lock" 2>/dev/null)
fi
# Fallback to Concrete CLI if present
if [[ -z "$version" && -x "$TARGET/concrete/bin/concrete5" ]]; then
version=$(php "$TARGET/concrete/bin/concrete5" --version 2>/dev/null | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+' | head -n1)
fi
# Normalize leading v and Composer-style suffixes
version=$(echo "$version" | sed -E 's/^v//; s/^([0-9]+\.[0-9]+\.[0-9]+).*/\1/')
if [[ -z "$version" ]]; then
echo "UNKNOWN - could not determine Concrete CMS version from composer.lock or CLI"
exit 2
fi
verlte() {
# returns true if $1 <= $2
[[ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | head -n1)" == "$1" ]]
}
verlt() {
# returns true if $1 < $2
[[ "$1" != "$2" ]] && verlte "$1" "$2"
}
if verlte "$version" "9.5.0"; then
echo "VULNERABLE - Concrete CMS version $version is <= 9.5.0"
exit 1
elif verlt "$version" "9999.9999.9999"; then
echo "PATCHED - Concrete CMS version $version is > 9.5.0"
exit 0
else
echo "UNKNOWN - unparsable version state: $version"
exit 2
fi
If you remember one thing.
<=9.5.0, identify which ones actually delegate block-management rights, and upgrade those systems to 9.5.1 through normal web-app change control. For a MEDIUM verdict there is no noisgate mitigation SLA — go straight to the 365-day remediation window; use compensating controls where needed, and complete the actual vendor upgrade inside the noisgate remediation SLA of ≤365 days.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.