deniz.in

Piyasalar

Hava durumu

Hava durumu yükleniyor

· kaynak dev.to (home feed)

Güncellenmeyen transient hash sayesinde saldırgan sahte bir Merkle root kaydederek zkLink Nova'dan yaklaşık 780 bin dolar çekti

Tek bir transaction, sıfırlanmamış bir transient-storage hash'i sayesinde saldırgan sahte bir batch root kaydedip kendi ürettiği kanıtlarla para çekerek zkLink Nova'nın Ethereum escrow'larından yaklaşık 780 bin doları boşalttı.

Güncellenmeyen transient hash sayesinde saldırgan sahte bir Merkle root kaydederek zkLink Nova'dan yaklaşık 780 bin dolar çekti

Tek bir transaction escrow'ları boşalttı

28 Ekim 2026 saat 22:21:35 UTC'de, Ethereum bloğu 26.294.188 içinde tek bir transaction, agregasyon yapan rollup zkLink Nova'nın Ethereum escrow'larını boşalttı. dev.to'da yayımlanan bir incelemeye göre saldırgan 22,43 ETH ve yaklaşık 245,74 pufETH başta olmak üzere on bir ERC-20 token ile kaçtı. Ganimetin değeri kaynağa göre değişiyor: tahminler yaklaşık 743 bin dolar ile 805 bin dolar arasında; BlockSec Phalcon yaklaşık 780 bin dolar, SlowMist ise yaklaşık 783,1 bin dolar veriyor.

Hiçbir anahtar çalınmadı, hiçbir imza taklit edilmedi. Saldırgan kendi seçtiği bir Merkle root'u köprüye yazdı ve ardından kendi oluşturduğu kanıtlarla para çekti. Rapora göre zkLink'in yanıtı, Nova üzerinde blok üretimini durdurmak, etkilenen contract'ları acil olarak yamalamak ve çekimleri geçici olarak devre dışı bırakmak oldu. Diğer zincirlerde de ardından gelen saldırılar bildirildi.

Batch root'lar Ethereum'a nasıl ulaşır

Her Nova escrow'u, L2'den L1'e giden mesajların her batch'i için tek bir Merkle root saklar; Ethereum'daki bir çekim ancak kanıtı o batch için saklanan root ile eşleşiyorsa geçerlidir. Root'u yazabilen kişi herhangi bir çekimi onaylayabilir.

Root'lar doğrudan gelmez. Herkesin çağırabileceği beyaz listeye alınmış bir relayer contract olan SyncL2TxHashRelayer, zincirler arası mesaj yönlendiricisi Arbitrator'ı tetikler. Arbitrator, kaynak zincirin kanonik servisinden bir mesaj talep eder (Nova durumunda Linea'nın L1MessageService'i), kaynak gateway gerçekten teslim edileni kaydetmek için Arbitrator.receiveMessage() içine geri çağrı yapar ve Arbitrator mesajı EthereumGateway gibi hedef gateway'lere iletir. onlyArbitrator ile güvenilen bu gateway, root'u yazmak için onlyGateway ile güvenilen ZkLink.syncBatchRoot() fonksiyonunu çağırır. Her adım, bir öncekini yalnızca msg.sender üzerinden güvenir.

claimMessage içinde birleşen iki açık

zkLink'in herkese açık zklink-evm-contracts deposuna dayanan dev.to analizi, hatayı onlyRelayer ve nonReentrant korumalı Arbitrator.claimMessage() içinde konumlandırıyor.

Birincisi, fonksiyon hedef adresi de calldata'yı da çağıranın seçtiği parametrelerle Address.functionCall() çalıştırıyor. Bu çağrı Arbitrator içinden geldiği için, çağrılan taraf msg.sender olarak Arbitrator'ın adresini görüyor — yani her gateway'in güvendiği kimliğin ta kendisi. Bir relayer, mesaj-servisi parametresini doğrudan EthereumGateway olarak gösterebilir ve hem gateway'in hem ZkLink'in kabul edeceği sarmalanmış bir syncBatchRoot çağrısını çalıştırabilirdi.

