PHP2Shell on Lightsail: how we patched a Bitnami WordPress site without breaking it

On October 21, PHP shipped fixes for two critical flaws, CVE-2026-109618 and CVE-2026-109619, now known as PHP2Shell. Both let an attacker run code on your server without logging in. WordPress itself doesn’t need an update; the PHP underneath it does.

If your WordPress site runs on AWS Lightsail from the Bitnami blueprint, there’s a catch: apt upgrade won’t fix it. Bitnami ships its own copy of PHP under /opt/bitnami/php, and nothing updates it for you.

We run this exact setup for our own site, so we patched it on October 23. This is the whole process we followed: how to tell if you’re exposed, how to check nobody got in first, the fix itself, and how to prove it worked. Along the way we note the few things AWS’s instructions leave for you to work out.

Are you affected?

Connect to your instance and run:

/opt/bitnami/php/bin/php -v

The fixed versions are 8.2.35, 8.3.36, 8.4.27 and 8.5.12. Anything older on the same line is vulnerable, as is any PHP 8.1 or earlier. Ours said PHP 8.4.18, so we were exposed.

Updating WordPress, plugins or themes won’t change this number. Only the PHP runtime matters here.

Before you patch: check nobody got in first

Amazon’s threat researchers saw exploit attempts from October 17, days before the fix was public. So before patching, look for signs that someone already got in. Patching removes the hole; it doesn’t remove someone who’s already inside.

These are the checks we ran, all read-only:

Crashes and out-of-memory errors. Both flaws involve PHP’s memory handling, so failed attempts tend to leave crashes behind:

sudo zgrep -hE 'SIGSEGV|SIGBUS|exited on signal|core dumped|Allowed memory size' \
  /opt/bitnami/php/logs/php-fpm.log* /opt/bitnami/apache/logs/error_log*

WordPress files and users. On Bitnami, run WP-CLI through Bitnami’s own wrapper, with sudo and the full path. It switches to the web server’s user for you. A plain wp on the path may run as root, which you don’t want on a site that might be compromised.

W="sudo /opt/bitnami/wp-cli/bin/wp --path=/opt/bitnami/wordpress --skip-plugins --skip-themes"
$W core verify-checksums
$W plugin verify-checksums --all
$W user list --role=administrator
sudo find /opt/bitnami/wordpress/wp-content/uploads -iname '*.ph*'
sudo find /opt/bitnami/wordpress -name '*.php' -newermt 2026-10-17 | grep -v /cache/

Anything new that runs on its own. Look at cron jobs, processes running as the web user, and outbound connections:

sudo ls -la /etc/cron.d/; sudo crontab -u daemon -l
ps -u daemon -o pid,lstart,cmd | grep -vE 'httpd|php-fpm'
sudo ss -tnp state established

What we found: nothing worrying.

  • WordPress core matched its checksums.
  • Every plugin from wordpress.org matched too. Our two custom plugins have no public checksums to compare against.
  • No new admins, and no PHP files hiding in uploads.

One thing did look alarming at first: a burst of 60 POST requests on October 17, the day attacks began. But they came after the bot had listed our pages, they arrived exactly a second apart, and every response was a normal page of the same size. That’s a contact-form spam bot.

One honest limit: Apache doesn’t log request bodies by default. “No signs” means no signs; it can’t prove nothing happened. If you find real signs of compromise, stop. Don’t patch over the evidence; treat it as an incident.

Take a snapshot and a baseline

Two things before touching anything:

aws lightsail create-instance-snapshot --instance-name '<your-instance>' \
  --instance-snapshot-name "pre-php2shell-$(date +%Y%m%d-%H%M%S)"

Wait until the snapshot shows as available. Then record what your site looks like right now: the status code and size of every published page. That’s how you’ll know afterwards that nothing changed by accident.

sudo /opt/bitnami/wp-cli/bin/wp --path=/opt/bitnami/wordpress post list \
  --post_type=page,post --post_status=publish --field=url > urls.txt
while read u; do curl -s -o /dev/null -w "%{http_code} %{size_download} $u\n" "$u"; done < urls.txt > before.txt

Run that last loop from your own computer, not the server, so you test the site the way visitors reach it.

The fix: AWS’s in-place route, plus the gaps

AWS’s Lightsail guidance for these CVEs gives two options for Bitnami instances:

  • move to one of Lightsail’s own blueprints;
  • install package-managed PHP alongside Bitnami’s and point Bitnami’s Apache at it.

Moving is the cleaner long-term answer, but it’s a migration. We took the in-place route. From then on, PHP security fixes arrive with an ordinary apt upgrade.

The core of it, from AWS’s steps, is:

  1. add the Sury PHP repository (a widely used third-party Debian repository, not run by AWS);
  2. install the same minor version you already run (8.4 for us);
  3. create a PHP-FPM pool that runs as Bitnami’s daemon user on a new socket;
  4. repoint Apache from Bitnami’s socket to the new one.

That part works as written. These are the parts that need care.

1. Your PHP settings don’t come with you

Bitnami tunes its php.ini. The new PHP starts from Debian’s defaults instead:

