On 21 October 2026, the team that builds PHP told the world about two serious flaws in their software. PHP is the engine behind a large share of the web. The flaws let a stranger run their own commands on a web server without any password.
This checklist now uses that event as its example. A checklist works better when each line names a real problem, not a general principle.
The short version
If you read nothing else, read this.
- A stranger can take control of a web server. No password. No trick. One message.
- The fault sits in the engine, not in the website. Updating your website does not fix it.
- Updating PHP fixes it. That is the only real fix.
- If you cannot update today, put a filter in front of the server. That buys time.
- A container helps after a break-in. It does not stop the break-in.
- If you run a website, ask one question today: what version of PHP serves my site, and who updates it?
What is PHP?
Most websites need software on the server to build each page. PHP is one of the most common choices. WordPress, WooCommerce, Magento, Drupal and many others depend on it.
Think of a restaurant. You see the dining room. You do not see the kitchen. PHP is the kitchen. Every page you load is a dish that came out of it.
When something goes wrong in the kitchen, the dining room cannot fix it. That is the situation here.
What went wrong
PHP reads the forms that visitors send. A form might say “name: Jane, email: jane@example.com”. PHP takes that apart and keeps a tidy list.
Now picture a paper form with numbered boxes.
The form starts with a total at the top. Someone writes “5 items”. Then they fill the boxes one by one.
Here is the flaw. PHP writes the total first, then fills the boxes. If it runs out of room at box three, the total still says five. Boxes four and five stay empty — but the paper is not blank. It has old writing showing through from a previous use.
Later, PHP reads the form again. It sees “5 items”. It reads boxes four and five. It finds writing there, so it treats that writing as real instructions.
An attacker can now do three things:
- Send a form designed to run out of room at the exact right moment.
- Choose what is written on the paper before the form is used.
- Write instructions, not data.
The instructions run as a command on the server. The attacker does not write code. The attacker writes a list entry that PHP later mistakes for code.
Why this one is worse than a normal bug
Most website bugs live in the website. You fix them with an update, a plugin patch or a code change.
This bug lives in the engine. It happens while PHP is still reading the incoming message. Your website has not woken up yet.
That detail has a hard consequence. Everything you built to keep people out runs after this point. Your login page, your password check, your security plugin, your firewall inside the app. None of them get a turn.
The fault happens in the reception area, before the security guard starts his shift.
Researchers proved this against fully updated software, including WordPress. A clean, current website on a vulnerable PHP version is still exposed.
How someone attacks with it
Five steps. None of them need a login.
- Find a target. Attackers scan the internet for servers that answer in a way that shows they run PHP. This step is automated and cheap.
- Send one crafted form. The message carries an unusually long field name. That is the lever that makes PHP run out of room at the right moment.
- Plant the payload. An earlier field in the same message leaves chosen data in memory. The broken form entry later reads that data.
- Trigger cleanup. The message fails. PHP tidies up. During tidying, it follows the planted data and runs the attacker’s command.
- Take what they want. The command runs as the web server account. From there: read files, install a back door, connect out to another machine.
The whole exchange can return a normal “200 OK” response. A successful attack does not have to break anything visible. There may be no crash, no error page and no slowdown.
The part most people miss
The same bug can hand data back to the attacker.
Web servers reuse memory between visitors. Normally one visitor never sees another visitor’s data. This flaw breaks that separation. An attacker can ask the server for memory it has already used, and receive the contents.
What sits in that memory? Passwords people just typed. Session cookies. API keys. Sometimes the private keys that secure encrypted traffic.
For many businesses this is the worse half. Code execution is loud. A quiet leak of customer passwords is not. And the leak works even if your site never starts a session.
How fast this moved
This was not a slow, orderly disclosure.
- 20 October: the flaw identifiers leaked on social media before any fix existed.
- 20 October: the PHP team published an early warning.
- 21 October, 07:43 UTC: an attack was already recorded in the wild. The fix was still six hours away.
- 21 October, 13:30 UTC: the fixed versions went live.
- 21 October, same day: a ready-made attack tool became public.
The gap between “we know about it” and “anyone can do it” was under a day. Plan for that speed in future. A quarterly patch cycle does not cover this class of event.
What to do
Five steps, in this order. The order matters.
Step 1: find out what you run
Ask your host or your team one question.
What version of PHP serves my website, and when was it last updated?
Do not accept a version number read from a command line. Ask for the version the web server actually uses. They are often different.
Step 2: update PHP
The safe versions are listed in the appendix below. If you are on an older branch, some vendors offer a backported fix. Ask.
Then check three things, because an update alone is not enough:
- Every worker process restarted. A new file on disk does nothing while an old process keeps serving.
- Every container rebuilt. A container carries its own copy of PHP.
- The site still works. Confirm it from a real page load, not from the server console.
Step 3: if you cannot update today, filter at the front door
A filter in front of the server can reject the message before PHP ever reads it. This is the single most useful stopgap.
It is not a fix. Treat it as a bandage measured in hours or days.
Step 4: assume the worst for the last few days
Attacks started before the fix existed. That means any exposed server was fair game.
Check three things:
- Error logs for memory errors that name no file. The appendix shows the exact line.
- Process logs for a web server account that started a command shell.
- Outbound connections from your web server to places it never normally talks to.
If any of those appear, treat it as a real incident. Assume credentials in memory are compromised. Rotate them.
Step 5: change one thing in your process
Answer this question honestly. How many hours passed between the public warning and a fixed version on your site?
If the answer is measured in weeks, that is the real finding. This event will not be the last of its kind.
Is a container worth it as a second line of defence?
Yes. But be clear about what it does, because most people expect the wrong thing.
What a container is
A container is a sealed room that holds your website and its copy of PHP. It has its own files, its own software and its own limits.
You get the same protection a locked room gives you inside a building.
The lock stops a burglar from walking into the next room. It does not stop the burglar from getting into the room.
So it does not stop this attack
Correct. It cannot.
The container runs the same flawed PHP. The message still arrives and PHP still reads it. A locked room that contains a broken machine is still a room with a broken machine in it.
The people who wrote PHP said the same thing in plain terms. They noted that a container memory limit “is not a complete mitigation.” Take them at their word.
So what does it actually buy you?
It decides how much damage one break-in causes. That is a real and valuable control. It is just a different question.
| What you set up | What it buys you after a break-in |
|---|---|
| A sealed room | The damage stays in one room. The database, the file server and the other sites stay out of reach. |
| No master key inside the room | The attacker cannot grab the building’s controls and take over the host. |
| Nothing worth stealing inside | The room holds no passwords, no keys and no customer data. |
| No way out | The attacker cannot call home, download tools or send stolen data away. |
| A room you rebuild often | You patch by replacing the room, not by hunting for hidden changes. |
| One worker per room | A single failure does not take down the whole site. |
The “no way out” line deserves the most attention. This attack ends with a command that usually wants to phone home. If the room has no exit, the command has nowhere to go.
When a container is a false sense of security
Plenty of sites run containers and get none of the benefit. Check for each of these:
- The master key is inside the room. The Docker control socket is mounted into the container. That is full control of the host, handed over.
- The room has no walls. The container runs privileged, or shares the host’s network and process space.
- The room is open to the rest of the building. The website folder is mounted from the host, and the host runs whatever lands there.
- Passwords are written on the wall. Secrets stored as environment variables. Any process in the room can read them.
- Nobody ever rebuilds the room. This is the common one. A container built two years ago is a server nobody has patched, and it is harder to find than a physical server.
- Everything can leave. Outbound traffic is open, so the attacker can reach the internet freely.
Any one of these turns containerisation into a word on a slide rather than a control.
Two traps, if you go ahead
Trap one. Do not let one failure kill the whole site. A container limit that kills the container also kills every worker in it. Prefer a limit that removes only the single worker that failed.
Trap two. Do not use the operating system memory limits called RLIMIT_AS or RLIMIT_DATA. They cause exactly the kind of memory failure this attack exploits. You would be widening the hole while trying to narrow it.
The honest ranking
Keep two numbers apart in your head: the chance of a break-in, and the cost of a break-in. Containerisation improves only the second one.
- Update PHP. The only step that removes the vulnerability.
- Filter at the front door. Stop the message before PHP reads it. The real stopgap.
- Isolate in a container. Assume a breach and cap the damage.
- Block outbound traffic. Turn a working shell into a dead end.
- Watch the logs. Catch what you did not stop.
If you can do only one thing this week, do the first. If you cannot, do the second. A container is worth the effort, but it does not replace either.
The checklist
Each line names a test you can actually run. Work through one section at a time.
Keep software current
- List every piece of software on your server that serves visitors, with its version and its owner.
- Include containers, appliances and any product that bundles its own copy of the runtime.
- Follow the security news for the engine, not only for the website software.
- Ask: how many hours passed between the last critical warning and a fixed version in production?
- Remove software you no longer need. Every extra package is future patch work.
- Check a patch from a real page load. A version number shown on a console proves nothing.
- Restart every worker. A new file on disk does not clear a running process.
- Move off end-of-life software before the vendor stops shipping fixes. PHP 8.2 stops on 31 December 2026.
Protect accounts and secrets
- Turn on two-factor authentication for every administrative and remote login.
- Give each account the least access it needs. Remove access when the task ends.
- Keep passwords and keys in a proper store. Not in code, not in documents, not in environment variables.
- Rotate any credential the web server can read. This flaw leaks memory, so treat that memory as public.
- Review privileged accounts on a schedule, not after an incident.
Reduce what is exposed
- Publish only the services that must be reachable from the internet.
- Hide management pages behind a VPN or an allowlist.
- Cap the size of incoming messages, the number of fields and the depth of nesting. Do it at the filter, not in the app.
- Block outbound traffic from web servers by default. Allow only what the service needs.
- Do not publish internal names, addresses or build details.
- Review firewall rules after every change. An old rule outlives its reason.
Limit the damage of a break-in
- Give each service its own trust boundary. Do not share an account, a folder or a runtime between services.
- Make the filesystem read-only where the application allows it.
- Run as a low-privilege user with minimal rights.
- Block outbound traffic by default. This is the control that turns a shell into a dead end.
- Rebuild container images on a schedule. A container you never rebuild is a server you never patch.
- Test a single worker failure. Confirm the site survives it.
Prepare to recover
- Keep backups that a compromised web account cannot reach or delete.
- Test a full restore on a schedule. Time it.
- Write the recovery steps. Name the owner of each step.
- Keep a clean image to rebuild from. Rebuild beats cleaning every time.
Watch and respond
- Collect web server, engine error, login and process logs.
- Alert when a web server account starts a command shell. This one signal catches this attack family and most copies of it.
- Alert on a memory error that names no file. That means a request failed before any page started.
- Alert on a form field name longer than your real traffic ever produces.
- Alert when a web server opens a connection to an unfamiliar address.
- Decide in advance who to call, how to isolate the service and how to kill a credential. Write it down.
- Review every incident and near miss. Change one control each time.
Confirm the change worked
- Test the block, do not assume the block. A filter that has never blocked anything has never been tested.
- Record the change, the owner and the result.
- Test from outside the network, not from a management machine.
- Re-check any setting the application can override. An override quietly undoes a fix.
The bottom line
- The current critical flaw is CVE-2026-109618 and CVE-2026-109619, together called php2shell. Both score 9.2 out of 10.
- The flawed code dates to 2012. Almost every PHP site older than the October 2026 update is exposed.
- An attacker needs one message and no password. The command runs before your website gets a turn.
- The flaw also leaks memory. Treat every password and key in web server memory as exposed.
- Attacks started before the fix shipped, and a ready-made tool is public.
- A container caps the damage of a break-in. It does not stop one.
- Improve the chance of a break-in and the cost of a break-in separately. They are different problems with different fixes.
For the engineers
The detail below is for whoever maintains the server. Everything above stands alone without it.
Fixed versions
| Branch | Vulnerable | Fixed |
|---|---|---|
| 8.5 | before 8.5.12 | 8.5.12 |
| 8.4 | before 8.4.27 | 8.4.27 |
| 8.3 | before 8.3.36 | 8.3.36 |
| 8.2 | before 8.2.35 | 8.2.35 |
| 8.1 and older | all builds | No upstream fix. Source backports exist for 7.4, 8.0 and 8.1. |
Advisories: GHSA-w8hw-w9fx-j79w (CVE-2026-109618) and GHSA-6wmf-4rv5-p3rp (CVE-2026-109619). Both CVSS v4.0 9.2.
Runtime coverage
Apache mod_php, PHP-FPM, php-cgi, FrankenPHP and LiteSpeed. The PHP project rates mod_php highest risk, because a single request reaches the bug before application code runs. Older builds from PHP 5.4.0 onward carry the same code path.
Trigger shape, CVE-2026-109618
The insert helper increments nNumUsed and nNumOfElements before it allocates the key copy in _zend_hash_str_add_or_update_i(). An oversized array-syntax field name makes that copy cross memory_limit. The bucket is counted but never initialised. A prior empty-name field with a chosen binary value seeds the freed block. At php_request_shutdown(), the destructor callback resolves to system().
A request body of roughly this shape is the signature:
=[planted bytes]&a=1&ZZZ...(approx 3.5MB)...[x]=1
Detection lines
PHP Fatal error: Allowed memory size of 16777216 bytes exhausted
(tried to allocate 3500032 bytes) in Unknown on line 0
Unknown on line 0 means the failure happened before any script loaded. A normal out-of-memory error names a file and a line.
Also alert on a web worker that spawns sh or bash, and on segmentation faults in the worker pool.
Interim configuration
Apply through php_admin_value in the FPM pool or the Apache configuration. ini_set() and .user.ini load too late to help.
session.upload_progress.enabled = 0
memory_limit = -1
post_max_size = 8M
; PHP 8.5 only
fatal_error_backtraces = 0
memory_limit = -1 removes the controllable failure point but lets one request consume all server memory. Monitor it, and restore the limit once patched.
Front-door rules
For nginx, inside each server block that passes requests to PHP:
if ($http_content_type ~* "multipart/form-data") {
return 403;
}
For Apache, inside each virtual host that serves PHP:
RewriteEngine On
RewriteCond %{HTTP:Content-Type} multipart/form-data [NC]
RewriteRule ^ - [F]
These break file uploads. Test a real upload and a checkout form afterwards, and remove the rule once PHP is patched. Read-only mode, if you can accept it:
if ($request_method !~ ^(GET|HEAD)$) {
return 503;
}
Also cap decoded body size at the proxy, including requests with no usable Content-Length. Cap field name length, field count, multipart part count, headers per part, upload count and nesting depth.
Container notes
Prefer a cgroup policy that kills only the affected worker. Killing the container also kills the master. Do not use RLIMIT_AS or RLIMIT_DATA, because a failed mmap() reaches the vulnerable path.
Packaging notes
rpms.remirepo.net had no update on release day. Move to distribution packages or build from source.
PHP 8.2 reaches end of life on 31 December 2026.
Closing note
Security depends on the service and its context. Use this checklist as a prompt for a review, and consult qualified security professionals when you need a formal assessment or an incident response.