Skip to content
folhas.io
secure by design for connected products· Articles

Articles

PHP2Shell and Node.js: what they ask of device makers

On 21 October 2026, PHP and Node.js released serious security fixes. Anyone making devices with a web panel, gateways or Node executables has three questions to answer: whether the product contains the component, whether there is a duty to report under Article 14, and how the fix reaches the units already in the field.

Published

On 21 October 2026, two runtimes that many connected products carry inside them released security fixes on the same day. PHP fixed two critical vulnerabilities, known as PHP2Shell, that may allow unauthenticated remote code execution. Node.js fixed ten, among them an out-of-bounds write in the Brotli decoder which, according to the project, allows remote code execution.

This text looks at the case from the side of those who make devices with one of these runtimes embedded (a web management panel, a gateway, a Node application packaged into an executable) and follows the three questions it raises at once: whether the component is in the product, whether there is a duty to report under Article 14 of the CRA, and how the fix reaches the units already sold.

What happened on 21 October

PHP released versions 8.5.12, 8.4.27, 8.3.36 and 8.2.35, which fix CVE-2026-109618 and CVE-2026-109619, after pre-announcing them the day before. The flaws were discovered and reported by researchers from Calif, Wiz and OpenAI, and the project, which assigned the two CVEs, gave each of them 9.2 in CVSS v4.0. The NVD rates them in another scoring system, CVSS v3.1: 9.8 for CVE-2026-109618, the value some manufacturers use in their advisories, and 8.1 for CVE-2026-109619. Linux distributions and extended support vendors received the fixes ahead of the release.

According to the php.net advisory, every PHP installation serving web requests is at risk, including PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed. The project’s technical advisories validate remote code execution under mod_php for both flaws, without depending on the application. Under PHP-FPM, the advisory for CVE-2026-109619 validated it too, through a path that depends on a suitable operation by the application; the one for CVE-2026-109618 concludes, from analysis of the code, that PHP-FPM is susceptible, but without a complete exploit demonstrated. PHP 8.1 and earlier no longer receive new releases but, given the severity, the security team published minimal fixes for the 7.4, 8.0 and 8.1 branches, in source code and without a new version number.

On the same day, Node.js released versions 22.23.4, 24.22.1 and 26.12.1, with ten CVEs fixed. The project rates the flaws by severity, without a CVSS score, and the highest in each line is “high”. One of the four high-severity ones is CVE-2026-111497, the out-of-bounds write in the Brotli decoder, reported by OpenAI Codex Security (Khai Tran and Sylvie Mayer) and fixed, according to the Node.js advisory, by Matteo Collina and Antoine du Hamel. The flaw is in Google’s Brotli library itself, in versions 1.0.3 to 1.2.0, and on 21 Oct Google released the fixed versions 1.2.1, 1.1.1 and 1.0.10. The library’s CVE record, assigned by Google, gives it 9.2 in CVSS v4.0. The others include two high-severity bypasses of hostname verification in TLS certificates, and CVE-2026-106074, of medium severity, which affects startup snapshots and SEA executables and comes up again further down. Node.js 20, end-of-life since 30 Apr 2026, received no fix, and the project points out that end-of-life versions are always affected when there is a security release.

On exploitation, the sources diverge, and each statement has a date. In its alert of 21 Oct 2026, CISA wrote that it had not yet observed exploitation of PHP2Shell. On the same day, Amazon Threat Intelligence said it had recorded in its honeypots an exploitation attempt dated 17 Oct, four days before disclosure, and others in the early hours of 21 Oct, still under embargo, while admitting it did not know whether any of them had succeeded against real systems. After the release, it saw new proofs of concept appear within a few hours, used almost immediately against its sensors, and described large-scale exploitation attempts. Cloudflare said it had observed attack traffic against its customers. As of 23 Oct 2026, neither of the two PHP CVEs was in CISA’s KEV catalogue.

For the Node.js flaws there is no known active exploitation: they were not in the KEV on that date either. There is, however, a public proof of concept for CVE-2026-111497, and on 22 Oct Imperva demonstrated code execution on an official Node.js 24.22.0 image for Linux arm64, which does not in itself indicate active exploitation. CISA’s SSVC assessment in that CVE’s record is no reference here: on 23 Oct it changed from “none” to “poc” and, in the next revision the same day, back to “none”.

LineFixed version on 21 Oct 2026Project end of support
PHP 7.3 and earliernone6 Dec 2021 or earlier
PHP 7.4, 8.0 and 8.1none; only minimal source-code fixes28 Nov 2022, 26 Nov 2023 and 31 Dec 2025
PHP 8.28.2.3531 Dec 2026
PHP 8.38.3.3631 Dec 2027
PHP 8.48.4.2731 Dec 2028
PHP 8.58.5.1231 Dec 2029
Node.js 20 and 25none30 Apr 2026 and 1 Jun 2026
Node.js 2222.23.430 Apr 2027
Node.js 2424.22.130 Apr 2028
Node.js 2626.12.130 Apr 2029

