#!/bin/sh # check-patched.sh: is a PHP install protected against CVE-2026-109618 / CVE-2026-109619? # # Usage: # check-patched.sh check a Docker image (e.g. php:7.4-apache) # check-patched.sh --container check a running container # check-patched.sh --local check the PHP on this machine # # Exit codes: 0 = patched, 1 = vulnerable, 2 = could not check # # How it works: # - PHP 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12 / 8.6.0 and newer are official fixed releases. # - Older versions (7.4, 8.0, 8.1, unfixed 8.2+) still report the same version number after a # backport, so the script looks for "zend_new_array_singleton", a function added by the # backport, inside every PHP binary it finds (php, php-fpm, mod_php). # This is a heuristic. A vendor backport that is written differently may be reported as # vulnerable; ask your vendor in that case. # # https://github.com/softjapan/php74-cve-patched set -eu MARKER=zend_new_array_singleton # This part runs where PHP is installed (inside the container, or on this machine). INNER=' set -u MARKER="$1" PHP=$(command -v php 2>/dev/null || true) [ -n "$PHP" ] || { echo "ERROR: php not found"; exit 2; } VER=$("$PHP" -r "echo PHP_VERSION;" 2>/dev/null) || { echo "ERROR: could not run php"; exit 2; } echo "PHP version: $VER" # Official fixed releases (php.net advisory 2026-10-21) FIXED=$("$PHP" -r " \$v = PHP_VERSION; \$b = PHP_MAJOR_VERSION . \".\" . PHP_MINOR_VERSION; \$fixed = [\"8.2\" => \"8.2.35\", \"8.3\" => \"8.3.36\", \"8.4\" => \"8.4.27\", \"8.5\" => \"8.5.12\"]; if (version_compare(\$b, \"8.6\", \">=\")) { echo 1; } elseif (isset(\$fixed[\$b]) && version_compare(\$v, \$fixed[\$b], \">=\")) { echo 1; } else { echo 0; } ") if [ "$FIXED" = 1 ]; then echo "RESULT: PATCHED (official fixed release)" exit 0 fi # Binaries to inspect: CLI, FPM and Apache mod_php, wherever they live BINS="$PHP" for f in $(command -v php-fpm 2>/dev/null) /usr/local/sbin/php-fpm /usr/sbin/php-fpm* \ /usr/lib/apache2/modules/libphp*.so /usr/lib*/httpd/modules/libphp*.so \ /usr/local/lib/apache2/modules/libphp*.so /usr/lib/php*/sbin/php-fpm*; do [ -f "$f" ] && BINS="$BINS $f" done STATUS=0 for b in $(echo $BINS | tr " " "\n" | sort -u); do if grep -q "$MARKER" "$b" 2>/dev/null; then echo " patched $b" else echo " VULNERABLE $b" STATUS=1 fi done if [ "$STATUS" = 0 ]; then echo "RESULT: PATCHED (backport found in every PHP binary)" else echo "RESULT: VULNERABLE to CVE-2026-109618 / CVE-2026-109619" echo "See https://www.php.net/archive/2026.php#2026-10-21-5" fi exit $STATUS ' usage() { sed -n '2,10p' "$0" | sed 's/^# \{0,1\}//'; exit 2; } [ $# -ge 1 ] || usage case "$1" in -h|--help) usage ;; --local) sh -c "$INNER" sh "$MARKER" ;; --container) [ $# -ge 2 ] || usage docker exec "$2" sh -c "$INNER" sh "$MARKER" ;; *) echo "Image: $1" docker run --rm --entrypoint sh "$1" -c "$INNER" sh "$MARKER" ;; esac