Ha csütörtök, akkor meglepetés! – Amikor a PHP2Shell sebezhetőség felforgatja az infrastruktúrát

enlightened Ez az oldal a közösségért készül. heart Kövess minket máshol is:  Linux Mint Magyar Közösség a Mastodon-on  Telegram csatorna – csak hírek  Beszélgessünk a Telegram – Linux csevegő csoport  Hírek olvasása RSS segítségével  Linux Mint Hivatalos Magyar Közösség a Facebook-on      Linux Mint Baráti Kör a Facebook-on
wink Ha hasznosnak találod, és szeretnéd, hogy folytatódjon, támogasd a munkát Ko-fi vagy Paypal segítségével. laugh

Ha csütörtökön befut a riasztás egy kritikus, távoli kódfuttatást (RCE) lehetővé tevő PHP-sebezhetőségről – legyen szó akár a „PHP2Shell” néven futó támadási láncról –, nincs idő várakozni. A hír levélben, Slacken vagy riasztásban érkezik, a támadók pedig már pásztázzák a nyilvános IP-tartományokat. Ilyenkor a legfontosabb, hogy perceken belül legyen egy végrehajtható elhárítási tervünk, ne pedig órákig kutakodjunk, hogy melyik szerveren is fut Apache és melyiken nginx.

Ebben a cikkben végigmegyünk a teljes folyamaton – az azonnali izolációtól a csomagfrissítésen át a PHP keményítéséig –, és adunk egy egyszerű, hibamentesen futtatható bash scriptet, amely az egész procedúrát automatizálja. A cél: egyetlen paranccsal eljussunk az „uh-oh” állapotból a „megvagyunk” állapotba.

1. Azonnali izoláció és kármentesítés

Ha felmerül a fertőzés gyanúja – gyanús folyamat, ismeretlen fájl a webrootban, furcsa kimenő forgalom –, első lépés a webszerver leállítása. Ez nem esztétikai kérdés: amíg a szolgáltatás fut, a támadó akár percenként újra tudja használni a sebezhetőséget.

Debian/Ubuntu alatt:

bash

sudo systemctl stop apache2

RHEL/AlmaLinux/Rocky Linux alatt:

bash

sudo systemctl stop httpd

Ezután nézzük meg, mi történt az elmúlt 48 órában. A leggyakoribb nyomok: frissen módosult PHP fájlok, eval(), base64_decode(), system() vagy shell_exec() hívások a webrootban.

bash

# Elmúlt 48 órában módosult PHP fájlok
find /var/www/html -type f -name "*.php" -mmin -2880

# Veszélyes minták keresése
grep -rnwi "eval(" /var/www/html/
grep -rnwi "base64_decode" /var/www/html/
grep -rnwi "shell_exec" /var/www/html/

Tipp: a -mmin -2880 a 48 órán belül módosult fájlokat jelenti (2880 perc = 48 óra). Ha a támadás régebbi, növeld az értéket, vagy használj -mtime -7-et.

2. Frissítés – lehetőleg központilag

A sebezhetőség lényege, hogy a hibás PHP-verzió távoli kódfuttatást tesz lehetővé. A javítás szinte minden esetben a csomag frissítése. Egy szerveren ez néhány perc; száz szerveren viszont rémálom, ha kézzel csináljuk.

Itt jön képbe a Hermes Agent – vagy bármilyen konfigurációmenedzsment eszköz (Ansible, Puppet, Salt) –, amellyel a frissítés központilag, szinkronban futtatható a teljes flottán. A lényeg: ne egyesével SSH-zzunk, hanem adjunk ki egy parancsot, és nézzük, ahogy a flotta frissül.

Debian/Ubuntu alatt:

bash

sudo apt update && sudo apt install --only-upgrade php* -y
sudo systemctl restart apache2

RHEL/AlmaLinux/Rocky Linux alatt:

bash

sudo dnf upgrade --refresh -y && sudo dnf upgrade php* -y
sudo systemctl restart httpd

Ha a hivatalos OS-tárolók még nem frissültek (ez a kritikus napokban gyakori), az Agent segítségével vedd fel a dedikált tárolókat:

Figyelem: a tárolók hozzáadása disztribúció- és verziófüggő. Automatizálni csak akkor érdemes, ha a flotta homogén – heterogén környezetben ez könnyen eltörheti a rendszert. Éles rollout előtt tesztelj egy canary gépen.

3. PHP keményítés (Hardening)

A frissítés megszünteti a sebezhetőséget, de a mélységi védelem jegyében érdemes korlátozni a veszélyes függvényeket is. A php.ini fájlban a disable_functions sornál tiltsd le a parancsvégrehajtó és hálózati függvényeket:

ini

disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec

Emellett a webszerver konfigurációjában tiltsd le a PHP scriptek futtatását az uploads és egyéb írható mappákban. Apache esetén például:

apache

<Directory /var/www/html/uploads>
    php_admin_flag engine off
    <FilesMatch "\.php$">
        Require all denied
    </FilesMatch>
</Directory>

