Geçen hafta (2026/06/08 - 2026/06/14) Ethereum ve Solana genelinde yaklaşık 5,98 milyon dolar toplam kayıpla sonuçlanan 4 dikkat çekici olay tespit edildi. Aşağıdaki tablo öne çıkan olayları özetlemektedir:
| Tarih | Olay | Tür | Tahmini Kayıp |
|---|---|---|---|
| 2026/06/08 | Flooring Protocol | Tam Sayı Taşması | ~$900K |
| 2026/06/09 | Top Token | Yönetişim Saldırısı | ~$1.59M |
| 2026/06/10 | Raydium (Solana'da) | Girdi Doğrulaması Eksikliği | ~$1.34M |
| 2026/06/14 | Aztec | Girdi Doğrulaması Eksikliği | ~$2.15M |
- Aztec: Rollup'ın kanıt yolu ile L1 uzlaşma yolu arasındaki bir doğrulama açığı, ikisinin farklı işlem kümelerini işlemesine ve tutarsız durumlara ulaşmasına neden oldu.
- Raydium: Eksik bir doğrulama kontrolü, saldırganın LP token kullanım hesaplamasını manipüle etmesine ve dört havuzun tüm rezervlerini boşaltmasına olanak tanıdı.
Web3 için En İyi Güvenlik Denetçisi
Lansmanöncesinde tasarımı, kodu ve iş mantığını doğrulayın
Haftanın Öne Çıkanı: Aztec
Bu olayda, tek bir parametre sınırsız bırakıldığı için ZK kanıt doğrulayıcısı ve L1 uzlaşma mantığı farklı işlem kümelerini işledi. Kanıt ile uzlaşma arasındaki bu tutarsızlık sorunu, bu iki yolun ayrı kod olarak çalıştığı her rollup tasarımı için geçerlidir.
14 Haziran 2026'da Ethereum üzerinde gizlilik odaklı bir rollup olan Aztec Connect, yaklaşık 2,15 milyon dolarlık bir saldırıya uğradı [1]. Temel neden, doğrulanmış rollup işlem kümesi ile L1 uzlaşma işleme sınırı arasındaki uyumsuzluktu; bu durum ZK kanıt yolunun ve uzlaşma mantığının farklı işlem listelerini işlemesine yol açtı. Saldırgan, bu açığı rollup durumunda karşılıksız mevduat bakiyelerini kaydetmek için kullandı ve ardından bunları normal uzlaşma akışları üzerinden çekti.
Arka Plan
Aztec Connect, Ethereum üzerinde L2'de özel işlemlere olanak tanıyan gizlilik odaklı bir rollup'tır. Kullanıcı fonları L1'de başladığından, L2 Merkle ağacında notlar olarak temsil edilebilmesi için önce rollup işlemci sözleşmesine yatırılması gerekir.
Yatırma işlemi iki aşamada gerçekleşir:
Aşama 1: Kullanıcı depositPendingFunds() fonksiyonunu çağırır; bu fonksiyon increasePendingDepositBalance() aracılığıyla userPendingDeposits[assetId][owner] değerini artırır ve tokenleri RollupProcessor'a aktarır. Bu işlem L1'de bekleyen bir mevduat oluşturur.
function depositPendingFunds(uint256 _assetId, uint256 _amount, address _owner, bytes32 _proofHash) external {
increasePendingDepositBalance(_assetId, _owner, _amount);
// ... tokenleri sözleşmeye aktar
}
Aşama 2: Kullanıcı bir mevduat kanıtı gönderir; bu kanıt daha sonra bir rollup'a dahil edilir ve L2 durumuna eklenir. processRollup() çalıştığında, decodeProof() kodlanmış çağrı verisinden numTxs değerini okur ve bunu kodu çözülmüş kanıt verisiyle birlikte döndürür. Her ikisi de processRollupProof()'a aktarılır:
function processRollup(bytes calldata, bytes calldata _signatures) external {
(bytes memory proofData, uint256 numTxs, uint256 publicInputsHash) = decodeProof();
processRollupProof(proofData, _signatures, numTxs, publicInputsHash, rollupBeneficiary);
}
processRollupProof() içinde iki fonksiyon sırayla çağrılır. İlk olarak verifyProofAndUpdateState(), tüm kodu çözülmüş işlemlere karşı ZK kanıtını doğrular ve rollup durumunu günceller. Ardından processDepositsAndWithdrawals(), L1 uzlaşmasını yönetir; yalnızca ilk _numTxs slotunu iterasyonla geçer ve her mevduat için decreasePendingDepositBalance() çağırır (kullanıcı Aşama 1'de gerçekten fon yatırmadıysa bu çağrı geri döner; böylece rollup kredisini gerçek bir L1 transferine bağlar):
function processRollupProof(bytes memory _proofData, bytes memory _signatures,
uint256 _numTxs, uint256 _publicInputsHash, address _rollupBeneficiary) internal {
verifyProofAndUpdateState(_proofData, _publicInputsHash); // kanıt yolu: tüm kodu çözülmüş işlemler
processDepositsAndWithdrawals(_proofData, _numTxs, _signatures); // uzlaşma yolu: yalnızca ilk _numTxs
}
// processDepositsAndWithdrawals içinde:
end := add(proofDataPtr, mul(_numTxs, TX_PUBLIC_INPUT_LENGTH))
while (proofDataPtr < end) {
// ... her mevduat için:
decreasePendingDepositBalance(assetId, publicOwner, publicValue);
}
Bu iki aşamalı tasarım, L1 uzlaşma mantığının ZK kanıtının doğruladığı işlem kümesinin tam olarak aynısını işlemesini gerektirir. İki yol hangi işlemleri işleyeceği konusunda uyuşmazsa, bekleyen L1 bakiyeleri tüketilmeden rollup durumuna mevduat olarak kaydedilebilir.
Güvenlik Açığı Analizi
Rollup işlemci sözleşmesinde (0x7d65...2728), devre dahili olarak doğru num_txs değerini zorladı ancak bunu genel bir girdi olarak sunmadı. Solidity tarafı numTxs değerini doğrulanmamış çağrı verisi meta verilerinden okuduğundan, kanıt yolu ve uzlaşma yolu farklı işlem listelerini işleyebiliyordu.
Zincir dışı rollup_circuit'te num_txs, bir tanık olarak yüklenir ve hangi slotların gerçek işlem olarak değerlendirileceğini belirlemek için kullanılır. Devre, num_txs'in gerçek dolgu dışı kanıt sayısıyla eşleştiğini zorlar — bir slot gerçek bir kanıt içeriyorsa (sıfır olmayan veri kökü) ancak is_real yanlışsa (çünkü i >= num_txs), is_real.assert_equal(data_root_exists) kontrolü başarısız olur ve kanıt oluşturma durur. Ancak num_txs kanıtın genel girdisi olarak sunulmamaktadır:
const auto num_txs = uint32_ct(witness_ct(&composer, rollup.num_txs));
field_ct(num_txs).create_range_constraint(MAX_TXS_BIT_LENGTH);
// ...
auto is_real = num_txs > uint32_ct(&composer, i); // slot başına gerçek-tx mantığını kapılar
// ...
is_real.assert_equal(data_root_exists); // num_txs == gerçek tx sayısını zorlar

Solidity tarafında decodeProof(), verifyProofAndUpdateState() tarafından doğrulanan yeniden oluşturulmuş proofData'ya kopyalanmayan çağrı verisi meta verilerinden numTxs değerini okur. Devrenin dahili num_txs değeri genel bir girdi olmadığından, doğrulayıcının çağrı verisi numTxs değerinin devre içinde kanıtlanan değerle eşleşip eşleşmediğini kontrol etme imkânı yoktur:

Bu nedenle bir saldırgan, çağrı verisi numTxs değerini devrenin kanıtladığı num_txs değerinden düşük ayarlayabilirdi. Uzlaşma döngüsü, kanıtın rollup durumuna kaydettiği işlemleri atlardı. Eyleme dönüştürülemeyen bir işlem ilk kodu çözülmüş slotu işgal edebilirken (uzlaşma tarama aralığı içinde), gerçek bir mevduat sonraki bir slotta yer alabilirdi (devre tarafından kanıtlanmış ancak uzlaşma tarama aralığı dışında). Kanıt, mevduatı rollup durumuna kaydeder; ancak uzlaşma mantığı decreasePendingDepositBalance() çağrısı dahil olmak üzere bu işlemi tamamen atlardı. Bu durum, rollup durumu mevduatı zaten yansıtırken L1'deki bekleyen mevduat bakiyesini tüketilmemiş halde bıraktı.
Saldırı Analizi
Aşağıdaki analiz, 0x074ec9...9aeeb1 işlemine dayanmaktadır.
Saldırgan, kanıt yolu ile uzlaşma yolu arasındaki açığı iki aşamada kullandı.
Aşama 1: Karşılıksız bakiyeler oluşturma
-
Adım 1: Saldırgan, her biri iki kodu çözülmüş işlem içeren birden fazla rollup paketi gönderdi: slot 1'de eyleme dönüştürülemeyen (sahte) bir işlem ve slot 2'de gerçek bir mevduat. Devrenin
num_txsdeğeri 2 olarak ayarlandı (her iki gerçek işlemle eşleşiyor), ancak çağrı verisinumTxsdeğeri 1 olarak ayarlandı. L1 uzlaşma mantığı yalnızca slot 1'deki sahte işlemi işledi ve slot 2'deki gerçek mevduatı tamamen atladı. -
Adım 2: Ancak ZK kanıtı, slot 2'deki mevduat dahil tüm kodu çözülmüş işlemleri doğruladı ve kaydetti. Uzlaşma mantığı bu mevduata hiç ulaşmadığından
decreasePendingDepositBalance()çağrılmadı ve L1 bekleyen mevduat bakiyesi tüketilmemiş kaldı. Saldırgan bu kalıbı yedi farklı varlık için tekrarlayarak rollup durumunda karşılıksız bakiyeler oluşturdu.
Aşama 2: Fonları çıkarma
- Adım 3: Yedi karşılıksız bakiye oluşturulduktan sonra, saldırgan her varlık için standart para çekme işlemlerini başlattı. Bu para çekme işlemleri, bakiyeler rollup durumunda mevcut olduğundan uzlaşma mantığına meşru göründü; böylece L1 sözleşmesi karşılık gelen fonları —toplamda yaklaşık 2,15 milyon dolar— serbest bıraktı.

Sonuç
Bu güvenlik açığı kriptografik bir zayıflık değil, rollup mimarisindeki iki kritik kod yolu arasında bir durum tutarsızlığı hatasıydı. Temel neden şudur: devre num_txs'in gerçek işlem sayısıyla eşleştiğini dahili olarak zorlarken, bu değer genel bir girdi olarak sunulmadı. Solidity çözücüsü numTxs değerini, kanıtlanan değerle karşılaştırma imkânı olmaksızın doğrulanmamış çağrı verisi meta verilerinden okudu. Saldırgan, uzlaşma mantığının kanıtın rollup durumuna zaten kaydettiği mevduatları atlaması için çağrı verisi numTxs değerini devrenin kanıtladığı sayıdan düşük ayarladı. Ortaya çıkan karşılıksız bakiyeler daha sonra normal uzlaşma akışları aracılığıyla çekildi.
Güvenlik açığı, Aztec Connect'in kullanım süresi dolmadan önce mevcuttu. Rollup, işlem işleme ve para çekme işlemlerinin 31 Mart 2024'e kadar sona ereceğini duyurarak bir kullanım sonu bildirimi yaptı [2]. Ancak rollup işlemci sözleşmesi, 10 Nisan 2024'te bir çekme isteği aracılığıyla yükseltildi [3]; bu durum kanıt gönderimini tüm kullanıcılara açtı ve önceden var olan güvenlik açığını herkes tarafından istismar edilebilir hale getirdi.
Düzeltme, numTxs'i ZK kanıtı tarafından doğrulanan tam işlem kümesine bağlamayı gerektirir; böylece her iki yol da her zaman aynı kümeyi işler. Kanıt doğrulamasını L1 uzlaşmasından ayıran herhangi bir rollup tasarımı, her iki yolun da özdeş, doğrulanabilir şekilde sınırlandırılmış bir işlem kümesi üzerinde çalışmasını sağlamalıdır. Tek bir parametredeki tutarsızlık bile sağlam bir kanıt sistemini karşılıksız bakiye oluşturma vektörüne dönüştürebilir.
Referanslar
- [1] BlockSec Phalcon Uyarısı: Aztec Olay Analizi
- [2] Aztec Connect Kullanım Sonu Bildirimi
- [3] RollupProcessorV3 Yükseltme PR #67
Phalcon Explorer ile Başlayın
Akıllıca Hareket Etmek için İşlemleri Derinlemesine İnceleyin
Şimdi ücretsiz deneyinBu Haftaki Diğer Olaylar
Raydium
10 Haziran 2026'da Solana üzerindeki Raydium'un eski AMM v3 programındaki dört havuz yaklaşık 1,34 milyon dolarlık bir saldırıya uğradı [1]. Para çekme işleyicisi, çağıran tarafından sağlanan bir hesabın havuzun kayıtlı karşılığıyla eşleşip eşleşmediğini doğrulamadığından, saldırgan ödeme hesaplamasını manipüle etmek için kontrol ettiği bir hesapla ikame etti. Aynı teknik, dört havuzun tüm rezervlerini saniyeler içinde boşalttı.
Arka Plan
Raydium'un AMM'si, Solana üzerinde sabit çarpımlı bir piyasa yapıcısıdır. Her havuz iki token kasası tutar ve rezervlerin orantılı bir payını temsil eden bir LP token basar. Bir likidite sağlayıcısı para çektiğinde, işleyici ödemeyi orantılı olarak hesaplar ve her iki kasanın ilgili payını aktarır:
coin_out = total_coin * withdraw_amount / lp_supply
pc_out = total_pc * withdraw_amount / lp_supply
Solana'da her token türü, toplam arzı, ondalık basamakları ve basım yetkisini depolayan bir Mint hesabıyla tanımlanır. Her sahibin bakiyesi, o Mint'e bağlı ayrı bir Token hesabında saklanır — bir Mint'in farklı sahiplerde pek çok Token hesabı olabilir. Bu durum, tek bir ERC-20 sözleşmesinin hem token tanımını hem de tüm bakiyeleri dahili olarak yönettiği EVM'den farklıdır.
Yukarıdaki para çekme formülünde lp_supply, toplam LP arzını izleyen havuzun LP Mint hesabından okunur. Hesaplamanın doğruluğu bu değerin gerçek LP Mint'i olmasına bağlıdır. Ancak Solana'da çağıran her hesabı her talimata konumsal olarak iletir; bu nedenle işleyici, çağıran tarafından sağlanan her hesabın havuz durumunda depolanan kurallı hesapla eşleştiğini doğrulamalıdır.
Güvenlik Açığı Analizi
İstismar edilen program (27haf8...8vQv) açık kaynaklı değildi ve yürütülebilir verileri (ProgramData) saldırının ardından kapatıldı; bu da doğrudan bayt kodu incelemesini imkânsız kıldı. Aşağıdaki analiz, programın son yükseltme arabelleğinden yeniden oluşturulan bayt kodu temel alınarak ve zincir üstü işlem davranışıyla çapraz referanslanarak hazırlanmıştır.
Para çekme işleyicisinde, çağıran tarafından iletilen LP Mint hesabı havuzun kayıtlı amm.lp_mint değerine bağlanmamıştı. Zincir üstü bayt kodundan yeniden oluşturulan aşağıdaki tersine mühendislik sözde kodu hesap düzenini göstermektedir. İşleyici havuz durumu, PDA yetkisi, her iki kasa ve kullanıcı hesapları için bağlamaları kontrol etti —ancak slot 5'teki LP Mint için kontrol etmedi:
let amm_info = next_account_info(it)?; // accounts[1] — havuz durumu (amm.lp_mint tutar)
// ...
let amm_lp_mint_info = next_account_info(it)?; // accounts[5] — çağıran tarafından sağlanan mint
let amm = AmmInfo::load(amm_info)?;
// yetki, kasalar, açık emirler bağlamaları burada kontrol edildi...
// >>> EKSİK: accounts[5].key == amm.lp_mint kontrolü <<<
let lp_mint = Mint::unpack(&amm_lp_mint_info.data.borrow())?;
let lp_mint_supply = lp_mint.supply; // doğrulanmamış mint'ten okunur
let coin_amount = total_coin * withdraw_amount / lp_mint_supply;
let pc_amount = total_pc * withdraw_amount / lp_mint_supply;
LP Mint hesabı bağlanmadığından, bir saldırgan tamamen kontrol ettiği bir Mint hesabını ikame edebilirdi. Toplam supply değerini 1 olarak ayarlamak ve 1 token yakmak, her rezervin 1 / 1 = %100'ü oranında bir ödeme oranı sağladı.
Güvenlik açığı içeren kod, programın 3 Ocak 2023'teki son yükseltmesinden bu yana aktif ve değiştirilmemiş durumdaydı; bu, istismardan yaklaşık 1.254 gün öncesine denk geliyor.
Saldırı Analizi
Aşağıdaki analiz, 1csN6v...3s7s işlemine dayanmaktadır.
- Adım 1: Saldırgan,
decimals = 0ve toplamsupply = 0değerleriyle sahte bir LP Mint hesabı oluşturdu.

- Adım 2: Saldırgan, sahte LP Mint'e bağlı bir Token hesabı oluşturdu, ardından Mint yetkisi olarak tam olarak 1 token bastı ve Mint'in toplam
supplydeğerini 1 olarak sabitledi.

- Adım 3: Saldırgan para çekme fonksiyonunu çağırırken beklenen hesap slotuna sahte LP Mint'i ve LP kaynağı olarak Adım 2'deki Token hesabını (1 sahte LP token içeren) geçirdi.
withdraw_amount = 1velp_supply = 1değerleriyle işleyicitotal_coin * 1 / 1vetotal_pc * 1 / 1hesapladı; bu da her iki rezervin %100'üne eşitti (RAY/USDChavuzu için 893.700USDCve 66.837RAY).

- Adım 4: İşleyici saldırganın 1 tokenini yaktı ve tam rezervleri her iki havuz kasasından aktardı; bu da RAY/
USDChavuzunu tamamen boşalttı.

Saldırgan yaklaşık 15 saniye içinde üç havuza daha aynı kalıbı uyguladı. Dört havuzun tamamında boşaltılan miktarlar şöyledir:
| Havuz | Boşaltılan (yaklaşık) |
|---|---|
| RAY/USDC | ~66.837 RAY + ~893.700 USDC |
| RAY/wSOL | ~74.720 RAY + ~5.603 wSOL |
| RAY/SRM | ~8.622 RAY + ~10.692 SRM |
| RAY/Sollet ETH | ~5.038 RAY + ~16 Sollet ETH |
Sonuç
Temel neden, tek bir eksik hesap doğrulama kontrolüdür: para çekme işleyicisi, çağıran tarafından sağlanan Mint hesabının supply değerini, havuzun kayıtlı amm.lp_mint değerine bağlamadan LP arzı böleni olarak kullandı. Solana'da çağıran tarafından sağlanan her hesabın havuz durumunda depolanan kurallı karşılığına bağlanması gerekir. Doğru bir uygulama, anahtarı havuzun kayıtlı kaydıyla eşleşmeyen herhangi bir LP Mint'i reddetmeli ve kullanım tutarını dışarıdan sağlanan Mint'in supply değerinden değil, havuz içi bir LP sayacından hesaplamalıdır. İstismar edilen sözleşme, saldırıyla aynı gün kapatılan eski bir dağıtımdı (son yükseltme Ocak 2023). Raydium ekibine göre, tam tazminat Raydium'un hazinesi tarafından karşılanacaktır [1].
Referanslar
BlockSec Hakkında
BlockSec, tam yığın blok zinciri güvenliği ve kripto uyum sağlayıcısıdır. Müşterilerin kod denetimi gerçekleştirmesine (akıllı sözleşmeler, blok zinciri ve cüzdanlar dahil), saldırıları gerçek zamanlı olarak önlemesine, olayları analiz etmesine, yasadışı fonları takip etmesine ve protokollerin ve platformların tam yaşam döngüsü boyunca AML/CFT yükümlülüklerini karşılamasına yardımcı olan ürün ve hizmetler geliştiriyoruz.
BlockSec, prestijli konferanslarda birden fazla blok zinciri güvenlik makalesi yayımladı, DeFi uygulamalarına yönelik birçok sıfır gün saldırısını raporladı, 20 milyondan fazla doları kurtarmak için birden fazla saldırıyı engelledi ve milyarlarca dolarlık kripto para birimini güvence altına aldı.
-
Resmi web sitesi: https://blocksec.com/
-
Resmi Twitter hesabı: https://twitter.com/BlockSecTeam