İkincisi, tam bunu önlemek için tasarlanmış güvenlik mekanizması, EIP-1153 transient storage'da tutulan finalizeMessageHash adlı bir hash'ti. Gerçek bir mesaj ulaştığında receiveMessage() bu hash'i yazar, claimMessage() ise harici çağrıdan sonra kontrol eder. Ancak transient storage tüm transaction boyunca kalıcıdır ve slot hiç temizlenmez. Bu nedenle kontrol, bu claim sırasında gerçek bir teslimatın yaşandığını değil, yalnızca aynı transaction içinde daha önceki bir noktada bir teslimat yaşandığını gösterir. nonReentrant koruması buna karşı işe yaramaz çünkü iki claim, iç içe geçmiş re-entrancy değil, saldırgan bir contract'tan gelen ardışık çağrılardır.

Saldırı adım adım

BlockSec Phalcon ve SlowMist, transaction makbuzundaki transferlerle de örtüşen aynı diziyi anlatıyor:

  • Saldırgan önce herkese açık relayer'ı çağırarak iki L2 transaction hash'ini senkronize eden gerçek, bekleyen bir Linea mesajını claim etti. Linea kanıtı doğruladı, receiveMessage() transient hash'i kurdu, meşru mesaj iletildi ve slot dolu kaldı.

  • Aynı transaction içindeki ikinci relayer çağrısı hedef olarak EthereumGateway'i gösterdi ve calldata, syncBatchRoot(116409, attackerChosenRoot, 0) sarmalıyordu. Gateway çağıranın Arbitrator olduğu için bunu çalıştırdı; ZkLink, çağıranın kendi gateway'i olduğu için saldırganın root'unu kaydetti.

  • Gateway receiveMessage() çağırmadığı için slot'u üzerine yazan bir şey olmadı. Saldırgan, ilk çağrıyla aynı gateway'i, değeri ve calldata'yı yeniden kullanarak yeniden hesaplanan hash'i eski hash ile eşleştirdi ve sahte root hayatta kaldı.

  • Çekimleri finalize etmek izin gerektirmez ve yalnızca saklanan root'u kontrol eder; bu yüzden saldırgan on iki kanıt gönderdi — ZkLink escrow'sundan bir ETH çekimi ve L1ERC20Bridge'den on bir token çekimi — hepsi kendi adresine ödendi.

Token transferleri arasında 18.113,45 USDC, 3.360,90 USDT, 12,57 ezETH, 6,87 rsETH, 6,30 STONE ve daha küçük miktarlarda mstETH, mswETH, rswETH, wstETH ve WBTC yer aldı. Değerin büyük kısmını liquid restaking token'ları oluşturdu; en büyük tek kayıp pufETH oldu.

Neden önemli

İki açık tek başına yeterli olmazdı; birlikte, izin gerektirmeyen bir giriş noktasının köprünün genuine sanacağı bir güven üretmesine olanak tanıdı. Olay, rollup ve köprü tasarımcılarına iki doğrudan ders içeriyor. EIP-1153 transient storage çağırma değil, transaction kapsamlıdır; bu yüzden orada tutulan doğrulama durumu açıkça temizlenmelidir, yoksa tek bir eylemi kapsaması amaçlanan bir kontrol, aynı transaction içinde daha sonra gelen başka bir eylem tarafından yeniden oynatılabilir. Ayrıca çağıranın verdiği hedefi çağıran ayrıcalıklı bir fonksiyon, kendi güvenilir kimliğini öbür uçta çalışan her şeye ödünç verir; bu da arkasındaki her onlyArbitrator tarzı kontrolü açık bir kapıya dönüştürür. Yalnızca msg.sender üzerine kurulu güven zincirleri, ancak ortadaki router kadar güçlüdür.

  • #ethereum
  • #security
  • #smart-contracts
  • #bridges
  • #defi

İlgili yazılar