Skip to content
KitploitKITPLOIT
도구익스플로잇블로그
Log in
제출
도구익스플로잇블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

피드문의개인정보© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
POC-CVE-2026-65971 — CVE-2026-65971에 대한 개념 증명 및 기술 문서 — power-components/livewire-powergrid (< 6.10.4)의 sortDirection Livewire 속성을 통한 SQL 인젝션 | Kitploit
도구/GitHubGitHub/biitts/poc-cve-2026-65971
Vulnerability AnalysisExploitationWeb Application ExploitationPenetration TestingPapers & ResearchLearning & Education
GitHubbiitts/poc-cve-2026-65971

POC-CVE-2026-65971

CVE-2026-65971에 대한 개념 증명 및 기술 문서 — power-components/livewire-powergrid (< 6.10.4)의 sortDirection Livewire 속성을 통한 SQL 인젝션

저장소 보기
1153개월 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2026-65971 — Livewire PowerGrid의 sortDirection을 통한 SQL 인젝션

CVE-2026-65971 / GHSA-7fgc-3h6c-698r에 대한 개념 증명 및 전체 기술 문서입니다. 이는 power-components/livewire-powergrid의 SQL 인젝션으로, 공개된 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
---

## 🧠 요약

PowerGrid는 Laravel + Livewire용 데이터테이블 컴포넌트입니다 (~2k stars, Laravel 관리자 패널에서 널리 사용됨). 정렬 상태는 두 개의 **public Livewire properties**에 위치합니다:```php
public string $sortField = 'id';
public string $sortDirection = 'asc';

In Livewire에서 public 프로퍼티는 컴포넌트의 wire format의 일부입니다 — 컴포넌트에 접근 가능한 클라이언트는 POST /livewire/update를 통해 이를 설정할 수 있습니다. 이는 의도된 설계이며, 보안 경계는 서버가 해당 값을 어떻게 처리하는지에 있습니다.

PowerGrid의 naturalSort() 기능은 리터럴 플레이스홀더 {sortDirection}을 포함하는 원시 ORDER BY 표현식을 생성하며, 파이프라인은 이 플레이스홀더를 원시, 검증되지 않은 프로퍼티 값으로 대체한 후 문자열을 orderByRaw()에 전달합니다. 따라서 방향 키워드는 SQL 내부에 그대로 삽입됩니다.

Laravel 자체의 orderBy()는 asc/desc가 아닌 것은 거부하며, 이 검증이 일반적인 정렬 경로를 안전하게 만듭니다. 버그는 동일한 절에 대한 두 번째 검증되지 않은 경로가 존재하며 — 공격자가 검증을 완전히 건너뛰고 이 경로에 도달할 수 있다는 것입니다 (우회 경로 참조).

결과: ORDER BY 절에서의 임의 SQL로, 데이터베이스 사용자가 읽을 수 있는 모든 데이터를 읽기 위한 블라인드 불리언/시간 기반 오라클로 악용 가능합니다.


🔥 영향

naturalSort를 사용하는 PowerGrid 테이블에 접근할 수 있는 모든 사용자는 데이터베이스의 임의 데이터 — 다른 테이블, 비밀번호 해시, 세션 토큰, API 키, 교차 테넌트 레코드 — 를 시간 기반/불리언 오라클을 통해 읽을 수 있습니다.

  • 기밀성: 높음. DB 사용자가 SELECT할 수 있는 모든 것의 전체 읽기.
  • 무결성: 낮음. 스택 쿼리는 PDO MySQL의 기본 구성으로 차단되므로 ; UPDATE ...는 실행되지 않습니다. 쓰기 영향은 서브쿼리가 트리거할 수 있는 것으로 제한됩니다.
  • 가용성: 낮음. 동일한 프리미티브는 공격자에게 SLEEP() 및 무거운 서브쿼리를 제공합니다 — 데이터베이스 스레드를 고정시키는 데 쉽게 악용 가능.
  • 일반적인 배치는 악화 요인입니다. PowerGrid 테이블은 관리자 패널 및 멀티테넌트 백오피스에 위치하며 — 정확히 흥미로운 데이터가 있는 곳입니다. 이러한 테이블에 접근하는 낮은 권한의 테넌트 사용자는 전체 데이터베이스를 유출할 수 있습니다.

필요 권한은 PR:L입니다. 데이터테이블은 일반적으로 애플리케이션 인증 뒤에 있기 때문입니다. 영향을 받는 테이블이 인증되지 않은 페이지에 렌더링되는 경우, PR:N → 8.2 높음으로 다시 계산합니다.


🧩 근본 원인 — 완전한 오염 체인

세 개의 파일, 세 단계. 모든 참조는 취약한 태그 v6.10.3에 대한 것입니다.

1단계 — 소스: 공격자 제어 public 프로퍼티

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

두 속성 모두 허용 목록, 유효성 검사 규칙 또는 정규화 설정자가 없습니다. `sortDirection`은 할당되거나 뒤집힐 뿐입니다:```php
public function reverseSort(): string    // line 37
{
    return $this->sortDirection === 'asc' ? 'desc' : 'asc';
}

updatedSortDirection() (103번째 줄)이 존재합니다 — 검증이 이루어져야 할 자연스러운 위치입니다 — 하지만 v6.10.3에서는 지연 로딩 부기만 처리합니다. 이 메서드는 값을 검사하지 않습니다.

Livewire가 요청에서 바로 공개 속성을 채우기 때문에, sortDirection은 이 시점에서 완전히 공격자에 의해 제어되는 임의의 문자열입니다.

Stage 2 — The raw clause: naturalSort() plants a placeholder

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

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

return $this;

});

`Sql::sortStringAsNumber()`는 `src/DataSource/Support/Sql.php` (60–100행)에 있는 `getSortSqlByDriver()`가 구축한 드라이버별 표현식으로 해석됩니다. 모든 드라이버 변형은 동일한 리터럴 플레이스홀더로 끝납니다:```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; }

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

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

}

그리고 실행, 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

---

## 🔓 우회 — 라라벨의 유효성 검사가 당신을 구하지 못하는 이유

이 부분이 "원시 문자열 보간법"을 실제로 악용 가능한 버그로 만드는 부분이며, 이것이 성숙하고 널리 사용되는 패키지에서 이 문제가 살아남은 이유입니다.

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()는 Laravel의 유효성 검사 API입니다. asc/desc 이외의 값을 주면 다음과 같은 오류가 발생합니다:``` InvalidArgumentException: Order direction must be "asc" or "desc".

So on the normal path — user clicks a column header, `sortField=name`, `sortDirection=<payload>` — the framework blocks the injection. A quick audit stops here and concludes "mitigated by Laravel".

**`ColumnRawQueries` pipeline** — the second stage, shown above — has **no such guard**. Look at its `handle()` (lines 21–27): it iterates the columns, and for every column carrying `rawQueries` it applies them *unconditionally*. It never consults `sortField`. It never consults the `Sorting` pipeline's outcome.
도구 다운로드