Explorer
KNOW-PAT-060

Exploitation PHP-FPM RCE via FastCGI (CVE-2019-11043) - Détection et protection

Domaine
cybersecu
Type
pattern
Priorité
P2

Parent : [[INDEX-CYBERSECU]]

Exploitation PHP-FPM RCE via FastCGI (CVE-2019-11043) - Détection et protection

Problème

L'archive Ygg contient un exploit Python (fcgi_exploit_phpfpm.py) qui exploite la vulnérabilité CVE-2019-11043 dans PHP-FPM via nginx. L'exploit permet l'exécution de code PHP arbitraire en manipulant les paramètres FastCGI, notamment auto_prepend_file = php://input et allow_url_include = On.

Contexte archive Ygg

  • Fichier : 10_SERVEURS_LATERAUX/11b_web_185.132.134.125/fcgi_exploit_phpfpm.py
  • Cible : example.com (WordPress sur /home/gopay/htdocs/example.com/)
  • Objectif : Lire wp-config.php pour extraire les credentials
  • Port cible : 127.0.0.1:14002 (PHP-FPM socket)

Technique d'exploitation

L'exploit construit un paquet FastCGI avec :

params = {
    "SCRIPT_FILENAME": "/home/gopay/htdocs/example.com/index.php",
    "SCRIPT_NAME": "/index.php",
    "REQUEST_METHOD": "POST",
    "CONTENT_TYPE": "application/x-www-form-urlencoded",
    "CONTENT_LENGTH": str(len(php_code)),
    "SERVER_SOFTWARE": "nginx",
    "REMOTE_ADDR": "127.0.0.1",
    "SERVER_NAME": "example.com",
    "SERVER_PORT": "443",
    "REQUEST_URI": "/index.php",
    "DOCUMENT_ROOT": "/home/gopay/htdocs/example.com",
    # 🚨 Les deux lignes suivantes sont la vulnérabilité
    "PHP_VALUE": "allow_url_include = On\nauto_prepend_file = php://input",
}

Payload PHP :

<?php 
echo '---START---'.chr(10); 
echo file_get_contents('/home/gopay/htdocs/example.com/wp-config.php'); 
echo chr(10).'---END---'; 
die(); 
?>

Comment se protéger

1. Configuration nginx sécurisée

# /etc/nginx/conf.d/php-fpm.conf
# VULNÉRABLE (NE PAS FAIRE)
location ~ [^/]\.php(/|$) {
    fastcgi_split_path_info ^(.+\.php)(/.*)$;  # ❌ Vulnérable
    fastcgi_pass 127.0.0.1:9000;
}

# SÉCURISÉ ✅
location ~ ^/index\.php$ {
    # fastcgi_split_path_info supprimé ou protégé
    fastcgi_pass 127.0.0.1:9000;
    include fastcgi_params;
    
    # Ne pas passer PHP_VALUE/PHP_ADMIN_VALUE depuis l'extérieur
    fastcgi_param PHP_VALUE "";
    fastcgi_param PHP_ADMIN_VALUE "";
    
    # Restreindre SCRIPT_FILENAME
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

2. Configuration PHP-FPM sécurisée

; /etc/php/8.1/fpm/pool.d/www.conf
; Désactiver allow_url_include
php_admin_value[allow_url_include] = Off

; Désactiver auto_prepend_file/auto_append_file
php_admin_value[auto_prepend_file] =
php_admin_value[auto_append_file] =

; Empêcher la modification de ces valeurs
php_admin_flag[allow_url_fopen] = Off

; Socket UNIX au lieu de TCP (moins exposé)
listen = /run/php/php8.1-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

; Restreindre les extensions autorisées
security.limit_extensions = .php .php3 .php4 .php5 .php7

3. Patch CVE-2019-11043

# Mettre à jour PHP-FPM
apt update && apt upgrade php-fpm

# Vérifier la version
php-fpm -v
# Doit être >= 7.1.33, >= 7.2.24, >= 7.3.11

# Alternative : utiliser PHP 8.x (patché)

4. Monitoring pour détecter l'exploitation

# detect_phpfpm_rce.py — Détection d'exploitation FastCGI
import re
from collections import defaultdict

class PHPFPMExploitDetector:
    def __init__(self):
        self.suspicious_params = [
            'auto_prepend_file',
            'auto_append_file',
            'allow_url_include',
            'allow_url_fopen',
            'disable_functions',
        ]
    
    def analyze_fastcgi_logs(self, log_line):
        # Rechercher les paramètres suspects dans les logs FastCGI
        for param in self.suspicious_params:
            if f"PHP_VALUE[{param}]" in log_line or f"PHP_ADMIN_VALUE[{param}]" in log_line:
                return True, f"Suspicious PHP-FPM parameter: {param}"
        
        # Détecter php://input dans les logs
        if 'php://input' in log_line:
            return True, "php://input detected in FastCGI params"
        
        # Détecter des chemins inhabituels
        if 'SCRIPT_FILENAME' in log_line:
            match = re.search(r'SCRIPT_FILENAME=([^\s]+)', log_line)
            if match:
                path = match.group(1)
                # Vérifier si le script existe
                if not os.path.exists(path):
                    return True, f"Non-existent SCRIPT_FILENAME: {path}"
        
        return False, None

5. WAF Rules

# ModSecurity / OWASP CRS
SecRule REQUEST_HEADERS:Content-Type "@contains application/x-www-form-urlencoded" \
    "id:1001,phase:2,deny,status:403,msg:'Suspicious form data to PHP-FPM'"

SecRule REQUEST_BODY "@rx (auto_prepend_file|auto_append_file|allow_url_include)" \
    "id:1002,phase:2,deny,status:403,msg:'PHP configuration injection attempt'"

Checklist de validation

  • PHP-FPM mis à jour (>= 7.3.11 ou PHP 8.x)
  • fastcgi_split_path_info supprimé ou protégé
  • allow_url_include = Off en dur (php_admin_value)
  • auto_prepend_file vide en dur (php_admin_value)
  • PHP-FPM sur socket UNIX (pas TCP exposé)
  • security.limit_extensions configuré
  • Pas de PHP_VALUE/PHP_ADMIN_VALUE passés depuis nginx
  • Logs FastCGI monitorés pour les paramètres suspects
  • WAF rules actives pour les injections PHP
  • Chroot ou containers pour isoler PHP-FPM

Anti-pattern associé

  • KNOW-ANT-009 — Exposed PHP-FPM TCP socket with default configuration

Références