CRITICAL INFRA
Loading critical CVEs…
ALL EXPLOITED
Loading…

Fallstudie: PHP 7.4 auf Debian mit dem Sury-Repo patchen

Ein Kunde kam zu uns, nachdem wir eine Web-Shell gefunden hatten: PHP 7.4 auf Debian, ohne Sicherheitspatches. Wir konnten nicht ohne Test direkt auf 8.x springen, aber das RCE musste JETZT geschlossen werden. Die Loesung: das Sury-Repo haelt gepatchte Builds derselben 7.4-Serie — so haben wir die Sicherheitsfixes angewendet, ohne die Version zu aendern und ohne die App zu brechen. Hier ist das genaue Skript.

Der Kontext: ungepatchtes PHP 7.4, RCE-exponiert

Debian 11 (bullseye) liefert PHP 7.4, das aus den offiziellen Paketen keine Sicherheitspatches mehr erhaelt. Jede neue RCE-Schwachstelle (Remote Code Execution) bleibt offen — genau das ist passiert: der Angreifer lud eine PHP-Web-Shell hoch und hatte Zugriff auf den Server.

Kurz gesagt: ein ungepatchtes PHP ist eine offene Tuer; aber man kann die Version auf einem Produktionsserver nicht ueber Nacht aendern — man bricht die App.

Die Loesung, die die App NICHT bricht: Surys Patch derselben Serie

Das ist der Schluessel: das Sury-Repo (packages.sury.org, von einem Debian-Entwickler gepflegt) bietet gepatchte Builds derselben Serie. Sie gehen von 7.4.33 (Debian, ungepatcht) auf 7.4.33-xx+sury (dieselbe 7.4-Serie, aber mit den Sicherheitsfixes). Die Version bleibt 7.4 — die App merkt keinen Unterschied, aber die Sicherheitsluecke schliesst sich. Der Umstieg auf 8.x ist ein separater Schritt, wenn Sie Zeit zum Testen haben.

Kurz gesagt: Sie bekommen die Sicherheitspatches auf PHP 7.4 OHNE Versionswechsel — die App laeuft genau gleich, ist aber nicht mehr verwundbar.

Das Skript, das wir verwendet haben

Das Skript unten fuegt das Sury-Repo hinzu und upgradet jedes PHP-Paket auf den gepatchten Build derselben Serie. Es ist idempotent (beliebig oft ausfuehrbar), erkennt automatisch den Debian-Codename und einen etwaigen apt-Proxy und upgradet nur Pakete mit Sury-Kandidat (ueberspringt Waisen). Es macht einen Probelauf und fragt vor dem Anwenden nach Bestaetigung.

#!/bin/bash
# Add the sury.org repo + upgrade PHP to the PATCHED build of the SAME series
# (e.g. 7.4.33 Debian -> 7.4.33-xx+sury). Idempotent. Run as root.
# Works on Debian bullseye/bookworm/trixie (codename auto-detected).
set -e
export DEBIAN_FRONTEND=noninteractive
. /etc/os-release
CODENAME=${VERSION_CODENAME:-bullseye}
echo "=== $(hostname) / Debian $CODENAME ==="

# detect apt proxy (some boxes only have egress via a proxy)
PROXY=$(grep -rhoiE 'Proxy[[:space:]]+"https?://[^"]+"' /etc/apt/apt.conf.d/ /etc/apt/apt.conf 2>/dev/null | grep -oE 'https?://[^"]+' | head -1)
if [ -n "$PROXY" ]; then
  echo "apt proxy detected: $PROXY (used for key + https)"
  export http_proxy="$PROXY" https_proxy="$PROXY"
  echo "Acquire::https::Proxy \"${PROXY}\";" > /etc/apt/apt.conf.d/00sury-proxy
fi
apt-get install -y -q apt-transport-https ca-certificates curl gnupg >/dev/null 2>&1 || true
curl -fsSL ${PROXY:+--proxy "$PROXY"} https://packages.sury.org/php/apt.gpg -o /usr/share/keyrings/deb.sury.org-php.gpg
echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ ${CODENAME} main" > /etc/apt/sources.list.d/php-sury.list
echo "--- apt update ---"
apt-get update 2>&1 | grep -iE "sury|^Err|Failed" || true

