Pe 21 octombrie 2026, echipa PHP a publicat patch-uri de urgență pentru două vulnerabilități critice, CVE-2026-109618 și CVE-2026-109619, numite împreună „PHP2Shell”. Ambele permit execuție de cod de la distanță (RCE) fără autentificare. Atacul are loc înainte să ruleze codul aplicației, deci nu contează dacă site-ul tău e scris corect. Contează doar versiunea de PHP de pe server.

Ce este PHP2Shell, pe scurt

  • CVE-2026-109618: RCE prin coruperea memoriei la out-of-memory. Un request multipart forțează PHP să rămână fără memorie în timpul parsării, iar curățarea request-ului folosește apoi date inițializate doar parțial.
  • CVE-2026-109619: RCE prin stare globală rămasă de la un request anterior (use-after-free). Un request ulterior, servit de același worker, folosește memorie deja eliberată. Calea confirmată trece prin session.upload_progress.

Codul rulează cu drepturile utilizatorului serverului web (www-data, apache, nobody sau userul contului pe serverele cu PHP-FPM per cont). Vulnerabilitățile au fost descoperite de cercetători de la Calif, OpenAI și Wiz. watchTowr spune că a observat deja exploatare în rețeaua sa de honeypot-uri. CISA a emis o alertă pe 21 octombrie și cere patch imediat.

Cine este afectat

Orice instalare PHP care servește request-uri web: Apache mod_php, PHP-FPM, php-cgi, LiteSpeed (LSAPI) și FrankenPHP. Riscul cel mai mare este la mod_php.

RamurăVulnerabilVersiune reparată
8.5< 8.5.128.5.12
8.4< 8.4.278.4.27
8.3< 8.3.368.3.36
8.2< 8.2.358.2.35
7.4, 8.0, 8.1toatefără release oficial; doar backport-uri de la distribuție/vendor LTS sau patch-uri minime din sursă
7.3 și mai vechi (inclusiv PHP 5)toatenicio reparație, trebuie migrat

Atenție la PHP-ul ascuns în produse: panouri de administrare, appliance-uri, imagini Docker, routere cu interfață web.

Cum verifici serverul

Versiunea din linia de comandă și ce rulează efectiv pe site pot fi diferite. Verifică ambele.

Versiunea CLI implicită:

php -v

Toate binarele PHP de pe server (inclusiv cele alternative):

find / -xdev -type f -name php -perm -u+x 2>/dev/null \
  | while read p; do \
  echo "$p: $($p -r 'echo PHP_VERSION;')"; done

Procese PHP care rulează acum:

ps -eo user,cmd \
  | grep -E 'php-fpm|php-cgi|lsphp|httpd|apache2'

Ce versiune anunță site-ul (dacă X-Powered-By nu e ascuns):

curl -sI https://domeniul-tau.ro \
  | grep -iE 'x-powered-by|server'

Build reparat? Fix-ul adaugă setarea memory_hard_limit_percent:

php -r 'var_dump(ini_get(
  "memory_hard_limit_percent"));'

Dacă rezultatul e false, build-ul e vechi. Orice altă valoare înseamnă build cu patch (util la backport-urile care păstrează un număr de versiune „vechi”).

OpenAI a publicat pe GitHub un verificator de versiune, nu o reparație: openai/php2shell-checker. Citește headerele site-ului sau instalarea locală și îți spune dacă versiunea anunțată e în zona afectată:

git clone https://github.com/openai/php2shell-checker
cd php2shell-checker
node phpcheck.js "https://domeniul-tau.ro"

Rezultatul „OK” se bazează doar pe versiunea anunțată în headere. Nu confirmă că serverul are patch-ul. Dacă headerul e ascuns, primești „UNKNOWN”. Reparația propriu-zisă e update-ul de PHP.

Ce trebuie să facă adminii: reparația

1. Update PHP și restart la toate workerele

Debian / Ubuntu (repo-ul distribuției sau ppa:ondrej/php / packages.sury.org):

apt update
apt list --upgradable | grep php
apt install --only-upgrade 'php*'
systemctl restart php*-fpm apache2

RHEL / AlmaLinux / Rocky (Remi sau AppStream):

dnf upgrade 'php*'
systemctl restart php-fpm httpd

Fără restart, workerele vechi rămân încărcate în memorie cu codul vulnerabil.

2. Servere CentOS / AlmaLinux cu CWP sau CloudLinux (PHP alternativ în /opt/alt)

Pe aceste servere fiecare site poate rula altă versiune de PHP, instalată separat de PHP-ul sistemului. Fă inventarul complet:

