Geçen hafta (2026/07/20 - 2026/07/26) boyunca, toplam yaklaşık 39,5 milyon dolarlık kayıpla ilgili aşağıdaki 8 önemli güvenlik olayı öne çıkmaktadır.
| Tarih | Olay | Tür | Tahmini Kayıp |
|---|---|---|---|
| 2026/07/19* | Allbridge | Hatalı Girdi Doğrulaması | ~$1.65M |
| 2026/07/20 | Zilliqa | Hatalı Nonce Üretimi | ~$400K |
| 2026/07/20 | Wanchain | Hatalı Mesaj Kodlaması | ~$500K |
| 2026/07/22 | 42DAO | Özel Anahtar Ele Geçirilmesi | ~$900K |
| 2026/07/22 | AFX Trade | Özel Anahtar Ele Geçirilmesi | ~$24.15M |
| 2026/07/22 | B² Network | Özel Anahtar Ele Geçirilmesi | ~$3.8M |
| 2026/07/23 | Verus | Özel Anahtar Ele Geçirilmesi | ~$7.6M |
| 2026/07/24 | Lien Finance | Hatalı Doğrulama Mantığı | ~$542K |
*Allbridge olayı 19 Temmuz'da (17:51 UTC) yaşanmış olup geçen haftaki raporda yer almamıştır. Eksiksizlik açısından buraya dahil edilmiştir.
- Zilliqa, zincir dışı cüzdan uygulamasında uzun süre tespit edilemeyen ve 2019'dan bu yana eski yerel ZIL hesabı kullanıcılarını özel anahtar ele geçirilme riskiyle karşı karşıya bırakan bir açığı gün yüzüne çıkardığı için seçilmiştir.
- Allbridge, Solana'nın konumsal hesap modeli tarafından mümkün kılınan bir hesap takma adı (account-aliasing) açığını gözler önüne serdiği için seçilmiştir. Kaynak kodu mevcut olmadığından açığın dağıtılmış program ikilisinden yeniden oluşturulması gerekmiştir.
- Wanchain, bir köprünün kriptografi tehlikeye atılmadan nasıl tamamen boşaltılabileceğini gösterdiği için seçilmiştir. İmzalar geçerli ve doğru şekilde doğrulanmıştı; ancak enjektif olmayan, sınırlayıcı içermeyen mesaj kodlaması, 3.097,56 token için verilen bir yetkinin Cardano üzerinde 203.001.692,164714 token olarak yeniden yorumlanmasına izin vermiştir.
- Lien Finance, eşleşen öğelerin kimliğini ve çokluğunu kontrol etmeksizin bakiyelerin sayılarla örtüşüp örtüşmediğini doğrulayan bir mekanizmanın nasıl atlatılabileceğini örneklediği için seçilmiştir.
Web3 için En İyi Güvenlik Denetçisi
Lansmanı yapmadan önce tasarımı, kodu ve iş mantığını doğrulayın
Haftanın Öne Çıkanı: Allbridge Core
Bu olay, hesap takma adı (account-aliasing) örüntüsünün aynı değiştirilebilir hesabı iki talimat rolünde kabul eden herhangi bir Solana programına uygulanabilmesi nedeniyle öne çıkarılmıştır. Temel mekanizma — aynı durumun iki değiştirilebilir görünümünün bulunduğu ve ikinci yazmanın birincisini sessizce üzerine yazdığı durum — klasik ERC-20 öz-transfer hatasıyla (from == to olduğu transferFrom) aynı yapıya sahiptir. Projenin olay sonrası analizi [1] üst düzey bir özet sunmakta ancak kod düzeyindeki mekanizmayı ayrıntılı biçimde ele almamaktadır. Aşağıdaki analiz, bu boşluğu doldurmak amacıyla dağıtılmış program ikilisinden yeniden oluşturulmuştur.
19 Temmuz 2026'da (17:51 UTC), Solana üzerinde faaliyet gösteren bir zincirler arası köprü protokolü olan Allbridge Core, yaklaşık 1,65 milyon dolarlık (~1,12M USDC ve ~539K USDT) bir saldırıya uğramıştır. Temel neden, swap talimatının aynı Pool hesabını hem gönderme hem de alma rolünde kabul etmesi ve ikisinin farklı olması gerektiğini zorunlu kılmamasıydı. Aynı hesap iki kez iletildiğinde, bir rolden gelen dahili muhasebe güncellemeleri diğeri tarafından sessizce üzerine yazılırken gerçek token transferleri zaten tamamlanmıştı. Beş öz-swap işlemi, Pool'un kayıtlı durumunu yeterince bozarak saldırganın küçük bir USDT girdisini ~2,24M USDC'ye dönüştürmesine olanak tanıdı.
Arka Plan
Allbridge Core, takasları vUSD adı verilen dahili bir muhasebe birimi üzerinden yönlendiren bir zincirler arası köprüdür. Bir swap iki muhasebe aşamasından oluşur:
kaynak token -- swap_to_v_usd(gönderme_havuzu) --> vUSD
vUSD -- swap_from_v_usd(alma_havuzu) --> hedef token
vUSD bir SPL token değildir. Yalnızca her Pool'un zincir üstü verisindeki bir alan olarak mevcuttur. Her Pool, token_balance, v_usd_balance ve reserves değerlerini takip eder. Gönderme aşaması, kaynak tokenı kullanıcıdan gönderme köprü kasasına aktarır, gönderme Pool'unun token taraflı muhasebesini artırır ve vUSD çıktısını hesaplar. Alma aşaması ise bu vUSD'yi alma Pool'una ekler, token taraflı muhasebesini azaltır ve hedef tokenı alma köprü kasasından kullanıcıya aktarır.
Solana programları, hesaplarını çağırandan konumsal bir dizi olarak alır. Programın talimat işleyicisi hangi dizi konumlarının hangi rollere karşılık geldiğini belirtir (send_pool, receive_pool, send_mint vb.), ancak çalışma zamanı çağıranın iki konuma aynı hesap adresini iletmesini engellemez. Program hesapları iki farklı konumda seri dışı bıraktığında (deserialize ettiğinde), her seri dışı bırakma işlemi ayrı bir bellek içi nesne üretir. Bir nesnede yapılan değişiklikler diğerini etkilemez. Aynı hesap iki kez iletildiğinde her iki nesne de aynı zincir üstü veriyle başlar; ancak herhangi biri değiştirilir değiştirilmez birbirinden ayrışırlar.
İşleyici tamamlandığında, programın çıkış mantığı (örn. Anchor'ın AccountsExit'i) her hesap nesnesini sabit bir sırayla hesabın verisine geri serileştirir. İki nesne aynı hesapla eşleniyorsa ikinci serileştirme birincinin üzerine tamamen yazar.
Açık Analizi
Etkilenen Allbridge Core dağıtımının tam kaynak kodu mevcut değildi. Analiz, ProgramData'da depolanan 1.770.736 baytlık ELF'den yeniden oluşturulmuştur (SHA-256: 40f776...346bb6). Son dağıtım slotu (204.727.029), saldırı slotunun (433.941.722) öncesindedir.
Hatalı program BrdgN2...ceWB adresidir. Saldırı işleminin Swap talimatlarında (3'ten 7'ye kadar olan talimatlar), dört gönderme ve alma hesap çifti özdeşti:
| Roller | Hesap |
|---|---|
send_mint / receive_mint |
Es9vMF...wNYB |
send_pool / receive_pool |
DW4a2E...wCX |
send_bridge_token / receive_bridge_token |
2xY9TD...vohV |
send_user_token / receive_user_token |
817UdW...CVct |
Ayrıştırıcı, her rolü beklenen mint, kasa, sahip, PDA, yetki ya da Token programına bağlayan 16 adet kurtarılmış 32 baytlık karşılaştırma içermektedir. Ancak send_pool.key() != receive_pool.key() koşulunu zorunlu kılan herhangi bir karşılaştırma bulunmamaktadır. İki Pool rolü, işleyici çalışmadan önce bağımsız olarak seri dışı bırakılmaktadır.
Dağıtılan ELF, sabit bir sırayla iki Pool yapısı, gönderme ve alma hesaplamaları ile iki Pool çıkışını göstermektedir:
L54249-L54252 function_9881(accounts[5]) -> yerel gönderme_havuzu
L54294-L54297 function_9881(accounts[6]) -> yerel alma_havuzu
L74177-L74195 function_12623(send_pool) -> swap_to_v_usd
L74368-L74389 function_12940(receive_pool) -> swap_from_v_usd
L55932-L55936 function_8300(send_pool) -> tam Pool serileştirme
L55940-L55944 function_8300(receive_pool) -> tam Pool serileştirme
Her function_9881 çağrısı ayrı bir 176 baytlık yerel Pool nesnesi tahsis eder ve kodu çözülmüş alanları buna kopyalar. Hesap anahtarları eşit olduğunda her iki nesne de aynı veriyle başlar; ancak gönderme nesnesinde yapılan bir değişiklik alma nesnesini etkilemez.
Yeniden oluşturulmuş sözde-Rust kodu (dağıtılan davranış, kurtarılan kaynak değil) [2] [3]:
let mut send_pool = Account::<Pool>::try_from(&accounts[5])?;
let mut receive_pool = Account::<Pool>::try_from(&accounts[6])?;
require_keys_eq!(send_pool.mint, send_mint.key());
require_keys_eq!(receive_pool.mint, receive_mint.key());
// Kasa, sahip, PDA, yetki ve Token programı bağlantıları kontrol edilmektedir.
// EKSİK: require_keys_neq!(send_pool.key(), receive_pool.key());
token::transfer(send_user_token, send_bridge_token, amount)?;
let v_usd = swap_to_v_usd(&mut send_pool, amount)?;
let receive_amount =
swap_from_v_usd(&mut receive_pool, v_usd, receive_amount_min)?;
token::transfer(receive_bridge_token, receive_user_token, receive_amount)?;
// İşleyici döndükten sonra AccountsExit:
send_pool.exit(program_id)?; // birinci yazma
receive_pool.exit(program_id)?; // ikinci yazma, birincinin üzerine yazar
function_8300, yazıcısını 0. konumdan başlatır ve tanımlı 131 Pool baytının tamamını serileştirir. Birinci çıkış send_pool'u, ikinci çıkış ise receive_pool'u aynı baytların üzerine yazar. Dolayısıyla nihai hesap durumu, iki yerel güncellemenin birleşimi değil, yalnızca eskimiş alma tarafı değeridir.
SPL token transferleri ayrı CPI'lardır. Pool çıkışlarından önce SPL Token programına ait kasa hesaplarını güncellerler; bu nedenle ikinci Pool serileştirmesi girdi transferini geri alamaz. Her takma adlı swap, transfer edilen tokenleri kasada bırakırken karşılık gelen gönderme tarafı Pool güncellemesini atar. Alma tarafı güncellemesi ise kalır ve Pool'un kayıtlı durumunu daha yüksek v_usd_balance ve daha düşük token tarafı muhasebesine doğru iter.
Temel kusur, programın send_pool ve receive_pool'un farklı hesaplar olduğunu kontrol etmemesidir. Diğer tüm doğrulamalar (mint bağlantısı, kasa sahipliği, PDA türetme) geçerlidir; çünkü her iki rol de aynı Pool'a meşru olarak aittir.
Saldırı Analizi
Aşağıdaki analiz, 3LNLaG...Y39Q işlemine dayanmaktadır.
Saldırı, bir Kamino flaş borçlanması, yedi Allbridge Swap talimatı ve bir Kamino geri ödemesini içeren tek bir işlemde tamamlandı.
- Adım 1: Saldırgan Kamino'dan ~1,12M
USDCödünç aldı ve Allbridge üzerinden ~949KUSDTiçin swap yaptı (talimatlar 1-2). Bu ilk swap, sonraki öz-swaplar için gerekenUSDT'yi sağladı ve hemUSDChem deUSDTPool durumlarını değiştirdi.

