Bitnami WordPress の PHP と Apache をソースビルドで更新する — Lightsail で CVE-2026-109618 の RCE を塞ぐまで

Bitnami WordPress の PHP と Apache をソースビルドで更新する — Lightsail で CVE-2026-109618 の RCE を塞ぐまで AWS

2026 年 10 月 21 日、php.net のトップに「Critical remote code execution vulnerabilities, update now」のバナーが出た。CVE-2026-109618 と CVE-2026-109619、どちらも未認証のリモートリクエストで届く RCE で CVSS 9.2、PHP-FPM 構成も対象。修正版は 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35 で、全部その日に出た。

このブログ(pocraft.net)は AWS Lightsail の Bitnami WordPress インスタンスで動いている。Debian 12、PHP 8.4.18、WordPress 7.0.7。Bitnami の PHP は apt で入れたものではなく /opt/bitnami/php にソースからビルドされたもので、apt upgrade では一生上がらない。この記事は、その PHP を 8.4.27 に、ついでに Apache を 2.4.66 から 2.4.69 に、どちらもソースビルドで in-place に入れ替えた記録である。作業は Claude Code と一緒にやった。途中でヘッダの取り違えでビルドが落ちた話と、「Site Health は緑なのに脆弱」という話が読みどころになると思う。

Site Health は「推奨バージョン」と言っていた

最初に WordPress の Site Health を見た。結果は「サイトは推奨バージョンの PHP (8.4.18) を実行しています」、ステータス good。

これは嘘ではないが、役に立たない。Site Health の PHP 判定は メジャー・マイナー(8.4)しか見ていない。8.4.18 は 2026 年 2 月のビルドで、そのあと 8.4.21(5 月)で 11 件、8.4.23(7 月)で 1 件、8.4.24(7 月)で 4 件、8.4.26(9 月)で 11 件の CVE が直っている。合計 27 件、そこに 10 月の RCE 2 件が乗る。「8.4 系だから推奨」という表示の裏で、パッチレベルは 8 か月分遅れていた。

PHP の更新で「今の問題が解決するバージョン」を選ぶなら、php.net の ChangeLog を読むしかない。読んだ上で 8.4.27 を選んだ。8.5.12 でも塞がるが、WordPress 7.0 系と Cocoon テーマで動作実績があるのは 8.4 系で、8.4 のセキュリティサポートは 2028 年末まで続く。脆弱性対応で系列まで変えるのは、変数を 2 つ同時に動かすことになる。

Bitnami の PHP は apt では更新できない

/opt/bitnami/php/bin/php -i を見ると、Bitnami がどうビルドしたかが残っている。

Configure Command => '/bitnami/blacksmith-sandox/php-8.4.18/configure'
  '--prefix=/opt/bitnami/php' '--enable-fpm' '--with-fpm-user=daemon'
  '--with-mysqli=mysqlnd' '--enable-intl' '--with-sodium' '--enable-gd'
  '--with-config-file-scan-dir=/opt/bitnami/php/etc/conf.d' ...
  'EXTENSION_DIR=/opt/bitnami/php/lib/php/extensions'

Bitnami の公式見解は「新しいバージョンのイメージで新しいインスタンスを立てて移行してください」で、in-place の更新はサポート外だ。ただ、この Configure Command があれば同じ構成のバイナリを作れる。Debian 12 の PHP パッケージ(8.2 系)に切り替える手もあるが、Apache の FPM 連携や php.ini の場所が全部変わるので、ソースビルドで同じ場所に入れ替えるほうが差分が小さい。

もうひとつ確認したのは拡張モジュールの互換性だ。/opt/bitnami/php/lib/php/extensions には imagick.so、memcached.so、opcache.so が入っていて、これらは PECL でビルドされたもの。PHP の拡張 API バージョンは同じマイナー系列(8.4.x)なら変わらない。

$ strings imagick.so | grep -m1 -E "^API[0-9]{8}"
API20240924,NTS
$ php -i | grep "PHP Extension =>"
PHP Extension => 20240924

8.4.27 も API20240924 なので、imagick と memcached は再ビルドせずそのまま読める。これが分かった時点で、作業は「PHP 本体だけ入れ替える」に確定した。

PHP 8.4.27 のビルドと入れ替え

