<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Server – Linux und Ich</title>
	<atom:link href="https://linuxundich.de/en/tag/server/feed/" rel="self" type="application/rss+xml" />
	<link>https://linuxundich.de/en</link>
	<description>A German Linux blog, in English</description>
	<lastBuildDate>Thu, 22 Oct 2026 08:01:43 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.3</generator>
	<item>
		<title>PHP2Shell: How to check your Docker containers for the PHP vulnerability</title>
		<link>https://linuxundich.de/en/php2shell-check-docker-containers/</link>
					<comments>https://linuxundich.de/en/php2shell-check-docker-containers/#respond</comments>
		
		<dc:creator><![CDATA[Christoph Langner]]></dc:creator>
		<pubDate>Thu, 22 Oct 2026 07:53:20 +0000</pubDate>
				<category><![CDATA[GNU/Linux]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[homelab]]></category>
		<category><![CDATA[How-to]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Server]]></category>
		<guid isPermaLink="false">https://linuxundich.de/en/php2shell-check-docker-containers/</guid>

					<description><![CDATA[The critical PHP vulnerabilities CVE-2026-109618 and CVE-2026-109619 also affect self-hosted services. A small loop shows which Docker containers still ship a vulnerable PHP version.]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Since October 21, PHP2Shell has been all over the news. Behind the catchy name are two vulnerabilities in PHP, CVE-2026-109618 and CVE-2026-109619, which let attackers run code on a web server with a crafted HTTP request, no login required. The PHP developers rate both at 9.2 under CVSS 4.0, and according to <a href="https://watchtowr.com/intelligence/php-cve-2026-109618-faq/">watchTowr</a> the flaw is already being exploited. If you self-host, now is the time to take a look.</p>



<p class="wp-block-paragraph">The holes are plugged in PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35. For PHP 8.1 and older there are no regular releases with the fix so far, so the only way out is moving to a supported branch. On a regular server, the package manager takes care of that. Docker is a different story: every container brings its own PHP, and that PHP stays exactly as old as the image.</p>



<h2 class="wp-block-heading">Which container runs which PHP?</h2>



<p class="wp-block-paragraph">So the first question is: which containers on my little home server actually contain PHP? For Nextcloud or WordPress that&#8217;s obvious, for self-built images or small web apps less so. Instead of going through every container by hand, the following loop asks all running containers for their PHP version and compares it with the patched releases right away.</p>



<pre data-lui-file="php2shell-check.sh" class="wp-block-code"><code>for c in $(docker ps --format '{{.Names}}'); do
  v=$(docker exec &quot;$c&quot; php -r 'echo PHP_VERSION;' 2&gt;/dev/null) || continue
  case &quot;${v%.*}&quot; in
    8.5) fix=8.5.12 ;;
    8.4) fix=8.4.27 ;;
    8.3) fix=8.3.36 ;;
    8.2) fix=8.2.35 ;;
    *)   fix=99 ;;
  esac
  if [ &quot;$(printf '%s\n' &quot;$fix&quot; &quot;$v&quot; | sort -V | head -n1)&quot; = &quot;$fix&quot; ]; then
    printf '\e[32m%-30s %-8s ok\e[0m\n' &quot;$c&quot; &quot;$v&quot;
  else
    printf '\e[31m%-30s %-8s anfällig?\e[0m\n' &quot;$c&quot; &quot;$v&quot;
  fi
done</code></pre>



<p class="wp-block-paragraph">The loop silently skips containers without PHP. For all others, the version shows up in green if it is at least at the patch level and in red if it is below. PHP 8.1, 7.x and older also end up in red, because there simply is no fix for them. The red lines say “anfällig?”, German for “vulnerable?”. On my server, the result looks like this:</p>



<pre class="wp-block-code"><code>$ ./php2shell-simple.sh
nextcloud-app-1                8.5.12   ok
nextcloud-cron-1               8.5.12   ok
linuxundich-test-wordpress-1   8.3.36   ok
planetfoss-web                 8.3.33   anfällig?
sisu-web                       8.4.26   anfällig?</code></pre>