- Adım 2: Saldırgan, hem gönderme hem de alma için özdeş mint, Pool, kasa ve kullanıcı token hesaplarını kullanarak beş adet ~100K
USDTöz-swap işlemi gerçekleştirdi (talimatlar 3-7). Alma Pool'u her çağrıda son olarak serileştirildiğinden, kalıcı muhasebe alma tarafı azalmasını kaydetti; gönderme tarafı eklemeyi ise kaydetmedi. Gözlemlenen çıktılar, bozulma biriktikçe her çağrıda azaldı:
| Öz-swap | Girdi | Kayıtlı vUSD |
Fiyat (vUSD/USDT) | USDT çıktısı |
|---|---|---|---|---|
| 1 | ~100K | ~162K | ~5,24 | ~47,8K |
| 2 | ~100K | ~256K | ~16,6 | ~25,4K |
| 3 | ~100K | ~471K | ~57,2 | ~12,5K |
| 4 | ~100K | ~918K | ~190 | ~5,73K |
| 5 | ~100K | ~1,82M | ~563 | ~2,36K |
Beş çağrı kasaya ~500K USDT aktardı ve ~93,8K USDT geri döndürdü. Pool'un kayıtlı token_balance değeri her çağrıda düşerken gerçek kasa bakiyesi büyüdü; bu durum, bir sonraki adımın istismar ettiği açığı genişletti.

