Back to Blog

~$5.98M Kaybedildi: Aztec, Raydium ve Daha Fazlası | BlockSec Haftalık

Code Auditing
June 17, 2026
12 min read
Key Insights

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
num_txs'in dahili olarak zorlandığını ancak genel girdi olarak sunulmadığını gösteren devre kodu
num_txs'in dahili olarak zorlandığını ancak genel girdi olarak sunulmadığını gösteren devre kodu

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:

numTxs meta verilerinin doğrulanmış proofData'ya dahil edilmediğini gösteren decodeProof
numTxs meta verilerinin doğrulanmış proofData'ya dahil edilmediğini gösteren decodeProof

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_txs değeri 2 olarak ayarlandı (her iki gerçek işlemle eşleşiyor), ancak çağrı verisi numTxs değ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

Phalcon Explorer ile Başlayın

Akıllıca Hareket Etmek için İşlemleri Derinlemesine İnceleyin

Şimdi ücretsiz deneyin

Bu 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 = 0 ve toplam supply = 0 değ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 supply değ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 = 1 ve lp_supply = 1 değerleriyle işleyici total_coin * 1 / 1 ve total_pc * 1 / 1 hesapladı; bu da her iki rezervin %100'üne eşitti (RAY/USDC havuzu için 893.700 USDC ve 66.837 RAY).
  • Adım 4: İşleyici saldırganın 1 tokenini yaktı ve tam rezervleri her iki havuz kasasından aktardı; bu da RAY/USDC havuzunu 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

Phalcon Security ile Başlayın

Her tehdidi tespit edin, önemli olanları uyarın ve saldırıları engelleyin.

Şimdi ücretsiz deneyin

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ı.

Best Security Auditor for Web3

Validate design, code, and business logic before launch. Aligned with the highest industry security standards.

BlockSec Audit