ls -d /opt/alt/php* 2>/dev/null
for p in /opt/alt/php*/usr/bin/php; do \
  echo "$p: $($p -r 'echo PHP_VERSION;')"; done
  • CloudLinux (alt-php): yum update 'alt-php*', apoi restart la LSAPI/PHP-FPM sau la webserver. Verifică în changelog-ul CloudLinux că pachetul include backport-ul pentru cele două CVE-uri.
  • CWP: recompilează versiunile de PHP-FPM din PHP Version Switcher / PHP-FPM Selector cu ultimele versiuni minore (8.2.35, 8.3.36, 8.4.27, 8.5.12), apoi repornește serviciile php-fpm*.
  • Conturile pe PHP 5.x, 7.0–7.3 nu au reparație. Mută-le pe 8.2+ sau izolează-le până la migrare.

3. Măsuri temporare dacă nu poți face update azi

Echipa PHP spune clar că nicio setare nu elimină complet riscul. Măsurile de mai jos doar reduc expunerea până la update:

Se pun în php.ini, în pool-ul FPM sau în config-ul Apache (nu în .user.ini și nu cu ini_set):

session.upload_progress.enabled = 0
display_errors = Off

Doar pe PHP 8.5:

fatal_error_backtraces = 0

Fără upload-uri pune post_max_size = 8M, cu upload-uri 32M:

post_max_size = 8M
upload_max_filesize = 6M

În pool-ul FPM, folosește php_admin_value ca aplicațiile să nu poată suprascrie valorile:

php_admin_value[session.upload_progress.enabled] = 0
php_admin_value[memory_limit] = -1

memory_limit = -1 este recomandarea oficială pentru CVE-2026-109618, dar pune limita de memorie la nivel de container/cgroup ca să nu cadă serverul. Nu folosi RLIMIT_AS/RLIMIT_DATA (ulimit -v), pentru că declanșează exact calea vulnerabilă. După orice schimbare, repornește toate workerele.

4. WAF / ModSecurity și limite la nivel de webserver

CISA spune că un WAF poate oferi o protecție parțială până la update. Limitează request-urile multipart înainte să ajungă la PHP:

Apache:

LimitRequestBody 33554432

ModSecurity:

SecRequestBodyLimit 33554432
SecArgumentsLimit 500
SecRule &ARGS "@gt 500" \
  "id:1009618,phase:2,deny,status:413,\
  log,msg:'Prea multe campuri'"

Nginx:

client_max_body_size 32m;

Pe CWP, ModSecurity cu regulile OWASP CRS se activează din panoul de administrare. Ține regulile actualizate.

5. Hardening general (bun oricum, nu repară CVE-urile)

disable_functions = exec,passthru,
  shell_exec,system,proc_open,
  popen,pcntl_exec
open_basedir = /home/USER/public_html:/tmp
expose_php = Off
allow_url_include = Off
  • Fiecare site pe userul lui (PHP-FPM per cont), nu totul sub www-data.
  • Fără execuție PHP în folderele de upload (php_admin_flag engine off sau location ~ ^/uploads/.*\.php$ { deny all; }).
  • Document root fără drept de scriere pentru userul PHP acolo unde se poate.

Verifică dacă ai fost deja atacat

Caută semne începând cu câteva zile înainte de 21 octombrie:

Erori tipice de exploatare în logurile PHP / Apache / FPM:

grep -rE "zend_mm_heap corrupted|\
 in Unknown on line 0|exited on signal" \
  /var/log/httpd /var/log/apache2 \
  /var/log/php*-fpm* \
  /usr/local/apache/logs 2>/dev/null \
  | tail -50

POST-uri foarte mari / multe răspunsuri 500 din același IP:

awk '$6=="\"POST" && $9==500 {print $1}' \
  /var/log/apache2/access.log \
  | sort | uniq -c | sort -rn | head

Procese shell pornite de userul webserverului:

ps -ef --forest \
  | grep -E 'www-data|apache|nobody' \
  | grep -E 'sh|bash|curl|wget|nc '

Fișiere PHP modificate recent în webroot și uploads:

find /var/www /home/*/public_html \
  -name '*.php' -mtime -7 -type f

Webshell-uri: funcții tipice:

grep -rlE \
  -e 'eval\(base64_decode|gzinflate\(base64' \
  -e 'assert\(\$_(POST|GET|REQUEST)|system\(\$_' \
  /var/www /home/*/public_html

Fișiere .php în folderele de upload:

find / -xdev -path '*uploads*' -name '*.php'

Crontab-uri și conexiuni suspecte:

for u in www-data apache nobody; do \
  crontab -l -u $u; done
ss -tnp | grep -E 'php|sh'

Pentru scanare automată poți folosi Linux Malware Detect (maldet) cu ClamAV. Dacă găsești ceva: izolează contul, schimbă parolele de bază de date, cheile API și sesiunile, pentru că bug-ul de citire a memoriei poate scurge date din procesul PHP. Reinstalează din backup curat, nu „curăța” fișier cu fișier.

Surse

Ai un server sau un site pe PHP și nu ești sigur dacă e reparat? Scrie-mi și îl verific, fac update-ul și securizez configurarea.