# installed PHP packages
PKGS=$(dpkg-query -W -f='${Package}\n' 2>/dev/null | grep -E '^php[0-9]+\.[0-9]+(-|$)|^libapache2-mod-php[0-9]' | tr '\n' ' ')
echo "--- installed PHP packages: ${PKGS} ---"
echo "BEFORE: $(php -v 2>/dev/null | head -1)"

# keep only packages that have a sury candidate (skip orphans without one, e.g. php5)
UPG=""
for p in ${PKGS}; do
  cand=$(apt-cache policy "$p" 2>/dev/null | awk '/Candidate:/{print $2}')
  inst=$(apt-cache policy "$p" 2>/dev/null | awk '/Installed:/{print $2}')
  if [ -n "$cand" ] && [ "$cand" != "(none)" ] && [ "$cand" != "$inst" ]; then UPG="$UPG $p"; fi
done
echo "--- to upgrade (sury candidate): ${UPG:-NONE} ---"

if [ -z "$UPG" ]; then
  echo "nothing to upgrade (already current or no candidate)"
else
  echo "--- dry run ---"
  apt-get install --only-upgrade -s ${UPG} 2>&1 | grep -E "upgraded,|^Remv" | tail -3
  read -p "Enter = continue, Ctrl+C = abort: " x
  echo "--- upgrade (confold) ---"
  apt-get install --only-upgrade -y \
    -o Dpkg::Options::=--force-confold -o Dpkg::Options::=--force-confdef ${UPG} 2>&1 | tail -4
fi

systemctl reload apache2 2>/dev/null || true
for f in $(systemctl list-units --type=service 2>/dev/null | grep -oE 'php[0-9.]+-fpm'); do
  systemctl reload "$f" 2>/dev/null || true
done

echo "AFTER:  $(php -v 2>/dev/null | head -1)"
echo "--- build source (should show +sury / +deb...~1.gbp) ---"
dpkg-query -W -f='${Package} ${Version}\n' $(echo "$PKGS" | awk '{print $1}') 2>/dev/null
echo "apache: $(systemctl is-active apache2 2>/dev/null)"
echo SURY_DONE

Am Ende pruefen Sie, dass die Version den Suffix +sury zeigt — der Beweis, dass Sie den gepatchten Build fahren, nicht den alten aus Debian.

Optional, spaeter: Migration auf PHP 8.x

Der Patch oben schliesst die Sicherheitsluecke sofort. Der Umstieg auf eine neue Serie (8.2/8.3) ist ein separater, optionaler Schritt, in Ruhe nach dem Testen der App — ebenfalls aus dem Sury-Repo:

# OPTIONAL — erst nachdem Sie die App auf der neuen Serie getestet haben
apt -y install php8.2-fpm php8.2-cli php8.2-mysql php8.2-curl php8.2-mbstring php8.2-xml php8.2-zip
update-alternatives --set php /usr/bin/php8.2
systemctl enable --now php8.2-fpm
# Pool auf 8.2 umstellen, testen und erst dann die alte Serie entfernen:
apt -y purge 'php7.4*' && apt -y autoremove

Automatisches Patching, damit es nicht wieder passiert

Das Grundproblem war, dass niemand Updates einspielte. Wir richten unattended-upgrades ein, um Sicherheitsfixes (inkl. Sury) automatisch zu installieren, und needrestart, um betroffene Dienste neu zu starten.

apt -y install unattended-upgrades needrestart
dpkg-reconfigure -plow unattended-upgrades   # aktivieren
# Vorschau, ohne anzuwenden:
unattended-upgrade --dry-run -d 2>&1 | tail

Kurz gesagt: ab jetzt holt sich der Server seine Sicherheitspatches jede Nacht selbst; Sie bekommen nur den Bericht.

Ergebnis

Der Server laeuft jetzt mit gepatchtem PHP 7.4 (Sury-Build), die Web-Shell und Zugangswege wurden entfernt, und Sicherheitspatches werden automatisch eingespielt — ohne Versionswechsel und ohne die App zu riskieren. Der Umstieg auf 8.x bleibt ein optionaler Schritt, in Ruhe gemacht. Wir haben den Server auch unter Monitoring gestellt, um jede neue Datei im Web-Root sofort zu erkennen.

Lassen Sie uns über Ihr Projekt sprechen →