PHP2SHELL Explained
php2shell CVSS 9.2 · CRITICAL
PHP Core · disclosed 21 Oct 2026

PHP2SHELL

Two memory-safety bugs in the PHP interpreter's request lifecycle. Both let an unauthenticated attacker reach remote code execution with the web worker's privileges — the out-of-memory path (…618) before your application code even runs, and the stale-state path (…619) by reusing a worker across requests.

CVE-2026-109618 · OOM corruption CVE-2026-109619 · cross-request UAF
Severity
9.2
Auth needed
None
Patched
21 Oct
Exploited since
17 Oct
Active exploitation. Amazon Threat Intelligence observed exploitation attempts starting 17 Oct 2026 — four days before public disclosure. Apache mod_php is the highest-risk configuration because the fault is reachable before application code runs. Patch out of your normal cycle.
// check

Are you affected?

Enter the exact PHP version on the target. Logic runs entirely in your browser.

try: 8.3.21 8.4.27 8.1.30 7.2.34
// the two flaws

How each bug works

Both are classic memory-safety faults, reached through ordinary request handling. The mechanism below is conceptual — enough to understand reachability and impact, not a recipe.

Class
Memory corruption (heap)
Trigger
Allocation failure while per-request data is only partly initialized
Attack vector
Network, unauthenticated. Crafted multipart requests can exhaust memory while appearing within configured limits.
Impact
RCE as the web worker; confirmed from PHP 5.4.0 onward
Highest risk
mod_php — fault reachable before app code
Where it goes wrong
1
Request arrives. PHP begins allocating internal structures for the request body.
2
Memory runs out. An allocation fails mid-way, leaving request data half-initialized.
3
Cleanup runs anyway. The teardown path treats the incomplete structures as if fully valid — reading and freeing fields that were never set up.
4
Corruption. Operating on that invalid state corrupts the heap, which an attacker who controls the layout can steer toward code execution.
Why limits don't save you: RLIMIT_AS / RLIMIT_DATA can actually trigger the vulnerable path. Container and cgroup caps help availability but are not a complete fix.
Known OOM shapes: public research demonstrates oversized multipart fields, header amplification across many parts, and deeply nested empty-file controls. They are useful detection signals, not a substitute for patching.
The five exploit chains

Public analysis maps the two CVEs to five concrete chains — one OOM path (…618) and four stale-state paths (…619). Each has a different precondition and runtime surface.

ChainCVEVectorPre-authPreconditionSAPI
A…619Session injection + LFIyesApplication has an LFI sinkall
B…619var_destroy UAF → handler overwritenoApplication calls unserialize()all
C…619Handler mismatch → __destructyesTwo directories use different serialize_handler valuesFPM
D…618OOM corruption → stale EG()yesNone — any endpointmod_php
E…619FPM session OOM → stale BG()yesProducer calls session_start()FPM
Chain D is the headline. It needs no application feature, LFI, or unserialize() — only a reachable mod_php worker. The other chains depend on application behavior or FPM-specific configuration.
// walkthrough

Attack path on a real stack

Where the request travels across a typical PHP deployment, and where each flaw bites. Pick a flaw, choose detail level, then Play or click any component.

flaw
audience
request path fault location impact dashed = breach
// remediation

Fix & mitigations

Updating is the only complete remediation for both flaws. Temporary mitigations reduce exposure but do not remove the underlying fault — and every config change needs a full worker restart to take effect.

BranchPatched releaseStatus
8.5.x8.5.12supported
8.4.x8.4.27supported
8.3.x8.3.36supported
8.2.x8.2.35supported
7.4 – 8.1source backportno binary release
5.4 – 7.3— none —EOL · no fix
If you cannot patch immediately
CVETemporary mitigationApplied where
…618Enforce memory_limit=-1 so apps cannot override itphp.ini (admin), then restart workers
…618Bound post_max_size, upload_max_filesize, request size & nesting at the frontend. Do not use RLIMIT_AS/RLIMIT_DATA.server / proxy
…619Set session.upload_progress.enabled=0php.ini / FPM pool (before parsing) — not ini_set()
…619On PHP 8.5 also set fatal_error_backtraces=0php.ini (admin), then restart workers

