Code Security

PHP2Shell Explained: How CVE-2026-109618 and CVE-2026-109619 Turn One Request Into RCE

Amartya | CodeAnt AI Code Review Platform
Sonali Sood

Founding GTM, CodeAnt AI

On October 21, 2026, the PHP project shipped an emergency security release for two critical, unauthenticated remote code execution flaws in PHP core, tracked as CVE-2026-109618 and CVE-2026-109619 and nicknamed PHP2Shell. Both carry a CVSS v4.0 score of 9.2. The bugs were discovered by Calif, Wiz Research and OpenAI through OpenAI's "Patch the Planet" initiative, coordinated with the PHP security team.

The "patch now" message has been covered everywhere, and you should patch now. This piece is for the engineer who has already done that and wants to understand what actually broke, why it sat in the codebase since PHP 5.4.0, why your deployment's exposure depends on whether you run mod_php or PHP-FPM, and how to reason about this class of bug in your own code review.

A note on scope and attribution. CodeAnt did not discover these vulnerabilities and has not independently reproduced them. The analysis below synthesizes the public technical disclosures from the PHP project, Calif (Ryan Austin), Wiz Research, Sansec, JFrog, AWS and others, with links in the Sources section. Where the public writeups disagree on which CVE maps to which code primitive, we say so rather than invent precision. Code excerpts are drawn from PHP's open-source tree and the published research, reproduced here only to explain the mechanism.

At a Glance

Field

Detail

Nickname

PHP2Shell

CVEs

CVE-2026-109618 (OOM corruption), CVE-2026-109619 (stale global state)

Severity

CVSS v4.0 9.2 (Critical), both

Class

Unauthenticated remote code execution via use-after-free; one path also leaks process memory

Component

PHP core (the engine), independent of the application on top

Authentication

None required

Affected

PHP 5.4.0 through the versions below; vulnerable code dates to PHP 5.4.0 (2012)

SAPIs

Apache mod_php and PHP-FPM both affected; exposure differs by SAPI (see below)

Fixed in

PHP 8.5.12, 8.4.27, 8.3.36, 8.2.35

No fix

PHP 8.1 and earlier (end of life)

Exploited in the wild

Yes. Amazon Threat Intelligence reported attempts from October 17; Cloudflare reported attacks at 07:43 UTC on October 21, before the 14:00 UTC release

Public exploit

Yes. The PHP2Shell tool (credited to Calif) and technical writeups are public

Remediation

Update PHP. It cannot be fixed in application code

What is PHP2Shell, & Why is This PHP Vulnerability Different?

Most PHP security vulnerability news items you read about are bugs in an application or a plugin: a WordPress component, a Magento module, a Joomla extension. You patch the component and move on. PHP2Shell is not that. It is a flaw in the PHP engine itself, in the code that parses an HTTP request before a single line of your application runs.

That distinction is the entire story. Because the vulnerable code lives below the application, a fully patched, well-written, hardened application is still exploitable if it runs on a vulnerable PHP build. Calif reported confirming unauthenticated RCE against clean, current versions of a long list of widely deployed PHP applications:

  • WordPress 7.1.2

  • Drupal 11.4.3

  • Joomla 6.1.4

  • MediaWiki 1.46.2

  • Roundcube 1.7.4

  • Magento 2.4.9

  • phpMyAdmin 5.2.3

  • Nextcloud 35.0.1

  • Laravel 13.11

  • Symfony 8.1

  • WHMCS

None of these appear on that list because of a bug in their own code. They appear because they are PHP, and PHP is the vulnerable surface. That is also why some of the mainstream coverage has reached for figures as dramatic as "up to 70% of the world's websites." Treat that framing as a press estimate of PHP's overall market share rather than a measured count of exploitable hosts, but the shape of it is right: this is a Log4Shell-class event in terms of population size.

The two CVEs, by their official descriptions

The PHP project assigned two identifiers, and the cleanest way to keep them straight is by the root-cause label the advisory gave each:

CVE

PHP's description

Core idea

CVE-2026-109618

PHP Remote Code Execution (OOM corruption)

An out-of-memory fatal interrupts PHP mid-request, while internal request data is only partially initialized. Request cleanup then branches on that invalid data.

CVE-2026-109619

PHP Remote Code Execution (stale global state)

PHP retains global state pointing into a request's heap. If that state is not reset before the heap is freed, a later request on the same worker consumes it.