<p class="wp-block-paragraph">So Nextcloud and the test WordPress had already pulled their updates, while PlanetFOSS and the site for Sisu were still lagging behind. By the way, the question mark after “vulnerable” is on purpose. The loop only compares version numbers. If an image uses the PHP package from Debian, Ubuntu or Alpine, the fix may have been backported without the number changing. In that case, only the distribution&#8217;s security advisories can tell you more.</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6ad9e44286491&quot;}" data-wp-interactive="core/image" data-wp-key="6ad9e44286491" class="wp-block-image wp-lightbox-container"><img fetchpriority="high" decoding="async" height="877" width="1397" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://linuxundich.de/wp-content/uploads/2026/10/php2shell-simple.webp" alt="Split terminal window on bender: on the left the script php2shell-simple.sh with line numbers, on the right its output with green and red container names and their PHP versions"/><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button><figcaption class="wp-element-caption">The loop as a small script on the left, its output on the right: planetfoss-web and sisu-web show up in red, the rest in green.</figcaption></figure>



<p class="wp-block-paragraph">For a closer look, there is <a href="https://github.com/EXT-IT/php2shell-check">php2shell-check</a> by EXT IT. The MIT-licensed script is read-only and judges PHP by package versions rather than just the version number, so it also takes backported fixes into account. It looks into Docker containers, FPM pools and old processes that are still running, and on request sends a harmless request to your website. For the containers it needs root or membership in the docker group.</p>



<p class="wp-block-paragraph">The project page suggests the usual one-liner with <code>curl</code> and <code>sudo bash</code>. For a script that runs with root privileges on my server, I&#8217;m not too fond of that. Better download the script together with <code>SHA256SUMS</code> from the releases, check it with <code>sha256sum -c SHA256SUMS</code>, skim through it once and then start it by hand. According to the developers, a “not vulnerable” is no guarantee, by the way:</p>



<pre class="wp-block-code"><code>$ sudo bash php2shell-check.sh https://eure-seite.example</code></pre>



<p class="wp-block-paragraph">On my home server I ran the script with sudo. As a member of the docker group it would see the containers without root as well, but running processes of other users and some logs would stay hidden. This is how I fetched and started it:</p>



<pre class="wp-block-code"><code>$ curl -fsSLO https://github.com/EXT-IT/php2shell-check/releases/latest/download/php2shell-check.sh
$ chmod +x php2shell-check.sh 
$ sudo ./php2shell-check.sh </code></pre>



<p class="wp-block-paragraph">The verdict matches the loop: planetfoss-web with PHP 8.3.33 and sisu-web with PHP 8.4.26 are vulnerable, Nextcloud and the test WordPress are not. On top of that, it found a stopped container from Tuxsucht that my loop had completely missed:</p>



<pre class="wp-block-code"><code>PHP in docker containers
  [ OK ] container      nextcloud-app-1 (nextcloud:35-apache)
         PHP 8.5.12 (fixed from 8.5.12)
[...]
  [VULN] container      planetfoss-web (planetfoss-web:local)
         PHP 8.3.33, fixed in 8.3.36
         -&gt; Update PHP to 8.3.36 or later (container: pull the new image and recreate)
[...]
  [VULN] container      sisu-web (sisu-web:local)
         PHP 8.4.26, fixed in 8.4.27
[...]
  [WARN] container      php83 (stopped, tuxsucht-php)
         stopped, but its image ships PHP 8.3.33: it comes back vulnerable on the next start
[...]
Summary  2 vulnerable, 3 undecided, 12 warnings, 11 ok</code></pre>



<p class="wp-block-paragraph">Sure, <code>docker ps</code> only lists running containers, and a stopped container doesn&#8217;t accept any requests. But if someone starts it again at some point, the hole is back right away. The script is also very clear about mod_php: all five running containers serve PHP through the Apache module, and the advisory demonstrates a complete attack without login against exactly this setup.</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;6ad9e44286d60&quot;}" data-wp-interactive="core/image" data-wp-key="6ad9e44286d60" class="wp-block-image wp-lightbox-container"><img decoding="async" height="877" width="1397" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://linuxundich.de/wp-content/uploads/2026/10/php2shell-check.webp" alt="Terminal window with the output of php2shell-check on bender: color-coded VULN, WARN and OK entries for the containers planetfoss-web, sisu-web and php83"/><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button><figcaption class="wp-element-caption">php2shell-check flags sisu-web as vulnerable, warns about mod_php and also finds the stopped container php83 with an old PHP.</figcaption></figure>



