Like changing the lock after the thief already copied the key but forgetting to disable the old cut
CVE-2025-22386 affects Optimizely Configured Commerce before 5.2.2408. The flaw is insufficient session destruction / expiration in the B2B storefront: when a user logs out, the server can leave that session token valid, so anyone who already has the token can continue using it. Optimizely's advisory says this impacts all versions before 5.2.2408 (STS) and 5.2.2408 (LTS) and that the fix is server-side invalidation on logout.
In practice, this is less dangerous than the 7.3 vector suggests because the bug does not steal a token by itself. The attacker first needs a valid session token through some other means such as a shared device, browser compromise, proxy logs, session leakage, or a separate client-side weakness. That makes this a post-token-theft persistence flaw, not an initial-access primitive. The vendor's own advisory rated it Medium 5.9 on December 13, 2024, while the CVE/NVD record appeared on January 3-4, 2025 and shows a CISA-ADP 7.3 score; reality is closer to the lower end.
4 steps from start to impact.
Obtain a live storefront session token with Burp Suite or browser artifacts
- Attacker can access a victim's authenticated session token
- Target uses an affected Configured Commerce version before 5.2.2408
- Victim has an active storefront session
- Requires a separate token-capture opportunity first
- Modern EDR/browser protections often catch malware-based cookie theft
- Short session lifetimes and secure client handling reduce token exposure
Wait for or trigger logout, then replay with Burp Repeater or curl
curl or Burp Repeater is enough; no exploit framework is needed.- Captured token is still within any remaining session validity window
- Application relies on the vulnerable logout/session handling path
- Any upstream SSO/global logout, custom session revocation, or aggressive idle timeout can break replay
- Some deployments may rotate cookies or bind sessions to additional state
Operate as the logged-out user using normal storefront workflows
- Victim account has access to sensitive B2B pricing, order, or customer records
- Relevant storefront functions are reachable with the preserved session
- Impact is bounded by the victim account's role and tenant/customer scope
- Step-up auth on sensitive actions would cut off some abuse even if browsing stays open
Persist quietly until the session finally expires
- Session remains valid long enough to matter operationally
- Natural expiry limits dwell time
- Password changes, explicit token revocation, or re-auth policies may terminate access
The supporting signals.
| In-the-wild status | No confirmed active exploitation found in the public sources reviewed; not present in CISA KEV and no campaign reporting surfaced. |
|---|---|
| Proof-of-concept availability | No public PoC located in source search. Feedly also notes no evidence of public exploit code. |
| EPSS | 0.00265 (~0.265%) from user intel. Public Feedly's early FIRST snapshot showed 0.04% / 11th percentile on 2025-01-04, which is directionally consistent with low expected exploitation. |
| KEV status | Not KEV-listed in the current CISA Known Exploited Vulnerabilities Catalog. |
| CVSS vector reality check | CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N captures remote replay impact, but it overstates real-world risk because it hides the hardest prerequisite: already having the victim's session token. |
| Affected versions | All Configured Commerce versions before 5.2.2408 are affected according to Optimizely's advisory. |
| Fixed versions | Fixed in 5.2.2408 (STS) and 5.2.2408 (LTS). This is a vendor-hosted app fix; no distro backport model is relevant here. |
| Scanning / exposure data | No product-specific GreyNoise/Shodan/Censys/FOFA exposure signal was identified in public search. That fits a niche B2B commerce platform with a smaller exposed population than mass-market VPN, firewall, or CMS targets. |
| Disclosure timeline | Optimizely's advisory was updated 2025-01-06 and lists Date Published: 2024-12-13. NVD shows the CVE record was received 2025-01-03 and published 2025-01-03/04 depending on timezone display. |
| Researcher / reporter | No public researcher attribution was provided in the vendor advisory. |
noisgate verdict.
The decisive factor is that exploitation starts after session-token capture, which this CVE does not provide. That turns the bug into an account-persistence amplifier with meaningful confidentiality/integrity impact, but a much smaller reachable population than internet-facing pre-auth flaws.
Why this verdict
- Downward adjustment: requires a valid session token first — the vulnerability preserves a stolen token; it does not obtain one.
- Downward adjustment: post-initial-access behavior — needing token theft, shared-device access, or browser compromise implies the attacker is already partway in.
- Downward adjustment: limited exposed population — Optimizely Configured Commerce is a niche B2B platform, not a mass internet edge product with broad opportunistic scanning.
- Downward adjustment: account-scoped blast radius — impact is serious for the affected user/customer context, but this is not server compromise or tenant-wide default takeover.
- No upward pressure from threat intel — no KEV listing, no public campaign reporting, and no public PoC found.
Why not higher?
This is not unauthenticated remote code execution, auth bypass, or arbitrary account takeover at internet scale. The attacker must already possess a valid session token, which is a major real-world prerequisite that sharply narrows who can use the bug and how often it will be used.
Why not lower?
Once the prerequisite is met, the attacker can continue acting as a legitimate logged-out user and access or change sensitive B2B storefront data at that account's privilege level. For organizations with valuable pricing, ordering, and customer data in the storefront, that is more than backlog hygiene even if it is not an emergency patch.
What to do — in priority order.
- Restrict storefront access to trusted networks where feasible — Follow the vendor's own mitigation guidance for shared or uncontrolled environments: prefer corporate networks or VPN-backed access for sensitive customer-service and purchasing workflows. There is no noisgate mitigation SLA for a MEDIUM, so do this in normal change control while scheduling the patch inside the remediation window.
- Reduce browser and device sharing for storefront users — Treat kiosks, shared warehouse stations, and jump boxes as high-risk token leakage points. Enforce browser close-on-logout, cookie clearing, and separate OS/browser profiles for users who handle B2B orders or account data.
- Add telemetry for post-logout session reuse — Correlate logout events with continued successful requests for the same account, cookie, IP, or user-agent. This is the best detective control because replayed requests look legitimate to network controls.
- Require re-auth for sensitive workflows if supported — If the storefront or upstream identity layer supports step-up auth for actions like address changes, quote approval, or order placement, enable it. That does not fix stale-session browsing, but it can shrink business impact.
- MFA alone doesn't solve this because a replayed authenticated session cookie usually bypasses the login prompt entirely.
- A generic WAF is weak here because the traffic uses normal authenticated requests and valid application paths.
- Password reset by itself may not immediately kill the stale storefront session if the app fails to invalidate server-side session state on logout.
Crowdsourced verification payload.
Run this on an auditor workstation, CI job, or vuln-management box against your asset inventory, not on the target host. Invoke it as python3 check_cve_2025_22386.py --version 5.2.2407 for a single asset or python3 check_cve_2025_22386.py --csv commerce_assets.csv --column version for bulk checks; no special privileges are required.
#!/usr/bin/env python3
# check_cve_2025_22386.py
# Determine whether an Optimizely Configured Commerce version is affected by CVE-2025-22386.
# Exit codes:
# 0 = all checked assets are PATCHED
# 1 = one or more assets are VULNERABLE
# 2 = UNKNOWN / input error
import argparse
import csv
import re
import sys
from typing import Optional, Tuple
FIXED = (5, 2, 2408)
def parse_version(v: str) -> Optional[Tuple[int, int, int]]:
if v is None:
return None
s = v.strip()
# Accept values like 5.2.2408, v5.2.2408, 5.2.2408 (STS), 5.2.2408-LTS
m = re.search(r'(\d+)\.(\d+)\.(\d+)', s)
if not m:
return None
try:
return tuple(int(x) for x in m.groups())
except ValueError:
return None
def status_for_version(v: str) -> str:
parsed = parse_version(v)
if parsed is None:
return 'UNKNOWN'
if parsed < FIXED:
return 'VULNERABLE'
return 'PATCHED'
def check_single(version: str) -> int:
status = status_for_version(version)
print(status)
return 1 if status == 'VULNERABLE' else 0 if status == 'PATCHED' else 2
def check_csv(path: str, column: str) -> int:
overall = 0
seen = 0
try:
with open(path, newline='', encoding='utf-8-sig') as f:
reader = csv.DictReader(f)
if column not in (reader.fieldnames or []):
print(f'UNKNOWN: column "{column}" not found in CSV header', file=sys.stderr)
return 2
for row in reader:
seen += 1
version = row.get(column, '')
asset = row.get('asset') or row.get('hostname') or row.get('name') or f'row-{seen}'
status = status_for_version(version)
print(f'{asset}: {status} ({version})')
if status == 'VULNERABLE':
overall = 1
elif status == 'UNKNOWN' and overall == 0:
overall = 2
except FileNotFoundError:
print(f'UNKNOWN: file not found: {path}', file=sys.stderr)
return 2
except Exception as e:
print(f'UNKNOWN: failed to read CSV: {e}', file=sys.stderr)
return 2
if seen == 0:
print('UNKNOWN: CSV contained no rows', file=sys.stderr)
return 2
return overall
def main() -> int:
parser = argparse.ArgumentParser(description='Check Optimizely Configured Commerce versions for CVE-2025-22386')
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('--version', help='Single Configured Commerce version, e.g. 5.2.2407')
group.add_argument('--csv', help='CSV file containing asset inventory')
parser.add_argument('--column', default='version', help='CSV column name holding the version (default: version)')
args = parser.parse_args()
if args.version:
return check_single(args.version)
return check_csv(args.csv, args.column)
if __name__ == '__main__':
sys.exit(main())
If you remember one thing.
Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.