Both are, at bottom, use-after-free vulnerabilities. Both are reachable by an unauthenticated remote attacker. The shared trigger is the same, and it is worth understanding on its own because it is the key that unlocks everything else.

The Shared Primitive: A Controllable Out-of-Memory Fatal

Neither bug is a classic injection. The attacker is not smuggling a payload past a parser. The attacker is forcing PHP to run out of memory at a moment of their choosing, so that a fatal error interrupts PHP's own C code before it finishes a cleanup step. Everything downstream, the dangling pointers, the stale globals, the eventual code execution, flows from that single capability.

To weaponize an out-of-memory condition you need two things: a way to deliver enough memory pressure, and a way to land the fatal at a precise point. Calif's research documents both.

Bypassing post_max_size to reach memory_limit

PHP has a post_max_size guard that is supposed to cap request bodies well below memory_limit, so a single request can't exhaust the heap. According to Calif, PHP compares post_max_size against the declared Content-Length before parsing begins, but does not enforce the same limit against the bytes it actually consumes. A chunked HTTP/1.1 request, or a lengthless HTTP/2 multipart request, carries no meaningful declared length, so Apache can pass it straight through. A configured input-decompression filter produces the same mismatch between compressed and decoded size. The guard that was supposed to make memory exhaustion impossible is bypassed.

Exhausting memory_limit while staying under post_max_size

The second technique is more elegant and does not even need the bypass. An RFC1867 file-upload part with filename="" contains no file data, and PHP does not count it toward max_file_uploads. But PHP still materializes all six $_FILES entries for it: name, full_path, type, tmp_name, error and size.

Now nest the field name, for example files[a][b][c][d].... PHP has to build that nested array hierarchy separately for each of the six entries. One tiny, empty upload record expands into a cascade of native HashTable, Bucket and string-key allocations. Hundreds of these records can occupy more than the default 128 MiB heap while the complete HTTP body stays under the default 8 MiB post_max_size. The attacker now controls, to a fine grain, exactly which later allocation is the one that tips the process over memory_limit and triggers the fatal.

That is the loaded gun. The three use-after-free primitives below are three different things the attacker can point it at.

CVE-2026-109618: OOM Corruption Via the Request-Variable Hash Table

The first interruption point lives in _zend_hash_str_add_or_update_i, the routine that inserts a key into a PHP array. The ordering of operations is the bug. Per the published analysis, PHP increments the table's live counters before it finishes initializing the new bucket:

idx = ht->nNumUsed++;
ht->nNumOfElements++;

p = ht->arData + idx;
p->key = zend_string_init(str, len, persistent); /* may zend_bailout() */

p->h = ZSTR_H(p->key) = h;
ZVAL_COPY_VALUE(&p->val, pData);
idx = ht->nNumUsed++;
ht->nNumOfElements++;

p = ht->arData + idx;
p->key = zend_string_init(str, len, persistent); /* may zend_bailout() */

p->h = ZSTR_H(p->key) = h;
ZVAL_COPY_VALUE(&p->val, pData);
idx = ht->nNumUsed++;
ht->nNumOfElements++;

p = ht->arData + idx;
p->key = zend_string_init(str, len, persistent); /* may zend_bailout() */

p->h = ZSTR_H(p->key) = h;
ZVAL_COPY_VALUE(&p->val, pData);

Incrementing nNumUsed makes the new bucket visible to teardown. If the very next line, the key allocation, is the allocation that hits memory_limit, PHP performs a zend_bailout() (a non-local jump, implemented with longjmp) and control never reaches the key, hash, value, or collision-chain assignments. The counter stays incremented. The bucket's contents do not exist. Later, array destruction walks every slot up to nNumUsed and treats that uninitialized slot as a live bucket:

for (p = ht->arData; p != ht->arData + ht->nNumUsed; p++) {
    i_zval_ptr_dtor(&p->val);
    if (p->key) {
        zend_string_release(p->key);
    }
}
for (p = ht->arData; p != ht->arData + ht->nNumUsed; p++) {
    i_zval_ptr_dtor(&p->val);
    if (p->key) {
        zend_string_release(p->key);
    }
}
for (p = ht->arData; p != ht->arData + ht->nNumUsed; p++) {
    i_zval_ptr_dtor(&p->val);
    if (p->key) {
        zend_string_release(p->key);
    }
}