Nginx esetén a location blokkban érdemes internal-ra állítani a PHP-t, vagy egyáltalán nem továbbítani a PHP kéréseket az adott könyvtárból.

Figyelem: a curl_exec letiltása sok legitim alkalmazást (pl. WordPress pluginok, API-kliensek) eltörhet. Éles környezetben először tesztkörnyezetben próbáld ki, és nézd meg, mit panaszkodik az alkalmazás.

4. Ellenőrzés

A frissítés és hardening után ellenőrizzük, hogy minden rendben van-e:

bash

# PHP verzió
php -v

# Webszerver hibanapló (Debian/Ubuntu)
tail -n 50 /var/log/apache2/error.log

# Webszerver hibanapló (RHEL/AlmaLinux/Rocky)
tail -n 50 /var/log/httpd/error_log

Ha a webszervert leállítottuk, most indítsuk újra:

bash

sudo systemctl start apache2   # vagy httpd

5. A bash script – egy parancs, végponttól végpontig

A fenti lépéseket összefogja az alábbi script. Több disztribúciót kezel (Debian/Ubuntu és RHEL/AlmaLinux/Rocky), biztonsági mentést készít a php.ini-ről, és hibatűrően fut: egy-egy nem kritikus lépés hibája nem állítja le a teljes folyamatot.

bash

#!/usr/bin/env bash
#
# php2shell-response.sh
# PHP2Shell / kritikus PHP RCE sebezhetőség – gyors elhárító script
#
# Használat:
#   sudo ./php2shell-response.sh [--stop-web] [--no-upgrade] [--hardening-only]
#
# Opciók:
#   --stop-web        Leállítja a webszervert az izolációhoz (fertőzés gyanúja esetén)
#   --no-upgrade      Kihagyja a csomagfrissítést (csak vizsgálat + hardening)
#   --hardening-only  Csak a php.ini hardening + ellenőrzés fut
#
set -euo pipefail

# ---------- Színek / logolás ----------
RED='\033[0;31m'; GRN='\033[0;32m'; YEL='\033[1;33m'; NC='\033[0m'
log()  { echo -e "${GRN}[+]${NC} $*"; }
warn() { echo -e "${YEL}[!]${NC} $*"; }
err()  { echo -e "${RED}[x]${NC} $*" >&2; }
die()  { err "$*"; exit 1; }

# ---------- Root ellenőrzés ----------
[[ $EUID -eq 0 ]] || die "Root jogosultság szükséges. Futtasd: sudo $0"

# ---------- Argumentumok ----------
STOP_WEB=0
DO_UPGRADE=1
HARDENING_ONLY=0
for arg in "$@"; do
  case "$arg" in
    --stop-web)       STOP_WEB=1 ;;
    --no-upgrade)     DO_UPGRADE=0 ;;
    --hardening-only) HARDENING_ONLY=1; DO_UPGRADE=0 ;;
    -h|--help)        grep '^#' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
    *) die "Ismeretlen opció: $arg" ;;
  esac
done

# ---------- Disztribúció felismerése ----------
if [[ -f /etc/os-release ]]; then
  . /etc/os-release
  OS_ID="${ID:-unknown}"
  OS_LIKE="${ID_LIKE:-}"
else
  die "Nem található /etc/os-release"
fi

is_debian() { [[ "$OS_ID" =~ (debian|ubuntu) || "$OS_LIKE" =~ debian ]]; }
is_rhel()   { [[ "$OS_ID" =~ (rhel|centos|almalinux|rocky|fedora) || "$OS_LIKE" =~ (rhel|fedora) ]]; }

if is_debian; then
  PKG_MGR="apt"; WEB_SVC="apache2"; WEB_LOG="/var/log/apache2/error.log"
elif is_rhel; then
  PKG_MGR="dnf"; WEB_SVC="httpd";   WEB_LOG="/var/log/httpd/error_log"
else
  die "Nem támogatott disztribúció: $OS_ID"
fi
log "Disztribúció: $OS_ID  |  Csomagkezelő: $PKG_MGR  |  Webszerver: $WEB_SVC"

# ---------- Webszerver szolgáltatás nevének ellenőrzése ----------
if ! systemctl list-unit-files | grep -q "^${WEB_SVC}\.service"; then
  if systemctl list-unit-files | grep -q '^nginx\.service'; then
    warn "Apache nem található, de nginx igen. A script Apache-ra készült – ellenőrizd kézzel."
  else
    warn "A(z) $WEB_SVC szolgáltatás nem található."
  fi
fi

# ---------- 1) Izoláció ----------
if [[ $HARDENING_ONLY -eq 0 && $STOP_WEB -eq 1 ]]; then
  log "Webszerver leállítása (izoláció)..."
  systemctl stop "$WEB_SVC" || warn "Nem sikerült leállítani: $WEB_SVC"
fi