準備

  • /opt/bitnami/php を丸ごと tar で退避(116MB → 52MB)
  • 一時スワップ 2GB を作成。インスタンスはメモリ 945MB、スワップ無しで、make -j2 の途中で OOM になる可能性がある
  • tarball は php.net の downloads ページに載っている sha256 と照合してから展開
  • ビルド用の -dev パッケージ(libxml2-dev、libssl-dev、libicu-dev、libsodium-dev、libzip-dev、libonig-dev など 20 個ほど)を apt で導入

configure と make

Configure Command をそのまま流用した。8.4 では廃止済みのオプション(--with-zlib-dir、--with-libxml-dir、--enable-inline-optimization、--with-png-dir、--with-xmlrpc)が混ざっていて警告が出るが、無視されるだけで害はない。Bitnami 独自の --enable-apcu=shared(ext/ に同梱してビルドしていたらしい)と、使っていない --with-pdo-pgsql / --with-pdo-dblib は外した。

./configure --prefix=/opt/bitnami/php --enable-fpm --with-fpm-user=daemon \
  --with-fpm-group=daemon --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd \
  --enable-intl --with-sodium --enable-gd --with-jpeg --with-webp --with-freetype \
  --with-zip --with-curl --with-openssl --enable-mbstring --enable-soap \
  --with-config-file-scan-dir=/opt/bitnami/php/etc/conf.d \
  EXTENSION_DIR=/opt/bitnami/php/lib/php/extensions   # ほか Bitnami と同じ
make -j2                                   # 2 vCPU で約 15 分
make install INSTALL_ROOT=/home/bitnami/build/stage

make install を直接 /opt/bitnami/php に打たず、INSTALL_ROOT で一時ディレクトリに入れたのは、本番に触る前に検証したかったからだ。ステージ側の php を本番の php.ini で起動して、読み込まれる拡張を数える。

$ stage/opt/bitnami/php/bin/php -c /opt/bitnami/php/etc/php.ini -m | wc -l
59
$ /opt/bitnami/php/bin/php -m | wc -l
59

同じ 59。imagick も memcached も OPcache も、新しいバイナリから旧 .so を読めている。

入れ替え

php-fpm を止めて、ステージの bin / sbin / include / lib / php(man ページ)を cp -a で上書きし、起動する。etc/(php.ini、php-fpm.conf)には触らない。停止時間は数秒で、その間 Apache は 503 を返す。

入れ替え後の確認で一つ気をつけたのは、php -v は CLI の話でしかないということだ。FPM が本当に新しいバイナリで動いているかは、Web 経由で確かめないと分からない。DocumentRoot に乱数名のプローブを 1 枚置き、curl で叩いて、すぐ消した。

$ curl -s https://pocraft.net/zz-phpprobe-7f3a.php
8.4.27 sapi=fpm-fcgi opcache=on imagick=yes memcached=yes

最後に php.net の告知にある緩和策のうち session.upload_progress.enabled = Off を php.ini に足した。10 月 23 日の追加告知(CVE-2026-115560)が「次の bugfix リリースまでこれを切っておけ」と言っていて、WordPress はこの機能を使わない。

WordPress 側も上げる

PHP が終わったら、保留になっていた WordPress 本体 7.0.7 → 7.1.3、プラグイン 6 本、Cocoon 親テーマ 2.9.4.2 → 2.9.6 も上げた。Contact Form 7 の最新版は WordPress 7.1 以上を要求していて「更新不可」になっていたので、本体を先に上げる順番になる。

バックアップは、以前から cron で回している日次バックアップスクリプトをそのまま手で叩いた。このスクリプトには過去の試行錯誤がコメントで残っている。mariadb-dump は /home 配下に直接書けない(Errcode 13)ので /tmp を経由すること、wp-content は /bitnami/wordpress/wp-content へのシンボリックリンクで tar -C でリンク先を指定しないとリンク 1 本だけの空アーカイブになること。どちらも一度踏んだ穴で、今回は踏まずに済んだ。

更新後の Apache のエラーログに、WP Mail SMTP の Fatal が 1 件だけ残っていた。

PHP Fatal error: Uncaught Error: Call to undefined method
  WPMailSMTP\WP::is_network_admin_scope() in .../PartnerPlugins/PartnerPlugin.php:115

タイムスタンプは 03:05:33、クライアントは 127.0.0.1。wp plugin update --all が 6 本を順に更新している最中で、WP Mail SMTP の新しいファイルと古いファイルが混在した一瞬に、WP-Cron のループバックリクエストが刺さったものだ。更新完了後に wp plugin verify-checksums が通り、該当メソッドも存在し、wp-cron.php と admin-ajax.php のループバックも 200 を返す。更新中の一過性の Fatal で、放置してよい。