The critical detail for exploitability is where this executes. This path is reached through ordinary request-variable registration (php_register_variable_ex → zend_symtable_str_update_ind → _zend_hash_str_add_or_update_i), which runs during php_request_startup(), before your target PHP file runs at all. On Apache mod_php, a crafted multipart request can corrupt this table before any application code, authentication check, or .htaccess rule gets a vote.

This is the clearest illustration of why PHP2Shell is a pre-authentication, pre-application vulnerability, and it is also where the SAPI split first appears.

mod_php vs PHP-FPM: Why Your Exposure Depends on Your Stack

The single most useful thing to understand operationally is that mod_php and PHP-FPM handle a startup fatal differently, and that difference decides how easily each bug reaches code execution.

When the key allocation fatals during startup, the two SAPIs diverge:

/* mod_php: registered request cleanup runs even after startup failure */
php_request_shutdown(NULL);       /* destroys the malformed $_POST/$_FILES table */

/* PHP-FPM: startup failure takes the process-exit branch */
php_module_shutdown();
return FPM_EXIT_SOFTWARE;          /* no request-level table destruction */
/* mod_php: registered request cleanup runs even after startup failure */
php_request_shutdown(NULL);       /* destroys the malformed $_POST/$_FILES table */

/* PHP-FPM: startup failure takes the process-exit branch */
php_module_shutdown();
return FPM_EXIT_SOFTWARE;          /* no request-level table destruction */
/* mod_php: registered request cleanup runs even after startup failure */
php_request_shutdown(NULL);       /* destroys the malformed $_POST/$_FILES table */

/* PHP-FPM: startup failure takes the process-exit branch */
php_module_shutdown();
return FPM_EXIT_SOFTWARE;          /* no request-level table destruction */

On Apache mod_php, request shutdown runs even after a failed startup, so it reaches the hash-table teardown and consumes the malformed bucket. The corruption is realized. Per Calif, this yields RCE against arbitrary PHP scripts on mod_php for PHP >= 7.3.

On PHP-FPM, the startup failure exits the worker. There is no second request in that process to consume the dangling state, so this particular interruption point does not reach RCE on FPM.

This does not make PHP-FPM safe. It means the OOM-corruption-at-startup path is a mod_php problem, and the attacker needs a different interruption point, one that fatals after startup succeeds, to reach an FPM worker that survives to serve another request. PHP2Shell has exactly that, and it routes through two features you have probably never thought about as attack surface: session upload progress and the generic unserializer.

CVE-2026-109619: Stale Global State Via Session Upload Progress

PHP's session.upload_progress feature exists so a browser can poll how far along a large file upload is. To do that, it has to open and populate a session while PHP is still parsing the request body, which is to say before the engine considers its modules fully activated. That early-initialization window is the problem.

The sequence during request startup is roughly:

PG(modules_activated) = 0;
/* ... */
php_hash_environment();     /* parses request variables and multipart input */
zend_activate_modules();    /* runs normal module RINIT */
PG(modules_activated) = 1;
PG(modules_activated) = 0;
/* ... */
php_hash_environment();     /* parses request variables and multipart input */
zend_activate_modules();    /* runs normal module RINIT */
PG(modules_activated) = 1;
PG(modules_activated) = 0;
/* ... */
php_hash_environment();     /* parses request variables and multipart input */
zend_activate_modules();    /* runs normal module RINIT */
PG(modules_activated) = 1;

Multipart parsing can enter the session extension before modules_activated is set. On the first qualifying file part, php_session_rfc1867_callback() calls php_rinit_session(0) and stashes request-owned pointers, PS(id) and PS(http_session_vars), into the session module's globals. At this moment those globals point into the current request's heap, but normal module activation has not finished.

Now interrupt it. If multipart parsing fatals (using the OOM primitive above) before the matching MULTIPART_EVENT_END cleanup runs, that cleanup is skipped, and request shutdown gates the session module's RSHUTDOWN on the activation flag that was never set:

if (PG(modules_activated)) {
    zend_deactivate_modules();   /* skipped: the flag is still false */
}
/* ... */
shutdown_memory_manager(/* ... */);  /* the request heap is freed anyway */
if (PG(modules_activated)) {
    zend_deactivate_modules();   /* skipped: the flag is still false */
}
/* ... */
shutdown_memory_manager(/* ... */);  /* the request heap is freed anyway */
if (PG(modules_activated)) {
    zend_deactivate_modules();   /* skipped: the flag is still false */
}
/* ... */
shutdown_memory_manager(/* ... */);  /* the request heap is freed anyway */

