
प्रूफ-ऑफ-कॉन्सेप्ट और तकनीकी लेख CVE-2026-65971 के लिए — power-components/livewire-powergrid (< 6.10.4) में sortDirection Livewire प्रॉपर्टी के माध्यम से SQL इंजेक्शन
sortDirection के माध्यम से SQL इंजेक्शनCVE-2026-65971 / GHSA-7fgc-3h6c-698r के लिए प्रूफ-ऑफ-कॉन्सेप्ट और पूर्ण तकनीकी विवरण,
यह power-components/livewire-powergrid में सार्वजनिक Livewire प्रॉपर्टी sortDirection के माध्यम से पहुँचा जा सकने वाला एक SQL इंजेक्शन है।
| CVE | CVE-2026-65971 |
| GHSA | GHSA-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 स्टार, Laravel एडमिन पैनल में व्यापक रूप से उपयोग किया जाता है)। इसकी सॉर्ट स्थिति दो **सार्वजनिक Livewire गुणों** में रहती है:```php
public string $sortField = 'id';
public string $sortDirection = 'asc';
In Livewire में, एक सार्वजनिक प्रॉपर्टी कंपोनेंट के wire प्रारूप का हिस्सा होती है — कोई भी क्लाइंट जो कंपोनेंट तक पहुँच सकता है, उसे POST /livewire/update के माध्यम से सेट कर सकता है। यह डिज़ाइन द्वारा है; सुरक्षा सीमा यह है कि सर्वर उस मान के साथ क्या करता है।
PowerGrid की naturalSort() सुविधा एक raw ORDER BY एक्सप्रेशन बनाती है जिसमें literal प्लेसहोल्डर {sortDirection} होता है, और एक पाइपलाइन उस प्लेसहोल्डर को कच्चे, बिना मान्यता वाले प्रॉपर्टी मान से बदल देती है, इससे पहले कि स्ट्रिंग को orderByRaw() को सौंपा जाए। इसलिए दिशा कीवर्ड SQL के अंदर शब्दशः जा पहुँचता है।
Laravel का अपना orderBy() किसी भी ऐसी चीज़ को अस्वीकार कर देता है जो asc/desc नहीं है, और यही मान्यता सामान्य सॉर्ट पथ को सुरक्षित बनाती है। बग यह है कि उसी क्लॉज़ तक पहुँचने का एक दूसरा, बिना मान्यता वाला पथ मौजूद है — और एक हमलावर मान्यता वाले पथ को पूरी तरह छोड़कर उस तक पहुँच सकता है (देखें बायपास)।
परिणाम: ORDER BY क्लॉज़ में मनमाना SQL, जो ब्लाइंड बूलियन/टाइम-बेस्ड ओरेकल के रूप में उपयोग किया जा सकता है, ताकि डेटाबेस उपयोगकर्ता जितना भी डेटा पढ़ सकता है, उसे पढ़ा जा सके।
कोई भी व्यक्ति जो naturalSort का उपयोग करने वाली PowerGrid टेबल तक पहुँच सकता है, वह डेटाबेस से मनमाना डेटा पढ़ सकता है — अन्य टेबल, पासवर्ड हैश, सत्र टोकन, API कुंजियाँ, क्रॉस-टेनेंट रिकॉर्ड — टाइम-बेस्ड / बूलियन ओरेकल का उपयोग करके।
SELECT कर सकता है, उसका पूर्ण पठन।; UPDATE ... निष्पादित नहीं होता। लेखन प्रभाव केवल उसी तक सीमित है जो एक सबक्वेरी ट्रिगर कर सकती है।SLEEP() और भारी सबक्वेरीज़ देता है — डेटाबेस थ्रेड्स को पिन करने के लिए आसानी से दुरुपयोग योग्य।आवश्यक विशेषाधिकार PR:L है क्योंकि एक डेटाटेबल सामान्यतः एप्लिकेशन प्रमाणीकरण के पीछे होता है। यदि प्रभावित टेबल किसी बिना प्रमाणीकरण वाले पृष्ठ पर प्रस्तुत होती है, तो PR:N के साथ पुनर्गणना करें → 8.2 उच्च।
तीन फ़ाइलें, तीन चरण। सभी संदर्भ भेद्य टैग v6.10.3 के हैं।
`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 में यह केवल lazy-loading bookkeeping संभालता है। यह मान का निरीक्षण कभी नहीं करता।
क्योंकि Livewire public properties को सीधे request से hydrate करता है, इस बिंदु पर sortDirection पूरी तरह से attacker-controlled है, एक arbitrary string के रूप में।
naturalSort() एक प्लेसहोल्डर स्थापित करता हैsrc/Providers/Macros.php, पंक्तियाँ 102–116 — naturalSort कॉलम मैक्रो:```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()` एक प्रति-ड्राइवर अभिव्यक्ति में रिज़ॉल्व होता है, जिसे
`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} को इंटरपोलेट करती है।
`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);
}
और निष्पादन, पंक्ति 52:```php
$query->{$method}($resolvedSql, $resolvedBindings); // $method === 'orderByRaw'
data_get($this->component, 'sortDirection') हमलावर की स्ट्रिंग लौटाता है, preg_replace_callback
उसे SQL टेक्स्ट में जोड़ता है, और orderByRaw() — जो अनुबंध के अनुसार अपने
तर्क को एस्केप नहीं करता — उसे डेटाबेस तक पहुँचाता है।
ठीक नीचे कड़वी विडंबना देखें: resolveBindings() (पंक्ति 69) मौजूद है, और naturalSort
'bindings' => [] घोषित करता है। सुरक्षित पैरामीटराइज़ेशन का तंत्र ठीक वहीं है। इसे
दिशा (direction) कीवर्ड के लिए उपयोग नहीं किया जा सकता — ORDER BY x ? मान्य SQL नहीं है,
दिशा कभी भी बाउंड पैरामीटर नहीं हो सकती — और यही कारण है कि दिशा कीवर्ड अनिवार्य रूप से
allowlist में होना चाहिए।