- Adım 3: Saldırgan yalnızca ~3,99K
USDTsağladı (talimat 8). BozulmuşUSDTPool'u ~2,24MvUSDüretti veUSDCPool'u bunu ~2,24MUSDC'ye dönüştürdü. ŞişirilmişvUSDçıktısı mümkün oldu; çünkü Pool'un kayıtlı token bakiyesi, beş tur atılan gönderme tarafı güncellemesinin ardından gerçek kasa bakiyesinin çok altındaydı.

- Adım 4: Saldırgan ~1,12M
USDCKamino flaş kredi anaparasını ve ~11,2USDCücretini geri ödedi (talimat 9). Saldırganın nihai bakiyeleri ~1,12MUSDCve ~539KUSDToldu.
Sonuç
Bu olayın temel nedeni, send_pool ve receive_pool'un farklı hesaplara atıfta bulunduğunu doğrulayan kontrolün eksikliğiydi. Tek bir değiştirilebilir Pool, iki yerel muhasebe nesnesine seri dışı bırakıldı; Token programı kasa transferlerini zaten tamamlamışken ikinci tam serileştirme birincinin üzerine yazdı. Bu durum, Pool'un kayıtlı muhasebesini gerçek kasa bakiyesinden ayırdı. Hesap takma adları, ELF kontrol akışı, serileştirme sırası, işlem kayıtları ve kasa bakiyeleri aynı mekanizmayı desteklemektedir [1].
Solana programları için, aynı hesap türünü iki veya daha fazla değiştirilebilir rolde kabul eden herhangi bir talimat ya tekliği zorunlu kılmalı (require_keys_neq!) ya da çıkıştan önce değişiklikleri birleştirmelidir. Anchor çerçevesinin #[account] kısıtlama sistemi varsayılan olarak roller arası tekliği zorunlu kılmaz; bu nedenle bu kontrol açıkça eklenmek zorundadır. Genel örüntü — aynı durumun iki değiştirilebilir görünümünün bulunduğu ve ikinci yazmanın birincisini sessizce üzerine yazdığı durum — programların kendi serileştirme sıralarını yönettiği her yerde ortaya çıkabilir.
Kaynaklar
- [1] Allbridge Core, Teknik Olay Sonrası Analiz: Solana Pool Swap İstismarı
- [2] Allbridge Core JS SDK, Solana Swap hesap arayüzü (
bridge.json) - [3] Allbridge Core EVM Sözleşmeleri, Pool muhasebe referansı (
Pool.sol)
Bu Haftanın Diğer Olayları
Zilliqa Ledger Cüzdanı
20 Temmuz 2026'da Zilliqa, yaklaşık 400K dolar kayıpla birlikte eski yerel ZIL hesaplarının aktif olarak istismar edildiğiyle tutarlı zincir üstü aktivite tespit etti. Temel neden, Zilliqa Ledger uygulamasının EC-Schnorr imzalama yolundaki önyargılı bir nonce'du: nonce üretim kodu, modüler indirgemeden sonra 40 baytlık bir arabellekten yanlış 32 baytı kopyaladı ve en anlamlı 64 biti sıfıra sabitledi. Aynı hesaptan birkaç genel imzayla saldırgan, kafes tabanlı teknikler aracılığıyla özel anahtarı kurtarabilir ve hesabı boşaltabilirdi.
Arka Plan
Zilliqa yerel işlemleri, secp256k1 üzerinde EC-Schnorr imzaları kullanır. Her imza için imzalayan, (secp256k1 eğri mertebesi) koşulunu sağlayan taze, tam genişlikte, öngörülemeyen bir geçici nonce örneklemek zorundadır. İmzalama akışı bir taahhüt , bir meydan okuma ve bir yanıt üretir; burada özel anahtardır. Yayınlandığında herkese açık hale gelir.
doğrusal ilişkisi kritik noktadır. Doğru bir uygulama, her taze ve düzgün dağıtıldığından güvenli kalır. Nonce önyargılı veya çok küçükse her genel imza, aynı özel anahtar hakkında bilgi sızdırır.
Açık Analizi
İlgili nonce üretim yolu, Zilliqa Ledger uygulamasının "Ledger ekibi incelemesine dayalı hata düzeltmeleri" adlı commit'inde tanıtıldı. Amaçlanan akış şuydu: 40 bayt rastgelelik üret, eğri mertebesi modülo indirgeme yap ve sonuçta elde edilen skaleri olarak kullan. Geniş bir rastgele tamsayı üretip eğri mertebesiyle modülo indirgemek tek başına sorun değildir. Kusur, nonce arabelleğine kopyalama sırasında ortaya çıktı:
unsigned char nonce[size+8];
cx_rng(nonce, size+8);
cx_math_modm(nonce, size+8, (unsigned WIDE char *) PIC(domain->n), size);
os_memcpy(T->K, nonce, size);
cx_math_modm sonrasında 32 baytlık skaler sonuç, 40 baytlık arabellek içinde sağa hizalı olarak yer alır. os_memcpy(T->K, nonce, size) ilk size (32) baytı kopyalar; bu işlem baştaki 8 sıfır dolgu baytını korurken entropi taşıyan son 8 baytı atar. Üretilen nonce bu nedenle yerine koşulunu sağlar.
Bu, her nonce'un 64 bitinin sıfıra sabitlendiği anlamına gelir. Schnorr yanıt denkleminden , her imza sonucun ile sınırlandırıldığı genel bir modüler doğrusal ilişki verir. Bu bir Gizli Sayı Problemi (HNP) örneğidir. Aynı anahtardan yaklaşık dört veya daha fazla etkilenmiş imzayla, standart kafes indirgeme algoritması özel anahtarı sıradan donanımda saniyeler içinde kurtarabilir [1].
Kusur, Temmuz 2026'da tespit edilene kadar Zilliqa Ledger uygulamasının 2019'dan itibaren yayınlanan her sürümünde mevcuttu [2].
Bu olay için Saldırı Analizi sunulmamaktadır. İstismar tamamen zincir dışında gerçekleştirildi: saldırgan, kafes indirgeme kullanarak halihazırda herkese açık zincir üstü imzalardan özel anahtarları kurtardı ve ardından standart transfer işlemlerini imzaladı. Analiz edilecek çok adımlı bir zincir üstü saldırı dizisi bulunmamaktadır.
Sonuç
Bu olay, zincir üstü bir akıllı sözleşme hatasından değil, zincir dışı bir imzalama uygulama açığından kaynaklandı. Zilliqa Ledger uygulaması, ile kısıtlanmış nonce'larla EC-Schnorr imzaları üretti; bu durum, aynı hesaptan birkaç yerel işlemin ardından özel anahtar kurtarmak için yeterli yapısal bilgi sızdırdı.
Birincil hafifletme önlemi, Ledger uygulamasını güncellemek değil anahtar emekliliğidir. Düzeltilmiş bir uygulama yeni zayıflatılmış imzaları engeller; ancak zincire kaydedilmiş ve halihazırda herkese açık olan imzaları silip yok edemez. Yeterince savunmasız imza üretmiş etkilenmiş her anahtar, ele geçirilmiş olarak değerlendirilmelidir [2].
Cüzdan ve protokol ekipleri için: nonce üretimi ve kodlamasını güvenlik açısından kritik kod olarak ele alın, uygulanabilir olduğunda iyi incelenmiş bir standardı izleyen deterministik nonce üretimini tercih edin ve etkilenen kullanıcılar için, aynı özel anahtarı zaten elinde bulundurabilen saldırganlar tarafından yapılabilecek öne geçme (front-running) girişimlerini hesaba katan koordineli bir geçiş yolu sunun.
Kaynaklar
- [1] Boneh ve Venkatesan, Gizli Anahtarların En Anlamlı Bitlerini Hesaplamanın Güçlüğü (HNP)
- [2] Zilliqa Ledger Olay Merkezi
Wanchain Cardano Köprüsü
20 Temmuz 2026'da Wanchain Cardano köprüsü, Cardano TreasuryCheck Plutus doğrulayıcısındaki enjektif olmayan bir mesaj kodlaması nedeniyle yaklaşık 515,2 milyon NIGHT (~500K dolar) kaybetti [1]. Projenin olay sonrası analizi [2] üst düzey mekanizmayı açıklamakta ancak bayt düzeyinde kodlama ayrıntıları veya kod sunmamaktadır; aşağıdaki analiz tam çarpışmayı yeniden oluşturmaktadır. Köprü düğümü imzaları geçerli ve doğru şekilde doğrulanmıştı; ancak değişken uzunluklu alanların sınırlayıcısız birleştirmesi, saldırganın iki bitişik sayısal alan arasındaki bayt sınırlarını kaydırmasına ve 3.097,56 NIGHT için verilen bir yetkiyi 203.001.692,164714 NIGHT çekimi olarak yeniden yorumlamasına olanak tanıdı.
Arka Plan
Etkilenen bileşen, Wanchain'in Cardano zincirler arası hazine sistemidir. Kaynak zincirdeki bir kullanıcı token yakar veya kilitler, köprü düğümleri ayrıştırılmış çözücü alanlarından oluşturulan bir yetkilendirme mesajını imzalar ve imzalı kanıt, hazineden varlık serbest bırakmak için Cardano TreasuryCheck Plutus doğrulayıcısına gönderilir.
Açık Analizi
TreasuryCheck doğrulayıcısı, köprü düğümü imzasını 14 ayrıştırılmış çözücü alanının ham birleştirmesi üzerinde doğrular:
hashRedeemer = sha3_256 $ mconcat
[ toPkhPay, toPkhStk, policy, assetName
, packInteger amount, packInteger adaAmount
, txHash, packInteger index, packInteger mode
, uniqueId, packInteger txType, packInteger ttl
, packInteger outputCount, userData
]
packInteger tarafından kullanılan tamsayı kodlaması değişken uzunlukludur ve birleştirmede uzunluk öneki, tür ayırıcısı ya da etki alanı ayrıştırılmış türlenmiş serileştirme bulunmamaktadır. Bu nedenle farklı anlamsal demetler aynı imzalanmış bayt dizisini üretebilir.
Temsili saldırıda köprü düğümleri normal bir çekimi imzaladı:
amount = 3097560000 -> packInteger = b8a103c0
adaAmount = 1206800 -> packInteger = 126a10
birleşik = b8a103c0126a10
Saldırgan şu şekilde ayrıştırılmış bir Cardano çözücüsü gönderdi:
amount = 203001692164714 -> packInteger = b8a103c0126a
adaAmount = 16 -> packInteger = 10
birleşik = b8a103c0126a10
Bayt dizileri özdeştir. İmza kontrolü geçti ve Cardano sözleşmesi çözücüyü, yetkilendirilenin yaklaşık 65.000 katı büyüklüğünde bir çekim olarak yorumladı.
Saldırı Analizi
Aşağıdaki analiz, temsili Cardano işlemine 0a4861...2ea1 ve BSC kaynak işlemine 0xe90111...d5f26b atıfta bulunmaktadır.
-
Adım 1: Saldırgan normal görünümlü kaynak zinciri köprü talepleri oluşturdu. Temsili durumda BSC işlemi 3.110
NIGHTyaktı ve Wanchain API'si beklenen Cardano alım miktarını 3.097,56NIGHTolarak kaydetti [3]. -
Adım 2: Köprü düğümleri ham, birleştirilmiş alanlardan oluşturulan yetkilendirme karmasını imzaladı.
-
Adım 3: Saldırgan imzalanmış bayt dizisini değiştirmeden tutarken
amountileadaAmountarasındaki Cardano çözücü sınırlarını değiştirdi. -
Adım 4: Cardano
TreasuryCheckdoğrulayıcısı çözücüyü yüksek değerli bir çekim olarak ayrıştırdı ve yeniden kullanılan imzayı başarıyla doğruladı. İşlem saldırgana 203.001.692,164714NIGHTödedi. -
Adım 5: Aynı örüntü birden fazla Cardano işlemine uygulandı ve toplam yaklaşık 515.206.545,426856
NIGHT(~500K dolar) boşaltıldı.
Sonuç
Hiçbir kriptografi kırılmadı. İmzalayanlar geçerli imzalar üretti ve TreasuryCheck doğrulayıcısı bunları doğru biçimde doğruladı. Kusur, mesaj yapımındaydı: hashRedeemer, 14 değişken uzunluklu alanı hiçbir sınırlayıcı veya uzunluk öneki olmaksızın tek bir bayt dizisinde birleştirdi ve bu, kodlamayı enjektif olmayan hale getirdi. Farklı alan demetleri aynı ön görüntüyü, aynı karmayı ve aynı geçerli imzayı üretiyor.
Düzeltme, imzalanmış kodlamayı enjektif yapmaktır; böylece herhangi bir bayt dizisini yalnızca bir alan demeti üretebilir. Sabit genişlikli tamsayı kodlaması veya her alana uzunluk öneki ekleme, saldırganın hareket ettirdiği sınırları sabitler. Standart bir yapılandırılmış kodlayıcı aynı sonucu elde eder ve hataya daha az açıktır. Genel kural: ham birleştirme yerine kanonik serileştirmeyi imzalayın. Bu, değişken uzunluklu argümanlarla kullanılan Solidity'nin abi.encodePacked ile aynı hata sınıfıdır; farklı girdi demetleri özdeş bayt dizileri üretebilir. Değişken uzunluklu alanlardan derlenen herhangi bir imzalı mesaja uygulanır ve özellikle mesajı oluşturan ve ayrıştıran tarafların farklı sistemlerde çalıştığı köprüler için geçerlidir.
Kaynaklar
- [1] BlockSec Phalcon'un Wanchain istismarı hakkındaki paylaşımı
- [2] Wanchain Cardano-BNB Chain Köprüsü Olay Sonrası Analizi
- [3] Temsili BSC işlemi için Wanchain durum API'si kaydı
Lien Finance
24 Temmuz 2026'da Ethereum üzerinde faaliyet gösteren merkeziyetsiz tahvil OTC protokolü Lien Finance, yaklaşık 542K dolar USDC için istismar edildi. Temel neden, tahvil takas fonksiyonundaki bir doğrulama açığıydı: fonksiyon, girdi ve çıktı grupları arasındaki paylaşılan tahvil toplam sayısının eşleşip eşleşmediğini kontrol etti; ancak hangi spesifik tahvillerin eşleştirildiğini takip etmedi. Bu durum, saldırganın tüm girdi tahvilleri için yakma adımını atlamasına ve teminatsız bir tahvil basmasına olanak tanıdı; basılan tahvil daha sonra protokolün OTC havuzları aracılığıyla gerçek USDC karşılığında satıldı.
Arka Plan
Lien Finance, ETH'ye karşı teminatlı tahviller çıkaran bir DeFi protokolüdür. BondMakerCollateralizedEth sözleşmesi, kullanıcıların tahvil token'ları basmak için ETH teminat kilitlemesine izin verir. Bir tahvilin getirisi bir fnMap ile tanımlanır; bu, teminat fiyatını vadeye kadar ödeme miktarıyla eşleştiren parçalı doğrusal bir fonksiyondur.
Anlamlı birim, tahvil grubudur. registerNewBondGroup(), bir gruptaki tüm tahvillerin getirilerinin, birleştirilmiş fnMap'lerinin her kırılma noktasında ETH fiyatını topladığını doğrular. Bu koşulu sağlayan bir grup tam bir teminat birimini yeniden oluşturur. Grupların birbirinin yerine geçebilmesinin nedeni budur: koşulu sağlayan herhangi iki grup eşit değerdedir ve exchangeEquivalentBonds() dönüştürme yolu bu garantiye dayanır.
Hem tahvil hem de grup kaydı izinsizdir: herhangi bir adres vade ve keyfi bir fnMap sağlayarak yeni bir tahvil kaydedebilir; herhangi bir adres, yalnızca getiri toplamı kontrolüne tabi olmak kaydıyla halihazırda kayıtlı tahvil kimliklerinin herhangi bir listesini grup olarak kaydedebilir.
Açık Analizi
Hatalı sözleşmeler 0xDA6F...BEf0 ve 0x8432...7de0 adresleridir.
Bir tahvil hem girdi hem de çıktı grubunda göründüğünde onu yakıp hemen yeniden basmak gereksiz işlemdir. exceptionBonds parametresi bu tahvilleri isimlendirir; böylece takas her iki adımı da atlayabilir. Fonksiyon bunu tek bir sayaçla uygular: exceptionCount, girdi grubunu tararken her eşleşmede bir artırılır ve çıktı grubunu tararken her eşleşmede bir azaltılır; hangi bondID'nin eşleştiği hiçbir zaman kaydedilmez.