# ---------- 2) Gyanús fájlok keresése ----------
if [[ $HARDENING_ONLY -eq 0 ]]; then
  log "Elmúlt 48 órában módosult PHP fájlok keresése..."
  if [[ -d /var/www/html ]]; then
    find /var/www/html -type f -name "*.php" -mmin -2880 -print 2>/dev/null || true
  else
    warn "/var/www/html nem létezik – kihagyva."
  fi

  log "Veszélyes minták keresése..."
  for pattern in 'eval(' 'base64_decode' 'shell_exec' 'system(' 'passthru' 'assert('; do
    echo "--- minta: $pattern ---"
    grep -rnwi --include='*.php' -- "$pattern" /var/www/html 2>/dev/null | head -n 50 || true
  done
fi

# ---------- 3) Csomagfrissítés ----------
if [[ $DO_UPGRADE -eq 1 ]]; then
  log "PHP csomagok frissítése..."
  if [[ "$PKG_MGR" == "apt" ]]; then
    export DEBIAN_FRONTEND=noninteractive
    apt-get update -y
    apt-get install --only-upgrade -y 'php*' || warn "apt upgrade részben sikertelen – ellenőrizd kézzel."
  else
    dnf upgrade --refresh -y || warn "dnf upgrade sikertelen – ellenőrizd kézzel."
    dnf upgrade -y 'php*' || warn "dnf php upgrade sikertelen – ellenőrizd kézzel."
  fi

  log "Webszerver újraindítása..."
  systemctl restart "$WEB_SVC" || warn "Nem sikerült újraindítani: $WEB_SVC"
fi

# ---------- 4) PHP hardening ----------
log "PHP hardening (disable_functions)..."
PHP_INI=$(php --ini 2>/dev/null | awk -F': ' '/Loaded Configuration File/ {print $2}')
if [[ -z "${PHP_INI:-}" || "$PHP_INI" == "(none)" ]]; then
  warn "Nem található betöltött php.ini – hardening kihagyva."
else
  BACKUP="${PHP_INI}.bak.$(date +%Y%m%d%H%M%S)"
  cp -a "$PHP_INI" "$BACKUP"
  log "Biztonsági mentés: $BACKUP"

  DANGEROUS="exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec"
  if grep -qE '^\s*disable_functions\s*=' "$PHP_INI"; then
    sed -i -E "s|^\s*disable_functions\s*=.*|disable_functions = ${DANGEROUS}|" "$PHP_INI"
    log "Meglévő disable_functions sor frissítve."
  else
    printf '\ndisable_functions = %s\n' "$DANGEROUS" >> "$PHP_INI"
    log "disable_functions hozzáadva a php.ini-hez."
  fi
fi

# ---------- 5) Ellenőrzés ----------
log "PHP verzió ellenőrzése..."
php -v 2>/dev/null | head -n 1 || warn "php parancs nem elérhető."

if [[ -f "$WEB_LOG" ]]; then
  log "Webszerver hibanapló utolsó 10 sora ($WEB_LOG):"
  tail -n 10 "$WEB_LOG" || true
else
  warn "Hibanapló nem található: $WEB_LOG"
fi

log "Kész. Ha a webszervert leállítottad, indítsd újra kézzel: systemctl start $WEB_SVC"

Használat

bash

chmod +x php2shell-response.sh

# Teljes elhárítás (izoláció + vizsgálat + frissítés + hardening)
sudo ./php2shell-response.sh --stop-web

# Csak vizsgálat + frissítés (webszerver futva marad)
sudo ./php2shell-response.sh

# Csak hardening + ellenőrzés
sudo ./php2shell-response.sh --hardening-only

# Súgó
./php2shell-response.sh --help

Miért biztonságos?

  • Idempotens: többször futtatható, a disable_functions sort frissíti, nem duplikálja.

  • Biztonsági mentés: minden php.ini módosítás előtt .bak.<timestamp> mentés készül.

  • Hibatűrő: set -euo pipefail a kritikus hibáknál megállít, de a nem kritikus lépéseknél || warn van, így egy-egy hiba nem borítja fel a folyamatot.

  • --stop-web nélkül a webszerver futva marad – éles környezetben először ezt futtasd, csak fertőzés gyanúja esetén állítsd le.

  • Sury PPA / Remi repo hozzáadását szándékosan nem automatizáltam – ez disztribúció- és verziófüggő, könnyen eltörheti a rendszert. Ha kell, futtasd kézzel a cikk szerint.

Egy kritikus PHP-sebezhetőség nem attól veszélyes, hogy létezik, hanem attól, hogy mennyi idő alatt reagálunk rá. A fenti folyamat – izoláció → vizsgálat → frissítés → hardening → ellenőrzés – percek alatt végigvihető, ha van egy kész scriptünk, és ha a flotta frissítését központilag tudjuk vezérelni.

A script nem csodaszer: a grep találatokat mindig nézd át kézzel, a hardening élesítése előtt tesztelj, és a tárolók hozzáadását csak homogén környezetben automatizáld. De arra tökéletes, hogy a „csütörtöki meglepetés” ne húzza el napokra a rendszergazda délutánját – hanem legyen meg a válasz már az első órában.

Jó vadászatot – és jó csütörtököt!

Hozzászólások