The Node.js dates are those of the project’s release schedule, which warns that they may change.

First question: is the component in the product?

CISA warned that PHP is often a component of other software and that its presence may not be directly observable. NCSC-NL, the national cyber security centre of the Netherlands, was more specific: PHP also comes embedded in appliances, network equipment and management interfaces, where the administrator may be unable to update it directly. For a device’s customer, the question may have no answer in sight; the one who can give it is the manufacturer.

With an SBOM generated at build time (article 01 explains why), the answer comes from querying the inventory, without opening a single device. In this case, each entry needs more than the name: the exact version; the fixes applied, because the minimal fixes for end-of-life branches carry no new version number (on 7.4, the binary compiled from them even identifies itself as 7.4.31-dev, below the last official release, 7.4.33); the way the runtime runs (for PHP, mod_php, PHP-FPM or php-cgi; for Node.js, the system runtime or an SEA executable); and whether the vulnerable code is reachable in the product. For CVE-2026-111497, the entry to look for is also the Brotli library, which may be in the firmware without Node.js. The CVE record places the flaw in the decoder: what matters is where the product decompresses Brotli data from untrusted sources, such as compressed HTTP responses or request bodies decompressed by middleware.

The public answer is a status per product and version (affected, not affected, fixed or under investigation), preferably in a machine-readable format such as VEX. The list NCSC-NL opened on 21 Oct, which CERT-EU advisory 2026-018 and the joint statement of 21 Oct recommend consulting, follows this logic: it is an inventory in SPDX, treats each entry as a lead for investigation and only marks it as fixed or not affected with a source from the project or the manufacturer. The advisory the manufacturer publishes is what allows its own row to be closed.

Even a minimal-risk assessment has work behind it. Synology confirmed impact on ten packages that depend on PHP and are fixed by updating PHP through Package Center. On the PHP component used internally by DSM, it concluded that it has no exposed attack surface and rated the risk as minimal but, even so, scheduled the fix for a later release, so that the results of vulnerability scanners and the compliance status stay consistent.

Several of the devices with embedded PHP that appear in this week’s advisories (firewalls, network appliances, network management systems) match categories of important products in Annex III of the CRA: firewalls in class II and network management systems in class I, according to the technical descriptions in Implementing Regulation (EU) 2025/2392, when that is the product’s core functionality (Art. 7(1)). In those categories, conformity assessment may require a third party: in class I, when harmonised standards, common specifications or European certification are not applied in full; in class II, always (Art. 32(2) and (3)), as article 03 shows.

Management panels with php-cgi

The php.net advisory expressly includes php-cgi among the installations at risk. It is the form the lighttpd documentation uses to connect PHP to that web server: the server starts the /usr/bin/php-cgi executable as persistent FastCGI processes, which handle successive requests. On OpenWrt, PHP is not part of the default image: it is an optional package from the packages feed (php8-cgi), and the default configuration of the uHTTPd server ships with the line that hands .php files to php-cgi commented out, and therefore disabled. Whoever adds a PHP panel to their image inherits this choice.

PHP’s two technical advisories analyse mod_php, PHP-FPM, LSAPI and FrankenPHP and do not treat php-cgi separately. Without that analysis, the prudent position is to treat it as affected, in whatever mode it runs, and update. Until the update reaches the field, the management interface should stay restricted to the administration network: CERT-EU and the joint statement of 21 Oct recommend considering isolating exposed applications from the public network, and Netgate points out that, by default, the pfSense web interface is only accessible from the local network. Netgate also asks users of the captive portal, by nature exposed to untrusted clients, to update PHP as soon as possible. Restricting the management interface does not protect the functions the device serves through PHP to untrusted networks.

A note for those who limit the memory of the PHP process in the startup script, with ulimit for example: the advisory for CVE-2026-109618 advises against RLIMIT_AS and RLIMIT_DATA, because they can trigger the flaw itself, and says that a container or cgroup limit neither reliably prevents the allocation failure nor is a complete mitigation; the php.net advisory treats it as an availability boundary. PHP’s temporary measures (memory_limit=-1 set so that applications cannot change it, a low post_max_size, session.upload_progress.enabled=0 and, on PHP 8.5, fatal_error_backtraces=0, with all workers restarted) reduce exposure to specific paths, and the project itself says that only the update resolves the issue completely.

Second question: is there a duty to report?