Bu, exceptionBonds = [BondA, BondB] ile [BondA, BondB] girdi grubu ve [BondC, BondA, BondA] çıktı grubunun doğrulamayı geçtiği anlamına gelir. Sayaç girdi tarafında 2'ye ulaşır (BondA ve BondB için birer eşleşme) ve çıktı tarafında 0'a geri döner; ancak her iki azaltma da BondA'nın iki girişinden gelir. Takas hiçbir şey yakmaz ve yalnızca BondC'yi basar: tüketilen girdi olmaksızın oluşturulan çıktı grubu token'ları.
Saldırı Analizi
Saldırı iki işleme bölünmüştür: Adım 1, 0xe8689a...284d0f işleminde; Adım 2-5 ise 0xb96d57...48e0e7 işleminde gerçekleşmektedir.
-
Adım 1: Saldırgan aynı vadeyi paylaşan BondA, BondB ve BondC'yi tanımlamak için izinsiz
registerNewBond()fonksiyonunu üç kez çağırdı. BondA ve BondB, teminatı_assertBondGroup()koşulunun gerektirdiği şekildeETHfiyatına toplandığı 3.200 dolar altında eşit olarak bölen kaldıraçlı alım çağrılarıdır. BondC, 1.870 dolarlık spot fiyata karşı 3.200 dolar kullanım fiyatıyla derin bir kârsız LBT'dir: sıfır içsel değer; ancak bir opsiyon olarak IMT başına yaklaşık 7,64 dolarlık zaman değeri. -
Adım 2: Saldırgan iki tahvil grubu kaydetti.
registerNewBondGroup([BondA, BondB]), Grup 36'yı (girdi) döndürdü;registerNewBondGroup([BondC, BondA, BondA])ise Grup 37'yi (çıktı) döndürdü. BondC'nin düz sıfır getirisi kırılma noktası toplamlarına hiçbir şey eklemez; bu nedenle varlığı denklik koşulunu bozmaz. BondA, Grup 37'de iki kez listelenmiştir ve bu durum takasın doğrulamayı geçmesini sağlar. -
Adım 3: Saldırgan
exchangeEquivalentBonds(inputGroup = 36, outputGroup = 37, amount = 6968565865905, exceptionBonds = [BondA, BondB])fonksiyonunu çağırdı. Girdi grubu taranırken: BondA ve BondB her biri bir istisnayla eşleşir, dolayısıyla ikisi de yakma işlemini atlar veexceptionCount2'ye ulaşır. Çıktı grubu taranırken: BondC hiçbir şeyle eşleşmez ve basılır, ardından BondA'nın iki girişi her biri bir istisnayla eşleşerek sayacı 0'a düşürür. -
Adım 4: Saldırgan basılan BondC'yi protokolün OTC havuzları (
GeneralizedDotc) aracılığıylaUSDCkarşılığında sattı ve 532.144USDCelde etti. -
Adım 5: Saldırgan aynı örüntüyü ikinci
BondMakerCollateralizedEthsözleşmesine karşı tekrarladı ve toplam kârı yaklaşık 542.144,63USDC'ye çıkardı.

Sonuç
Paylaşılan tahvillerin yeniden basımını atlamak amacıyla tasarlanan exceptionBonds mekanizması, çıktı grubunda bir bondID'yi çoğaltarak tüm girdi tahvillerine aynı anda uygulanabiliyordu. Bu durum, tahvil token'larının yalnızca issueNewBonds() aracılığıyla yatırılan teminat karşılığında oluşturulduğu değişmezi bozdu. Düzeltme, her istisnanın her iki grupta da tam olarak bir kez tüketilmesini sağlamak için hangi spesifik bondID'lerin eşleştirildiğini takip etmektir (örn. bit haritası veya küme kullanarak).
BlockSec Hakkında
BlockSec, tam yığın blockchain güvenliği ve kripto uyumluluk sağlayıcısıdır. Müşterilerin kod denetimi (akıllı sözleşmeler, blockchain ve cüzdanlar dahil) gerçekleştirmesine, saldırıları gerçek zamanlı olarak engellemesine, olayları analiz etmesine, yasadışı fonları takip etmesine ve protokoller ile 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 blockchain güvenlik makalesi yayımladı, çeşitli DeFi uygulamalarındaki sıfır gün saldırılarını raporladı, 20 milyon doların üzerinde kurtarmak amacıyla 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