Runtime ini_set() and .user.ini apply too late for the …619 mitigations — they must be set before request parsing.

Related advisory: CVE-2026-115560 (23 Oct). This distinct session-upload-progress issue needs an unusual runtime switch of session.serialize_handler before session_start(). Keep upload progress disabled; track the next PHP release for its fix.
// response drill

Make the call

A short defensive incident exercise. Work through the decisions in order; each answer explains the operational reason without simulating an exploit.

PHP2SHELL response drill

Scenario: an internet-facing service reports PHP 8.4.26 running via Apache mod_php.

0 / 3decisions correct
1 Triage

How should this version be classified?

2 Prioritize

Which deployment surface needs the fastest validation?

3 Contain

What is the complete first response?

Response path complete: confirm the web SAPI version after the restart, then retain logs and review the detection signals below.
// detection

Detection notes

Confirm your exact build after updating: php -v on CLI reflects the CLI SAPI; verify the web SAPI (mod_php / FPM) separately, then restart all workers.
// detection rules

Detection rules

Behavioral starting points, built from the confirmed mechanism — not signatures of any one exploit. Both flaws corrupt memory, so the strongest signal is host-side: worker crashes and respawns, not a clean network payload. Treat these as illustrative and tune to your stack.

SIEMSigma · host/app logs · strongest signal
# php-fpm / apache mod_php worker crash + UAF indicators
title: PHP2SHELL worker crash indicators
logsource:
  product: linux
  service: php-fpm        # also apache error_log for mod_php
detection:
  crash:
    message|contains:
      - 'exited on signal 11'    # SIGSEGV
      - 'exited on signal 6'     # SIGABRT
      - 'zend_mm_heap corrupted'
      - 'out of memory'
  interrupted:               # CVE-2026-109619 precondition
    message|contains:
      - 'Fatal error'
      - 'Maximum execution time'
  timeframe: 2m
  condition: crash or ( interrupted and crash )
falsepositives:
  - Legitimate OOM under load
  - Segfaults from unrelated extensions
level: high
IDSSuricata · network heuristic
# Burst of multipart POSTs from one source (CVE-2026-109618 surface)
alert http any any -> $HTTP_SERVERS any ( \
  msg:"PHP2SHELL possible OOM probe: multipart POST burst"; \
  flow:to_server,established; \
  http.method; content:"POST"; \
  http.header; content:"multipart/form-data"; nocase; \
  threshold: type both, track by_src, count 20, seconds 10; \
  sid:2026109618; rev:1; )
WAFModSecurity · request-shape heuristic
# Flag abnormal multipart bodies; tune thresholds per app. Reduces exposure, not a fix.
SecRule REQUEST_METHOD "@streq POST" \
  "id:2026109618,phase:2,log,pass,msg:'PHP2SHELL: inspect multipart shape',chain"
  SecRule REQUEST_HEADERS:Content-Type "@rx multipart/form-data" "chain"
    SecRule &FILES "@gt 200"        # unusually high part count — set to your real ceiling

# Also block the stale-state trigger name in multipart bodies:
SecRule MULTIPART_PART_HEADERS "@rx PHP_SESSION_UPLOAD_PROGRESS" \
  "id:2026109619,phase:2,log,pass,msg:'PHP2SHELL: upload-progress field present'"
Before you trust any rule: validate against your own traffic, watch false positives (crashes and large uploads happen for benign reasons), and remember these catch known patterns, not all variants. Absence of alerts does not prove safety — the …619 path can leave no required trace. Pre-built rule sets (e.g. the ikow PoC detection/ — Suricata, Sigma, ModSecurity, Lua) are useful to compare against, but review every rule before deploying it.
// sources

PHP.net advisory PHP session advisory (23 Oct) Tenable FAQ Rapid7 ETR Sansec CERT-EU 2026-018 ikow PoC research devploit research