The result on mod_php: the worker survives with PS(id) and PS(http_session_vars) still pointing into a heap that has just been freed. A dangling session.

The attacker then sends a second multipart request carrying a session ID and the PHP_SESSION_UPLOAD_PROGRESS field but no file part. With no file part, it reaches MULTIPART_EVENT_END without ever hitting FILE_START, so the early reset never runs. Cleanup calls php_session_initialize(), which reads the stale (freed) session ID, and php_session_track_init(), which destroys the stale (freed) session value. Both operate on memory that belongs to a prior, torn-down request. That is a textbook use-after-free, reachable with two unauthenticated HTTP requests.

The PHP-FPM Path: An Old Friend, the Generic Unserializer

To reach PHP-FPM reliably, the research needed request-owned state that an application leaves dangling after startup succeeds, so that the FPM worker lives on to serve the follow-up request. The answer is PHP's generic unserializer, the same subsystem behind years of unserialize() type-confusion and object-injection bugs.

php_var_unserialize_init() allocates bookkeeping (a reference list and a destructor list) from the request heap and publishes it in the Basic extension's globals, BG(unserialize).data, with a nesting level. Nested decodes reuse it. Normal completion calls php_var_unserialize_destroy() to release it and clear the pointer when the nesting level returns to zero.

A fatal inside a live decode skips that destroy. The request heap is freed while BG(unserialize).data still points into it and the nesting level is still nonzero. Because this fatal happens after a successful request startup, the PHP-FPM worker survives. The next multipart request reaches session upload progress before Basic's normal request initialization, and the stale nonzero nesting level makes php_var_unserialize_init() treat the dangling pointer as an active nested decoder.

The application reaches this through any ordinary post-startup decode: session_start(), session_decode(), unserialize(), or a wrapper such as WordPress's maybe_unserialize(). The decoded data can be completely legitimate. The attacker does not inject a malicious serialized payload; they control the surrounding memory pressure so that a genuine decode is interrupted at a useful point, then consume the stale state from the surviving worker.

From stale state to a write primitive

The destructor list is where this turns into controlled code execution. Each destructor block tracks a signed used_slots count, and the allocation helper checks only whether adding entries would exceed capacity, assuming the count cannot be negative:

if (!var_hash ||
        var_hash->used_slots + num > VAR_DTOR_ENTRIES_MAX) {
    /* allocate a fresh block */
}
/* otherwise, use the selected slots */
if (!var_hash ||
        var_hash->used_slots + num > VAR_DTOR_ENTRIES_MAX) {
    /* allocate a fresh block */
}
/* otherwise, use the selected slots */
if (!var_hash ||
        var_hash->used_slots + num > VAR_DTOR_ENTRIES_MAX) {
    /* allocate a fresh block */
}
/* otherwise, use the selected slots */

After reclaiming the freed storage with attacker-influenced bytes, that assumption is violated. On the tested 64-bit layout the slot address is computed as block + 16 + 16 * used_slots, so a negative used_slots selects memory before the intended array. Duplicate keys in the serialized input then reach var_push_dtor_value(), which preserves the prior value with a real ZVAL_COPY_VALUE(tmp_var, rval), copying a genuine zval, a pointer plus its type tag, into that attacker-chosen location. Control of where PHP writes a typed pointer is control of the interpreter. This is application-dependent RCE under both mod_php and PHP-FPM.

If you have followed PHP's deserialization history, the shape will be familiar. Searches for php unserialize exploit and php unserialize type confusion have pointed at this subsystem for a decade. PHP2Shell does not need a vulnerable unserialize() call site in your code; it abuses the unserializer's internal bookkeeping through a memory-safety fault, which is a different and broader problem than the classic object-injection bugs.

The Heartbleed Half: Unauthenticated Memory Disclosure

Code execution is the headline, but one of the bugs leaks memory on its own, and that deserves separate attention because it can be devastating even where RCE is not achieved.

Per Sansec's analysis, the upload-progress handler can return freed worker memory to the client inside the PHPSESSID response cookie. Because the handler runs before application code, this works even if the application never calls session_start(). The leaked bytes are whatever previously lived in that worker's heap: session tokens, passwords, API keys, and in the worst case TLS private keys from other requests the worker handled. This is a Heartbleed-style information-disclosure primitive, unauthenticated, requiring no application cooperation, and it is a strong reason to treat any exposure window as a credential-rotation event, not just a patch event.