SettingBitnami (ours)New PHP, out of the box
memory_limit512M128M
post_max_size80M8M
upload_max_filesize80M2M

Nothing breaks right away. Then a few days later someone tries to upload a 5 MB image and it fails. So we copied our Bitnami values into a drop-in file that the new PHP reads:

; /etc/php/8.4/fpm/conf.d/99-bitnami-parity.ini
memory_limit = 512M
post_max_size = 80M
upload_max_filesize = 80M
date.timezone = UTC
expose_php = Off
opcache.memory_consumption = 192
pdo_mysql.default_socket = /opt/bitnami/mariadb/tmp/mysql.sock
mysqli.default_socket = /opt/bitnami/mariadb/tmp/mysql.sock
; WordPress doesn't use this; turning it off blocks one CVE-2026-109619 path
session.upload_progress.enabled = 0

Use the values from your /opt/bitnami/php/lib/php.ini, not ours.

2. Compare extensions before you switch

AWS’s guide says to add any extra extensions your site needs. The reliable way to find them is to compare the module lists of the old and new PHP:

comm -23 <(sudo /opt/bitnami/php/sbin/php-fpm -m | sort -fu) \
         <(sudo /usr/sbin/php-fpm8.4 -m | sort -fu)

Ours showed imagick and memcached (installed with apt install php8.4-imagick php8.4-memcached) and pcntl, which is command-line only and safe to ignore. Run it again after installing; the only line left should be pcntl.

3. Keep a rollback you can trust

Before editing Apache’s config, take a copy with a timestamp in the name. Then a second run can’t overwrite your original backup with an already-edited file. On our box the PHP handler lives in /opt/bitnami/apache/conf/bitnami/php-fpm.conf:

TS=$(date +%Y%m%d-%H%M%S)
F=/opt/bitnami/apache/conf/bitnami/php-fpm.conf
sudo cp -p "$F" "$F.bak-$TS"
sudo sed -i 's|/opt/bitnami/php/var/run/www.sock|/opt/bitnami/php/var/run/ww2.sock|' "$F"
sudo /opt/bitnami/apache/bin/apachectl -t && sudo /opt/bitnami/ctlscript.sh restart apache

Leave Bitnami’s old PHP-FPM running for now. Nothing sends it traffic any more, but if anything looks wrong, copying the .bak back and restarting Apache puts you back where you started in seconds.

Prove it worked

Checking php -v only tells you about the command-line PHP. To see what Apache actually uses, drop in a temporary probe, request it, and remove it:

echo '<?php echo PHP_VERSION," ",PHP_SAPI," ",php_ini_loaded_file()," ",ini_get("memory_limit");' \
  | sudo tee /opt/bitnami/wordpress/v.php >/dev/null
curl -sk https://127.0.0.1/v.php; echo
sudo mv /opt/bitnami/wordpress/v.php /tmp/

We got 8.4.27 fpm-fcgi /etc/php/8.4/fpm/php.ini 512M. That’s the fixed version, served through PHP-FPM, reading the new config, with our memory limit intact. If you see /opt/bitnami/php/... in that output, Apache is still using the old PHP.

Then re-run your baseline loop into after.txt and compare it with before.txt. All 25 of our published pages, plus the login page, REST API and feed, came back with the same status codes and essentially the same sizes. The Apache error log showed nothing new after the restart.

After the patch

  • Reboot test. A change like this is only finished once it survives a reboot: the new PHP-FPM has to start on its own, before Apache needs it. We rebooted, then re-ran the probe and the page comparison.
  • Retire the old PHP. Once everything had held, we stopped Bitnami’s PHP-FPM for good, so the vulnerable copy isn’t left running: sudo systemctl stop bitnami.php-fpm && sudo systemctl disable bitnami.php-fpm. You can’t mask it, because Bitnami keeps the unit file in /etc/systemd/system. Disabling is enough, because Bitnami’s ctlscript.sh only starts enabled services. Undoing it is enable and start.
  • WP-CLI is a known leftover. Bitnami’s WP-CLI wrapper still uses Bitnami’s own command-line PHP. That isn’t reachable from the web, so it isn’t exposed to these flaws, but it’s worth knowing.
  • Future fixes are routine now. sudo apt update && sudo apt upgrade brings in PHP security releases. Restart php8.4-fpm afterwards.

The short version

  1. Check /opt/bitnami/php/bin/php -v against 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12.
  2. Look for signs someone got in first: crashes, checksums, admins, uploads, cron, connections. If you find any, stop and treat it as an incident.
  3. Snapshot the instance and record every page’s status code and size.
  4. Install the same PHP minor version from Sury, as AWS describes, and bring your php.ini values and extensions with you.
  5. Back up the Apache config with a timestamp, repoint it, and keep the old PHP-FPM running for rollback.
  6. Prove it: run a probe through Apache, compare your pages against the baseline, and check the error log.
  7. Reboot, check again, then retire Bitnami’s PHP-FPM.

This took us an afternoon, and most of that was checking rather than changing. If you’d rather not spend yours on it, or you have several sites in the same spot, we do this kind of work. We’re happy to look at yours.

Similar Posts