Un client a venit dupa ce i-am gasit un web-shell: PHP 7.4 pe Debian, fara patch-uri de securitate. Nu puteam sari direct pe 8.x fara sa testam aplicatia, dar RCE-ul trebuia inchis ACUM. Solutia: repo-ul Sury pastreaza build-uri patch-uite ale aceleiasi serii 7.4 — asa am aplicat corectiile de securitate fara sa schimbam versiunea si fara sa stricam aplicatia. Iata scriptul exact.
Contextul: PHP 7.4 fara patch-uri, expus la RCE
Debian 11 (bullseye) vine cu PHP 7.4, care nu mai primeste patch-uri de securitate din pachetele oficiale. Orice vulnerabilitate noua de tip RCE (executie de cod la distanta) ramane deschisa — exact ce s-a intamplat: atacatorul a urcat un web-shell PHP si avea acces la server.
Pe scurt: un PHP fara patch-uri e o usa deschisa; dar nu poti schimba peste noapte versiunea pe un server de productie — rupi aplicatia.
Solutia care NU strica aplicatia: patch-ul Sury pe aceeasi serie
Aici e cheia: repo-ul Sury (packages.sury.org, intretinut de un developer Debian) ofera build-uri patch-uite ale aceleiasi serii. Adica treci de la 7.4.33 (Debian, nepatch-uit) la 7.4.33-xx+sury (aceeasi serie 7.4, dar cu corectiile de securitate). Versiunea ramane 7.4 — aplicatia nu simte nicio diferenta, dar gaura de securitate se inchide. Migrarea pe 8.x o faci separat, cand ai timp sa testezi.
Pe scurt: primesti patch-urile de securitate pe PHP 7.4 FARA sa schimbi versiunea — aplicatia merge exact la fel, dar nu mai e vulnerabila.
Scriptul pe care l-am folosit
Scriptul de mai jos adauga repo-ul Sury si upgradeaza fiecare pachet PHP la build-ul patch-uit al aceleiasi serii. E idempotent (il poti rula de cate ori vrei), detecteaza singur codename-ul Debian si eventualul proxy apt, si upgradeaza doar pachetele care au un candidat Sury (sare peste orfane). Face o simulare si cere confirmare inainte sa aplice.
#!/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_DONELa final verifici ca versiunea arata sufixul +sury — dovada ca rulezi build-ul patch-uit, nu cel vechi din Debian.
Optional, mai tarziu: migrare la PHP 8.x
Patch-ul de mai sus inchide problema de securitate imediat. Trecerea pe o serie noua (8.2/8.3) e un pas separat, optional, facut cu calm dupa ce testezi aplicatia — tot din repo-ul Sury:
# OPTIONAL — abia dupa ce testezi aplicatia pe noua serie
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
# comuti pool-ul pe 8.2, testezi, si abia apoi scoti seria veche:
apt -y purge 'php7.4*' && apt -y autoremove
Patch-uri automate, ca sa nu se mai repete
Problema de fond era ca nimeni nu aplica update-urile. Punem unattended-upgrades sa instaleze automat corectiile de securitate (inclusiv cele Sury) si needrestart sa reporneasca serviciile afectate.
apt -y install unattended-upgrades needrestart
dpkg-reconfigure -plow unattended-upgrades # enable
# preview what it would install, without applying:
unattended-upgrade --dry-run -d 2>&1 | tailPe scurt: de acum serverul isi ia singur patch-urile de securitate in fiecare noapte; tu primesti doar raportul.
Rezultat
Serverul ruleaza acum PHP 7.4 patch-uit (build Sury), web-shell-ul si caile de acces au fost eliminate, iar patch-urile de securitate se aplica automat — fara sa fi schimbat versiunea si fara sa fi riscat aplicatia. Migrarea pe 8.x ramane un pas optional, de facut la rece. Am pus serverul si sub monitorizare, ca sa vedem imediat orice fisier nou aparut in root-ul web.