Why Did This Survive Since PHP 5.4.0?

This is the part that matters most for anyone who writes or reviews C, or who builds security tooling, and it is where the "just patch" coverage stops short.

The vulnerable pattern is old. Stefan Esser documented the first PHP memory-limit remote vulnerability in 2004. In 2020, bug #78876 reported that long multipart variables could cause an out-of-memory condition and leave temporary files uncleaned; it was treated as a robustness issue. In 2024, the OSTIF/Quarkslab audit of PHP rediscovered a related OOM problem but could not prove exploitation, so it was not fixed. It took until 2026 for the exploitation to be demonstrated end to end.

The reason it hid in plain sight is structural, and Calif generalized it into a single pattern worth memorizing:

publish or destroy state → perform fallible work → initialize, commit or restore.

If a non-local fatal lands during the fallible work, the commit or rollback is skipped, and a surviving consumer later trusts false state.

The _zend_hash bug is "increment the live count, then do the fallible key allocation, then write the bucket." The session bug is "publish request pointers into module globals, then do fallible parsing, then run the cleanup that was supposed to reset them." The unserializer bug is "publish decoder bookkeeping, then do the fallible decode, then destroy it." Same shape, three locations.

The thing that makes this class invisible to ordinary review and most tests is non-local control flow. zend_bailout() is a longjmp. A reviewer reading top to bottom sees "allocate, then initialize," and it looks fine, because on the happy path it is fine. The bug only exists on the edge where an allocation in the middle jumps out of the function entirely. A unit test that never induces an allocation failure at precisely that line will never see it. A fuzzer that never applies controlled memory pressure at that point will never see it. The defect is real in the source and absent from every normal execution trace.

What this means for code review and PHP vulnerability scanning

This is CodeAnt's actual interest in PHP2Shell, and it is a point the explainers are not making: the hard bugs are the ones that live on the error-handling and interruption paths, not the happy path. Finding them requires reasoning about what state is published before a fallible operation and whether every abnormal exit restores it. That is exactly the kind of cross-path reasoning that distinguishes real static analysis and AI-assisted review from pattern-matching on known-bad function names.

Concretely, when you audit C extensions, or when you evaluate a PHP vulnerability scanner or code-review tool, the questions that would have caught this family are:

  • Does any counter, flag, or global get published (made visible to a consumer) before the operation that could fail?

  • Can that operation trigger a non-local exit (longjmp, zend_bailout, a fatal, an exception across a C boundary)?

  • On every abnormal exit, is the published state reset before the memory it references is freed?

  • Does a later request on the same worker, or a later teardown, consume that state?

Those four questions are the signature of an interruption-point use-after-free. They are not PHP-specific; the same shape appears in any runtime that mixes manual memory management with non-local error handling.

Affected Versions & The Fix

Update to a fixed release. Per the PHP October 2026 security release:

PHP branch

Affected

First fixed version

PHP 8.5

8.5.0 – 8.5.11

8.5.12

PHP 8.4

8.4.0 – 8.4.26

8.4.27

PHP 8.3

up to 8.3.35

8.3.36

PHP 8.2

up to 8.2.34

8.2.35

PHP 8.1 and earlier

End of life

No official fix

PHP officially supports 8.2 and later. If you are on 8.1 or older, there is no upstream patch; your realistic options are to get your provider to backport the fix or to migrate to a supported branch. "It's internal" is not a mitigation here, because the trigger is a plain HTTP request to any reachable PHP endpoint.

Two operational traps that have already bitten people:

  • The Remi repository gap. If you install PHP from Remi's repository (common on RHEL-based servers), the volunteer-run repo did not publish the fix on release day. Use your distribution's packages or build from source until it catches up.

  • PHP-FPM reads its own pool config, not the CLI php.ini. Changing php.ini and checking php -v on the command line proves nothing about what your FPM workers are running.

How to verify you are actually patched

Do not trust the command line alone. Verify in the context that serves requests:

  1. Check the running version in a request served by the web SAPI (a phpinfo() page behind auth, or your observability tooling), not just php -v in a shell. The CLI binary and the FPM/Apache binary can differ.

  2. If you run PHP-FPM, confirm you actually reloaded the workers after upgrading (systemctl reload php8.3-fpm, with your version), and that the pool is serving the new build.

  3. If you run containers, confirm the base image was rebuilt and redeployed, not just pulled. A running container keeps its old PHP until it is replaced.

  4. Roll credentials that could have been exposed through the memory-disclosure path if you had any internet-facing exposure before patching: session secrets, API keys, and TLS private keys on affected hosts.