「Apache 側にも脆弱性あったら out じゃね?」

PHP の報告をしたら、こう言われた。もっともである。調べた。

Apache は 2.4.66、2025 年 12 月の Bitnami ビルド。最新は 2.4.69(2026 年 10 月 1 日)で、間に 2.4.67 で 11 件、2.4.68 で 13 件、2.4.69 で 20 件、合計 44 件の CVE が直っている。いちばん重いのは CVE-2026-23918(important)、HTTP/2 処理の double free で RCE の可能性があり、影響範囲は「2.4.66 のみ」。まさに動いているバージョンが名指しされている。

ただ、今すぐ刺さる経路は無かった。mod_http2 はロードされているが、httpd.conf に Protocols 指示が無い。Apache の既定は http/1.1 のみで、モジュールを読み込んだだけでは HTTP/2 は配信されない。curl で h2 を要求しても HTTP/1.1 で返ってくることを確認した。残りの moderate 以上は mod_dav、mod_vhost_alias、mod_proxy_html、mod_auth_digest、.htaccess の式評価が対象で、いずれも未ロードか AllowOverride None で無効。

つまり「穴はあるが扉が閉まっている」。脆弱性の評価は「そのモジュールが存在するか」ではなく「そのコードパスに外から届くか」で見る。とはいえ設定 1 行で開く扉を 10 か月遅れのまま置く理由も無いので、上げることにした。

Apache 2.4.69 のビルドで一度落ちた

Apache も Bitnami はソースビルドで、configure の記録が /opt/bitnami/apache/build/config.nice に残っている。

CPPFLAGS="-I/opt/bitnami/common/include -I/opt/bitnami/apache/include"; export CPPFLAGS
LDFLAGS="-Wl,-rpath=/opt/bitnami/apache/lib -L/opt/bitnami/apache/lib"; export LDFLAGS
"/bitnami/blacksmith-sandox/apache-2.4.66/configure" \
  "--prefix=/opt/bitnami/apache" "--enable-mods-shared=most" "--enable-http2" \
  "--enable-ssl" "--enable-proxy" "--enable-mpms-shared=all" \
  "--with-apr=/opt/bitnami/apache" "--with-apr-util=/opt/bitnami/apache" ...

apr と apr-util は /opt/bitnami/apache に同梱されていて、OpenSSL・nghttp2・pcre2・libxml2 はシステムのものにリンクしている(ldd で確認)。なので Debian の -dev パッケージを足せばビルドできるはずだった。configure は通り、make が落ちた。

log.c: In function 'log_ctime':
log.c:668:23: error: 'AP_CTIME_OPTION_MSEC' undeclared (first use in this function);
  did you mean 'AP_CTIME_OPTION_USEC'?

AP_CTIME_OPTION_MSEC は httpd 自身のヘッダ util_time.h にある定数で、2.4.69 のソースツリーには当然ある。無いと言われるのはおかしい。コンパイルの行を見ると、-I/opt/bitnami/common/include -I/opt/bitnami/apache/include が、ソースツリーの -I.../httpd-2.4.69/include より前に並んでいる。/opt/bitnami/apache/include には今動いている 2.4.66 のヘッダが入っている。新しい log.c が古い util_time.h を読んで、新しい定数が見つからなかった。

Bitnami がこのフラグを付けたのは apr のヘッダを拾うためだろうが、apr は --with-apr=/opt/bitnami/apache で見つかった apr-1-config が -I/opt/bitnami/apache/include/apr-1 を別に足してくれる。つまり -I/opt/bitnami/apache/include は、初回ビルド(そこにまだ何も無い)では無害で、in-place の再ビルドでだけ牙をむく。Bitnami 自身は毎回クリーンなサンドボックスでビルドしているので、この罠を踏むことがない。

CPPFLAGS からそのフラグを外して make distclean からやり直したら 3 分で通った。あとは PHP と同じで、DESTDIR でステージに入れ、ステージの httpd に本番の httpd.conf を食わせて -t(Syntax OK)、Apache を止めて bin / modules / include / build / error / icons を上書きし、起動。conf/ と htdocs/ には触らない。envvars は新旧で diff が無かった。停止から起動まで 1 秒未満。