Since 11 Sep 2026 (Art. 71(2)), Article 14 has obliged the manufacturer to notify actively exploited vulnerabilities contained in its product, also for products placed on the market before 11 Dec 2027 (Art. 69(3)). The joint statement of 21 Oct by the Commission, ENISA, CERT-EU, Europol and the CSIRTs network recalled, with regard to PHP2Shell, that this notification is made through ENISA’s single reporting platform. The deadlines are in the article on the 24h and 72h reports.

The question specific to this case is a different one: does exploitation of a component in other systems, on its own, oblige whoever integrates it to notify? The Commission guidance of 27 July 2026, C(2026) 5252, answers it in point 218, which ENISA’s FAQ on the platform refers to (question 14), alongside section 5.4 of the Commission’s FAQs on CRA implementation of 3 Dec 2025: the manufacturer only has to notify actively exploited vulnerabilities contained in its product. If the vulnerability of a third-party component is being exploited in the product, it notifies. If the vulnerability cannot be exploited in the product (for example, because the vulnerable code is not reachable), or if there has been no exploitation in it, it is not subject to mandatory reporting for that manufacturer.

The 2025 FAQs are broader: they only exclude the case in which the vulnerability cannot be exploited in the product, and the definition in Art. 3(42) speaks of exploitation in a system, without saying which. The guidance is the more recent and more specific text; neither of the two is binding.

In practice, large-scale exploitation attempts against PHP servers are a trigger for assessment, not awareness. According to the same guidance, the manufacturer becomes aware when, after an immediate initial assessment, it has a reasonable degree of certainty that a vulnerability in its product is being actively exploited, and the signal may come from customers, from authorities or from the media. The triage questions are concrete: in which products and versions PHP serves requests, whether the interface is reachable from untrusted networks in the known installations, and whether telemetry, logs or customer reports show signs of exploitation in units of the product itself. If triage concludes, with a reasonable degree of certainty, that there is active exploitation in units of the product, that is the time that starts the 24-hour deadline for the early warning notification; it is worth recording it, and recording the conclusion too when it is negative. On the Node.js side, as of 23 Oct 2026 there was no known active exploitation raising this question, but the public proof of concept for CVE-2026-111497 justifies keeping triage open for products that decompress Brotli data from untrusted sources.

There is an asymmetry that weighs on the installed base. According to the guidance (section 9.1), reporting applies to products placed on the market before 11 Dec 2027 and continues after the support period has ended, whereas the vulnerability handling obligations of Annex I, Part II do not apply to those products. A device sold today may therefore have a duty to report without having, under the CRA, a duty to fix. After becoming aware, the manufacturer must also inform the impacted users and, where appropriate, all users of the vulnerability and, where necessary, of the risk mitigation and corrective measures they can deploy (Art. 14(8)). According to the guidance (point 220), this duty applies in proportion to the risk and does not require the information to be made public.

For cases without mandatory reporting, point 218 allows for the voluntary notification of Article 15, but the platform does not accept it yet: according to ENISA’s FAQ, it comes in a future phase.

A note on rigour. The C(2026) 5252 guidance is not binding: the Commission itself points out that only the Court of Justice of the European Union can interpret the CRA authoritatively. As of 23 Oct 2026 we had found no official position on this specific case. What is set out above is a reading of the guidance applied to it and does not replace the legal analysis of each product.

Third question: how does the fix reach the field?

The same CVE is fixed through different paths depending on how the runtime entered the product, and it is the SBOM that says which one applies.

Four paths by which a runtime fix reaches the device, depending on how the runtime entered the product. As a system package, from a distribution or feed: update the package, rebuild the image and ship it, because a fixed package is not yet firmware in the field. Compiled into the firmware image: update the recipe, rebuild and ship via OTA or locally; on an end-of-life branch there is no fixed version. Inside a Node.js SEA executable: rebuild with the fixed Node.js, regenerate the snapshots and redistribute, because updating the system Node.js is not enough. As a separate application or container: update the application through its own channel without changing the firmware, which assumes an application channel on the device. In all of them, the SBOM says which path applies and the fix only counts when it reaches each unit.
The work left after the project publishes the fix depends on how the runtime entered the product, and that information has to be on record before it is needed.

When the runtime comes from a distribution or a packages feed, the project’s fix reaches the package first, and for PHP the distributions had it before the release. That is not yet firmware on the units. On OpenWrt, the PHP recipe on the main branch of the packages feed is already at 8.4.27; whoever builds on it has to rebuild each product’s image and ship it. There are also cases where the component fix bypasses the product’s normal channel: Netgate published fixed PHP packages for pfSense but, on existing installations, that update is not applied through the web interface, only from the command line (a new installation, or an upgrade to a current version, already includes them). For Brotli, the fix can arrive from two sides. Node.js ships its own copy of the library by default, but can be compiled against the system one (the --shared-brotli option), in which case the fix arrives with the update of that library. As the flaw is in the library, the CVE record calls for updating any Brotli library or binary in the affected versions, which includes firmware that uses Brotli without Node.js.