If You Genuinely Cannot Patch Within the Hour

These are temporary and partial. They buy time; they are not a fix. Combine them, because the PHP core developers have publicly warned that any one of them alone is insufficient.

  • Reject multipart/form-data at the web server. Every known trigger is a multipart request, and PHP parses that body before your application runs. Blocking it at nginx or Apache stops the delivery mechanism, but it breaks legitimate file uploads and some checkout/cart flows, so test before you leave it in place.

  • Take the service offline or make it read-only. Application-level maintenance modes (Magento, WordPress) do not help, because PHP parses the request before the application checks any maintenance flag. Serving a static page from the web server, or restricting to GET/HEAD, actually helps.

  • PHP settings, with caveats. Disabling session.upload_progress.enabled and setting memory_limit = -1 were published as partial mitigations. On their own they are insufficient, and memory_limit = -1 removes PHP's memory cap, so a single request can exhaust server memory. If you use them, monitor memory and restore the limit after you patch. For PHP-FPM these go in the pool config, not the CLI php.ini.

Detection: What PHP2Shell Exploitation Looks Like on the Wire

Because the primitive is memory pressure rather than a recognizable payload string, signature matching is weak here. Behavioral indicators are stronger. From the published mechanics, suspicious traffic tends to include:

  • multipart/form-data requests with upload parts that have an empty filename="" and deeply nested field names (a[b][c][d]...), the memory-amplification pattern.

  • Chunked or lengthless (HTTP/2) multipart bodies whose consumed size vastly exceeds any declared Content-Length, the post_max_size bypass.

  • Repeated multipart requests carrying PHP_SESSION_UPLOAD_PROGRESS and a session ID with no file part, the second-stage trigger for the session use-after-free.

  • Anomalous or high-entropy data appearing in PHPSESSID response cookies, a sign of the memory-disclosure path.

  • Clusters of worker crashes, segfaults, or out-of-memory kills correlated with the above request shapes.

Note that in-the-wild exploitation predates public disclosure in this case, so absence of these patterns in recent logs is not proof you were not probed. Treat the gap between your exposure window and your patch time as the risk window.

The Takeaway for Engineering Teams

PHP2Shell is a reminder that the most dangerous vulnerabilities are not always fresh code. This one was shaped in 2012, hinted at in 2020, half-found in 2024, and only proven exploitable in 2026, because it lives on the interruption path where normal tests, fuzzers and reviewers do not look. The defensive lesson generalizes well beyond PHP: audit what state your code publishes before any operation that can fail non-locally, and make sure every abnormal exit resets it before the memory it points to is freed. That is the exact class of cross-path reasoning that good automated code review exists to do, and it is where the next decade of memory-safety bugs will keep hiding.

Patch PHP first. Then go look at your own error-handling paths.

Sources and Further Reading

  • PHP project, October 2026 security release and advisories (CVE-2026-109618, CVE-2026-109619)

  • Calif Research, "php2shell: a tale of two PHP remote vulnerabilities" (Ryan Austin)

  • Wiz Research, PHP2Shell technical writeup (Sagi Tzadik, Shir Tamari, Nir Ohfeld, Tomer Schwartz)

  • Sansec Forensics, "PHP2Shell: Critical PHP RCE (CVE-2026-109618, CVE-2026-109619)"

  • JFrog Security Research, "PHP2Shell: All You Need to Know"

  • AWS Security Bulletin 2026-143 (PHP web runtimes)

  • Tenable, SoCRadar and runZero advisories on CVE-2026-109618 / CVE-2026-109619

  • OpenAI "Patch the Planet" initiative

FAQs

Is PHP2Shell a vulnerability in my application or in PHP itself?

Does PHP2Shell affect PHP-FPM or only Apache mod_php?

Which PHP versions are affected, and which are fixed?

Is PHP2Shell being exploited in the wild?

Can a web application firewall stop PHP2Shell?

Start Your 14-Day Free Trial

AI code reviews, security and quality trusted by modern engineering teams.

Table of Content
No headings found on page

Ship clean & secure code faster

Get Pentest Report

NO CC REQUIRED