diff --git a/src/pentesting-web/deserialization/livewire-hydration-synthesizer-abuse.md b/src/pentesting-web/deserialization/livewire-hydration-synthesizer-abuse.md index dfb6c987d47..f50621f8d3c 100644 --- a/src/pentesting-web/deserialization/livewire-hydration-synthesizer-abuse.md +++ b/src/pentesting-web/deserialization/livewire-hydration-synthesizer-abuse.md @@ -79,7 +79,7 @@ The tool parses the captured snapshot, injects the gadget tuples, recomputes the ## CVE-2025-54068 – RCE without `APP_KEY` -According to the vendor advisory, the issue affects Livewire v3 (>= 3.0.0-beta.1 and < 3.6.3) and is unique to v3. +According to the vendor advisory, the issue affects Livewire v3 (>= 3.0.0-beta.1 and <= 3.6.3) and is unique to v3. `updates` are merged into component state **after** the snapshot checksum is validated. If a property inside the snapshot is (or becomes) a synthetic tuple, Livewire reuses its meta while hydrating the attacker-controlled update value: @@ -122,11 +122,42 @@ Key reasons this works: - `getMetaForPath()` trusts whichever synth metadata already existed for that property even if the attacker previously forced it to become a tuple via weak typing. - Recursion plus weak typing lets each nested array be interpreted as a brand new tuple, so arbitrary synth keys and arbitrary classes eventually reach hydration. +### High-value pre-auth target: Filament login forms + +Applications built on top of Livewire often expose an even easier pre-auth surface than a toy `public $count;` property. For example, Filament login pages commonly hydrate a weakly typed `$form` object that is already serialized as a `form` tuple in the snapshot. That removes the "scalar -> array -> `arr` tuple" setup step entirely: + +- The snapshot already contains something like `{"form":[{...},{"s":"form","class":"App\\Livewire\\Forms\\LoginForm"}]}`. +- An attacker can send `updates.form` with nested malicious tuples directly, because recursion will eventually reinterpret children such as `[payload, {"s":"clctn","class":"GuzzleHttp\\Psr7\\FnStream"}]`. +- This is why pre-auth Livewire entrypoints that expose `FormObjectSynth` objects are especially attractive: they already provide both instantiation and public-property assignment. + +### Patch analysis: preserve raw metadata during update recursion + +The fix introduces a dedicated `hydratePropertyUpdate()` path so nested update values no longer call generic `hydrate($child, ...)` on attacker-controlled children: + +```php +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); + }); +} +``` + +Security impact of the patch: + +- Nested updates are revalidated against the original raw snapshot path instead of trusting fresh attacker-supplied tuple metadata. +- Recursive hydration no longer lets children redefine `s` or `class` mid-flight. +- This blocks both arbitrary synthesizer switching and arbitrary class selection inside nested update arrays. + ## Livepyre – end-to-end exploitation [Livepyre](https://github.com/synacktiv/Livepyre) automates both the APP_KEY-less CVE and the signed-snapshot path: -- Fingerprints the deployed Livewire version by parsing `