If the runtime is compiled into the firmware image, the fix only arrives with a new image, distributed via OTA or locally. On end-of-life branches the work grows: for PHP 7.4, 8.0 and 8.1 there are only the minimal source-code fixes; for earlier branches and for Node.js 20, not even that. The table above also shows a problem that the article on the support period develops: no current PHP or Node.js line is supported by the project until the end of a five-year support period starting in December 2027.

A Node.js SEA executable is the Node binary itself with the application injected into it, and the documentation requires the application blob to be generated by the same version of the binary into which it is injected. From this we infer that updating the system Node.js does not fix an SEA already distributed: it has to be rebuilt with 22.23.4, 24.22.1 or 26.12.1 and redistributed. Startup snapshots follow the same logic, because Node refuses to load a snapshot generated by a binary of a different version, and they have to be regenerated with the fixed binary. CVE-2026-106074 gives one more reason: the entropy caches of crypto.randomUUID() and crypto.randomInt() were stored in the snapshots and SEAs, and every process started from them repeated the same values. If those values were used for unit identifiers or for secrets, it is worth reviewing them; the recommendation is ours, because Node.js has not published guidance on the values already generated.

The path gets shorter when the runtime runs as an application separate from the firmware. On devices with u-OS, Weidmüller moved Node-RED from a native system service to a containerised application, which, according to the manufacturer itself, allows Node-RED and the firmware to be updated independently.

The same Node.js release also touched TLS trust: it blocks, with a list compiled into the binary, eight eMudhra intermediate certificates whose private keys were reported as compromised, and updates the embedded root store to that of NSS 3.131.1. A device with its own root store needs an update path for that too. The eMudhra incident, and what the same week brought for the Linux kernel and for Python, are in article 10.

As for what the CRA requires here, the dates matter. From 11 Dec 2027, for products placed on the market from that date (or substantially modified after it, Art. 69(2)), Annex I, Part II requires, during the support period (Art. 13(8)), that vulnerabilities be addressed and remediated without delay in relation to the risks they pose, with security updates separate from functionality updates where technically feasible, secure distribution mechanisms (automatic, where applicable) and updates made available without delay and, as a rule, at no cost, accompanied by advisory messages to users. For the units already in the field, fixing is today a technical and contractual decision, which the CRA does not impose. Outside the CRA, for consumer goods with digital elements, Directive (EU) 2019/771 (Art. 7(3), transposed in Portugal by Decree-Law No. 84/2021) requires the seller to ensure that the consumer receives the updates, including security updates, needed to keep the goods in conformity for the period of time the consumer may reasonably expect.

The duty to report upstream

The CRA also draws the reverse direction. For products placed on the market from 11 Dec 2027, Article 13(6) obliges a manufacturer that identifies a vulnerability in an integrated component to report it to whoever maintains that component and, if it develops a fix, to share it. The Commission guidance (section 9.2.1) waives the report when the manufacturer can confirm that the maintainer already knows about the vulnerability, as happens with flaws the project itself has published, and does not require the shared fix to be accepted. For the products on the market today none of this is an obligation yet, but sharing a fix of your own (a backport to an end-of-life PHP branch, for example) is good practice. How to send and receive these reports is the subject of the article on coordinated disclosure.

What to do this week

For those with PHP or Node.js products in the field:

  • Query the SBOM of each product and version in the field for PHP (and how it runs), Node.js (system or in an SEA) and the Brotli library, with the exact version and the fixes applied. Where there is no SBOM, the build manifest is the starting point.
  • Publish the status per product and version (affected, not affected, fixed or under investigation) and update it as the analysis progresses.
  • Open the Article 14 triage for the products in which PHP serves requests, with an owner, a written criterion and the time of conclusion recorded.
  • Prepare the fixed images: PHP 8.5.12, 8.4.27, 8.3.36 or 8.2.35; Node.js 22.23.4, 24.22.1 or 26.12.1; Brotli 1.2.1, 1.1.1 or 1.0.10 where the library is present; SEAs and snapshots rebuilt; root store, if the device has one of its own.
  • In the new images, also follow PHP’s informational advisory of 23 Oct 2026, which recommends keeping session.upload_progress.enabled=0 until the next regular release, because of an additional vector the project considers unlikely (CVE-2026-115560).
  • Until the update arrives, tell customers which measures are within their reach, starting with restricting the management interface to the administration network.
  • For products with PHP 8.1 or earlier, or with Node.js 20, decide between migrating to a newer version, contracting extended support or maintaining your own fixes, and put the decision in writing.

Sources