[mpm_event:notice] AH00489: Apache/2.4.69 (Unix) OpenSSL/3.0.22 configured -- resuming normal operations

TLS 1.3、HTTP → HTTPS の 302、gzip(376KB → 40KB)、PHP-FPM 連携、ロード済みモジュール 41 個(更新前と同数)、すべて更新前と同じに動いている。

学んだこと

Site Health の緑は「系列が新しい」の意味でしかない。 パッチレベルの遅れは自分で ChangeLog を読まないと分からない。PHP のセキュリティリリースは月次で来るので、Bitnami のように apt の外にいる環境では、誰かが見に行かない限り永遠に遅れる。

Bitnami は「入れ替えて使う」思想で、in-place 更新は想定していない。 でも Configure Command と config.nice が残っているので、同じ構成を再現するのは難しくない。罠は、動いている旧版のヘッダがインクルードパスに混ざること。これは Bitnami のサンドボックスでは起きず、in-place でだけ起きる。

脆弱性は「モジュールの有無」ではなく「経路の有無」で評価する。 mod_http2 が居ても Protocols h2 が無ければ届かない。ただしそれは「今日は大丈夫」であって「上げなくていい」ではない。

FPM の確認は Web 経由で。 php -v は CLI の答えで、FPM が別のバイナリを掴んだままでも気づけない。プローブを 1 枚置いて叩いて消す、で 10 秒で済む。

残っているのは MariaDB 12.2.2(2026 年 2 月、短期保守系列)で、127.0.0.1 のみ待受なので優先度を下げて見送った。php-fpm の pm.max_children=5 に日に数回到達する WARNING も、性能の話として別に見る。

手順まとめ

PHP(Bitnami、8.4.x 内のパッチ更新):

# 0. 退避・一時スワップ・tarball 検証
sudo tar -C /opt/bitnami -czf ~/backups/php-$(date +%F).tar.gz php
sudo fallocate -l 2G /swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile
curl -fsSLO https://www.php.net/distributions/php-8.4.27.tar.xz   # sha256 を downloads ページと照合
# 1. php -i の Configure Command を流用して configure / make / ステージ install
./configure --prefix=/opt/bitnami/php ... EXTENSION_DIR=/opt/bitnami/php/lib/php/extensions
make -j2 && make install INSTALL_ROOT=$PWD/stage
# 2. ステージ版で拡張の数が揃うことを確認
stage/opt/bitnami/php/bin/php -c /opt/bitnami/php/etc/php.ini -m | wc -l
# 3. 入れ替え(etc/ は触らない)
sudo /opt/bitnami/ctlscript.sh stop php-fpm
for d in bin sbin include lib php; do sudo cp -a stage/opt/bitnami/php/$d/. /opt/bitnami/php/$d/; done
sudo /opt/bitnami/php/sbin/php-fpm -t && sudo /opt/bitnami/ctlscript.sh start php-fpm
# 4. Web 経由で FPM の版を確認してから後片付け
sudo swapoff /swapfile && sudo rm /swapfile

Apache(Bitnami、2.4.x 内のパッチ更新):

sudo tar -C /opt/bitnami -czf ~/backups/apache-$(date +%F).tar.gz apache
curl -fsSLO https://downloads.apache.org/httpd/httpd-2.4.69.tar.gz   # .sha256 と照合
# config.nice のオプションを流用。CPPFLAGS から -I/opt/bitnami/apache/include は外す
CPPFLAGS="-I/opt/bitnami/common/include" LDFLAGS="-Wl,-rpath=/opt/bitnami/apache/lib -L/opt/bitnami/apache/lib" \
  ./configure --prefix=/opt/bitnami/apache --with-apr=/opt/bitnami/apache --with-apr-util=/opt/bitnami/apache ...
make -j2 && make install DESTDIR=$PWD/stage
sudo stage/opt/bitnami/apache/bin/httpd -f /opt/bitnami/apache/conf/httpd.conf -t
sudo /opt/bitnami/ctlscript.sh stop apache
for d in bin modules include build error icons; do sudo cp -a stage/opt/bitnami/apache/$d/. /opt/bitnami/apache/$d/; done
sudo /opt/bitnami/ctlscript.sh start apache

参考: PHP 公式の 2026-10-21 告知、PHP 8 ChangeLog、Apache httpd 2.4 の脆弱性一覧

コメント

タイトルとURLをコピーしました