<p class="wp-block-paragraph">Until you can update, the script suggests a stopgap: set <code>session.upload_progress.enabled=0</code> and <code>expose_php=0</code>, and keep <code>post_max_size</code> below a quarter of <code>memory_limit</code>. The check from the outside, on the other hand, wasn&#8217;t much help. For the two websites, the script only reports that the headers don&#8217;t reveal a PHP version and that there is no WAF in front. That says nothing about the patch level, which the script honestly admits.</p>



<h2 class="wp-block-heading">And how do you update PHP?</h2>



<p class="wp-block-paragraph">Don&#8217;t update anything inside the container itself, it would be gone again with the next recreate. Instead, pull the new image in the respective Compose directory and recreate the container with it. For self-built images with their own Dockerfile you need <code>docker compose build --pull</code>, so that Docker also fetches a fresh base image:</p>



<pre class="wp-block-code"><code>docker compose pull &amp;&amp; docker compose up -d</code></pre>



<p class="wp-block-paragraph">Important: of course this only helps if the base image already contains the patched version. According to <a href="https://www.it-connect.tech/php2shell-and-docker-how-to-check-and-update-your-php-containers/">IT-Connect</a>, the new tags initially even showed up without an image behind them, but by now generic tags such as <code>php:8.3-apache</code> ship the patched PHP. If your Dockerfile pins a fixed version like <code>php:8.4.24-fpm</code>, you have to bump it yourself. Simply run the loop again after the update. Take care of the containers that are reachable from the internet first.</p>



<p class="wp-block-paragraph">The PlanetFOSS test environment shows what this looks like in practice. Its image is based on <code>php:8.3-apache</code>, and the Dockerfile only adds extensions such as GD and Intl. My deploy script does call <code>docker compose up -d --build</code>, but that doesn&#8217;t fetch a new base image, it takes the one from August out of the cache. That&#8217;s where the old PHP 8.3.33 came from. Only <code>--pull</code> fetches the fresh version:</p>



<pre class="wp-block-code"><code>$ cd /data/docker/planetfoss
$ docker compose build --pull web
$ docker compose up -d web</code></pre>



<p class="wp-block-paragraph">After a good minute the new image was ready, and the container ran PHP 8.3.36. The database in the same Compose project stays untouched, because both commands only address the web service. Afterwards the loop shows PlanetFOSS in green, and php2shell-check also reports only sisu-web as vulnerable. The old image is still lying around on disk as a leftover, by the way; <code>docker image prune</code> cleans it up.</p>



<pre class="wp-block-code"><code>$ ./php2shell-simple.sh
[...]
nextcloud-cron-1               8.5.12   ok
linuxundich-test-wordpress-1   8.3.36   ok
sisu-web                       8.4.26   anfällig?</code></pre>



<p class="wp-block-paragraph">All in all, PHP2Shell is a good example of how self-hosting with Docker is convenient, but the updates don&#8217;t come by themselves. heise has collected <a href="https://www.heise.de/en/news/php2shell-Critical-flaws-allow-code-injection-in-PHP-11494897.html">more details on the vulnerabilities</a>. How to keep containers up to date without a lot of fiddling, I covered in <a href="https://linuxundich.de/en/keeping-docker-containers-up-to-date-with-dockcheck/">Keeping Docker containers up to date with Dockcheck</a>. How do you keep your containers up to date?</p>
<hr><p>☕ <strong>Support Linux und Ich</strong><br>Thanks for reading <a href="https://linuxundich.de/en/">Linux und Ich</a>. If you enjoy the blog and would like to support my work, you will find more information on the <a href="https://linuxundich.de/en/support/">donation page</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://linuxundich.de/en/php2shell-check-docker-containers/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
