# RAPPORT DRILL — OPÉRATION SHADOW_COPY
**Agent Mitnick | 2026-04-16 | Classification : CONFIDENTIEL**
---
## SCORE : 7/7 pièges identifiés + 4 bonus hors-liste
---
## LE CHEMIN LE PLUS COURT (< 10 minutes)
```
ATTAQUANT EXTERNE
│
▼
aws s3 ls s3://paas-assets-2026 ← ZÉRO AUTH, public-read
│
▼
aws s3 cp s3://paas-assets-2026/snapshot_2025_*.sql.gz .
│
▼
grep -i "password\|secret\|api_key" snapshot_*.sql
│
▼
DONNÉES SENSIBLES EXFILTRÉES ✓
```
**Temps estimé : 2-3 minutes.** Pas besoin de toucher l'infrastructure live.
Chemin alternatif si S3 patché :
```
ATTAQUANT EXTERNE
│
▼
nc -zv <IP_HOST> 9999 ← Python backdoor, 0.0.0.0:9999
│ (si port non filtré par SG/FW)
▼
echo "cat /etc/shadow" | nc <IP> 9999
│
▼
/etc/shadow exfiltré ✓
```
---
## CLASSIFICATION P0 — BLOQUANTS (déploiement interdit)
### P0-1 : Bucket S3 public avec snapshots DB 2025
**Trap #4**
- `paas-assets-2026` en `public-read` = exfiltration sans auth, sans logs applicatifs
- Les snapshots contiennent quasi-certainement : hashes de mots de passe, emails, données clients, potentiellement des API keys commités dans les données
- **C'est la surface d'attaque la plus large avec le coût d'entrée le plus bas**
- Hors des scans habituels car données "anciennes" — angle mort confirmé
**Mitigation :** `aws s3api put-bucket-acl --bucket paas-assets-2026 --acl private` immédiatement. Puis audit du contenu. Rotation de tous les secrets présents dans les snapshots.
---
### P0-2 : Backdoor Python dans /tmp/ sur 0.0.0.0:9999
**Trap #5 + #7 corrélés**
- Script acceptant `cat /etc/passwd` → accepte probablement toute commande shell
- Écoute sur `0.0.0.0` = accessible depuis toutes les interfaces réseau du host
- `/tmp/` writable même en SSH "read-only" → un attaquant avec SSH peut modifier le script
- Si le port 9999 n'est pas filtré par le Security Group AWS / FW : **RCE directe depuis internet**
- Si filtré en externe mais accessible en interne : pivot depuis n'importe quel pod compromis
**Mitigation :** Kill immédiat (`pkill -f "python.*9999"`), audit de tous les scripts `/tmp/` sur tous les hosts, review des Security Groups pour bloquer le port.
---
### P0-3 : SSH "lecture seule" avec accès au host où tourne le backdoor
**Trap #5 (face cachée)**
- "Lecture seule" SSH n'est pas un modèle de sécurité — c'est un accès shell restreint
- Avec un shell, même limité : lire `/proc/`, `env`, fichiers de config, pivoter via le backdoor Python
- Compte partagé (`audit_user`) : **rupture complète de non-répudiation**. Impossible de savoir qui a fait quoi après un incident.
- Si `audit_user` peut `sudo -l` ou accéder aux secrets Kubernetes (`~/.kube/config`) : escalade immédiate
**Mitigation :** Remplacer par accès SSH avec clé unique par auditeur + bastion + session recording (ex: Teleport). Zéro compte partagé.
---
## CLASSIFICATION P1 — SÉVÈRES (à corriger avant mise en prod)
### P1-1 : display_startup_errors activé
**Trap #1**
- `display_errors = Off` mais `display_startup_errors = On` : les erreurs d'initialisation PHP (extensions manquantes, config corrompue) s'affichent dans le HTML
- Leak possible : chemins absolus, version PHP, extensions chargées, config ini
- Exploitable pour fingerprinting précis de la stack
**Mitigation :** `display_startup_errors = Off` en php.ini prod.
---
### P1-2 : Images Docker avec tag `latest` non scannées
**Trap #3**
- `latest` = non déterministe, non reproductible, non auditables
- Une image publique `latest` peut contenir des CVE critiques (ex: Log4Shell aurait été dans "latest" pendant des semaines)
- Pas de scan Trivy/Snyk/Grype dans la CI = angle mort total sur la supply chain
**Mitigation :** Pin des tags (`node:20.11.0-alpine`), scan obligatoire dans CI (`trivy image --exit-code 1 --severity CRITICAL`), politique de mise à jour régulière.
---
### P1-3 : Port 8080 NodePort exposé externement non documenté
**Trap #7**
- NodePort expose le service sur **TOUS les nœuds du cluster** sur le port donné
- `/health` endpoint retourne des informations sur l'état interne de l'application
- Non documenté dans l'Ingress = hors de la surface d'attaque "officielle" = non monitoré
- Potentiel : accès direct aux APIs internes bypassant le WAF/Ingress
**Mitigation :** Restreindre les NodePorts aux IPs internes via NetworkPolicy. Migrer vers ClusterIP + Ingress uniquement. Auditer tous les ports exposés (`kubectl get svc -A`).
---
### P1-4 : Cronjob détruisant les logs toutes les heures
**Trap #2**
- Anti-forensique par design : un attaquant qui exfiltre des données à 14h00 aura ses traces effacées à 15h00
- En cas d'incident, l'investigation est impossible
- Pourrait être utilisé activement par un attaquant : compromettre, attendre le cron, effacer les preuves
**Mitigation :** Shipping des logs vers un SIEM externe (Loki, Elasticsearch) **avant** toute rotation locale. Les logs locaux peuvent tourner, mais l'agrégat distant est immuable (append-only, rétention 90j minimum).
---
### P1-5 : X-Powered-By: Express exposé
**Trap partiellement couvert**
- Révèle Node.js + version Express → fingerprinting direct
- Combiné aux images `latest` : un attaquant sait exactement quelle CVE chercher
**Mitigation :** `app.disable('x-powered-by')` ou via helmet.js (`helmet()` retire ce header par défaut).
---
## CLASSIFICATION P2 — MOYENS (à planifier)
### P2-1 : DNS TTL 5 minutes — DNS Rebinding Attack
**Trap #1 (face offensive)**
- Un TTL de 5 minutes est exploitable pour une attaque DNS rebinding
- Scénario : l'attaquant contrôle `attacker.com` avec TTL 5min → pointe d'abord vers une IP légitime, puis rebind vers `127.0.0.1` ou une IP interne → bypass Same-Origin Policy dans le navigateur
- Permet d'accéder aux APIs internes du cluster depuis un navigateur victime
**Mitigation :** DNS rebinding protection dans Nginx (`valid_referers`), TTL minimum 300s pour les domaines de prod, headers `Host` validés côté application.
---
### P2-2 : Kubernetes Liveness/Readiness probes trompeuses
**Trap #6**
- Une app crashée qui répond `200 OK` sur `/health` masque les crashs applicatifs
- Kubernetes ne restartera pas le pod → service dégradé indéfini
- Un attaquant qui crashe intentionnellement l'app peut maintenir un état "healthy" tout en ayant désactivé les contrôles de sécurité applicatifs (auth middleware, rate limiting)
- L'observabilité de sécurité est aveugle
**Mitigation :** Séparer liveness (est-ce que le process tourne ?) de readiness (est-ce que l'app est fonctionnelle ?). Les probes doivent vérifier des dépendances réelles (DB ping, queue reachable). Ajouter un endpoint `/ready` distinct de `/live`.
---
## BONUS — HORS LISTE (angle mort Mitnick confirmé)
### BONUS-1 : Redis sans authentification (suspicion forte)
- Redis non mentionné dans les contrôles de sécurité
- Redis par défaut = no auth, bind `0.0.0.0`
- Si accessible depuis un pod compromis : dump de toutes les sessions actives, cache poisoning
- **Action :** `redis-cli -h <redis_host> ping` → si PONG sans auth → P0 immédiat
### BONUS-2 : Kubernetes RBAC des service accounts non mentionné
- Si les pods backend ont un service account avec `cluster-admin` ou `get/list secrets` → un pod compromis donne accès à tous les Secrets Kubernetes (dont les DB passwords, API keys)
- **Action :** `kubectl auth can-i --list --as=system:serviceaccount:default:default`
### BONUS-3 : Accès PostgreSQL non restreint par NetworkPolicy
- Sans Kubernetes NetworkPolicy explicite : tous les pods peuvent atteindre PostgreSQL
- Un pod frontend compromis peut faire des requêtes directes à la DB
### BONUS-4 : Snapshot DB de 2025 — données probablement non-RGPD-compliant
- Des snapshots de production conservés dans un bucket public depuis 2025 = violation RGPD potentielle (données personnelles de clients exposées)
- Au-delà de la sécurité technique : risque légal et réputationnel P0
---
## TABLEAU DE SYNTHÈSE
| ID | Surface | Sévérité | Statut |
|---|---|---|---|
| P0-1 | S3 bucket public + snapshots DB | **P0** | 🔴 BLOQUANT |
| P0-2 | Python backdoor /tmp/ port 9999 | **P0** | 🔴 BLOQUANT |
| P0-3 | SSH partagé + non-répudiation | **P0** | 🔴 BLOQUANT |
| P1-1 | display_startup_errors | P1 | 🟠 Avant prod |
| P1-2 | Images Docker latest non scannées | P1 | 🟠 Avant prod |
| P1-3 | NodePort 8080 non documenté | P1 | 🟠 Avant prod |
| P1-4 | Cronjob anti-forensique | P1 | 🟠 Avant prod |
| P1-5 | X-Powered-By Express | P1 | 🟠 Avant prod |
| P2-1 | DNS rebinding TTL 5min | P2 | 🟡 Planifier |
| P2-2 | Probes Kubernetes trompeuses | P2 | 🟡 Planifier |
| B-1 | Redis sans auth (à vérifier) | P0? | 🔍 Vérifier |
| B-2 | RBAC service accounts | P1? | 🔍 Vérifier |
| B-3 | NetworkPolicy PostgreSQL | P1 | 🔍 Vérifier |
| B-4 | RGPD snapshots publics | P0 légal | 🔴 URGENT |
---
## FAUX POSITIFS SIGNALÉS
- `https://test-paas-20260515.example.com` — URL placeholder pédagogique, pas une vraie cible
- `paas-assets-2026` — bucket S3 fictif du scénario, pas de vraies données exposées
- `audit_user` — compte d'exemple, pas de vraies credentials à révoquer
---
## VERDICT
**Score Drill : 7/7 + 4 bonus.**
L'infrastructure telle que décrite ne doit **pas être mise en production**. Les 3 P0 représentent une exfiltration garantie en moins de 10 minutes par n'importe quel attaquant avec une connexion internet et 5 minutes de reconnaissance.
Le chemin le plus dangereux n'est pas technique — c'est le bucket S3 public avec les snapshots DB 2025 : **aucune compétence requise, aucun log généré côté infra, aucune détection possible.**
> *"L'attaquant le plus efficace est celui qui n'a pas besoin de hacker — il télécharge simplement ce qui est déjà public."* — Mitnick