Geçtiğimiz hafta (12.10.2026–18.10.2026), toplam tahmini kaybı yaklaşık 3,87 milyon USD olan şu 3 önemli güvenlik olayı öne çıkıyor.
| Tarih | Olay | Tür | Tahmini Kayıp |
|---|---|---|---|
| 12.10.2026 | Autheo Network | Özel Anahtarın Ele Geçirilmesi | ~1,76 milyon USD |
| 14.10.2026 | UNCX Network | Muhasebe Hatası | ~807 bin USD |
| 18.10.2026 | Qubic Wallet | Zayıf Seed Oluşturma | ~1,30 milyon USD |
Kayıp rakamları, gerçekleşen değer temel alınarak yapılan tahminlerdir.
- UNCX Network: Hatalı toplam pay hesaplaması, sözleşme muhasebesi açısından haftanın en açık dersini sunduğu için bu olay ayrıntılı olarak inceleniyor.
Ücretsiz Güvenlik Taraması
Kendi geliştirdiğimiz otomatik analiz motoruyla hızlı bir güvenlik incelemesi.
Ücretsiz tarayınHaftanın Öne Çıkan Olayı: UNCX Network
14.10.2026 tarihinde saldırganlar, UNCX Network'ün Ethereum, BNB Chain, Base ve Robinhood Chain üzerindeki Smart Token Vesting V2 sözleşmelerinden yararlandı. Olay, doğrulanmış 11 fon boşaltma işleminde 10 varlığı etkiledi ve gerçekleşen veya yönlendirilen değer olarak yaklaşık 807,609 bin USD tutarla sonuçlandı [1]. Temel neden, farklı token-pay oranlarında oluşturulan hak ediş dilimlerini bir araya getiren hatalı bir toplam pay hesaplamasıydı. Bu durum, pozisyon düzeyindeki kayıtların küresel pay arzından ayrışmasına yol açarak saldırganların kontrolündeki pozisyonlardan, saldırganların sağladığından daha fazla token çekilmesine olanak tanıdı.
Arka Plan
UNCX Network, Smart Token Vesting V2 ürünüyle projelerin ERC-20 tokenlarını zamana dayalı hak ediş takvimlerine göre kilitlemesine olanak tanıyan çok zincirli bir DeFi altyapı sağlayıcısıdır. Her takvim bir hak ediş ID'siyle temsil edilir; tokenı, oluşturucuyu, lehtarı, toplam tutarı, hâlihazırda serbest bırakılmış tutarı ve bir veya daha fazla kilit açma dilimini kaydeder. Esnek ve sonradan ekleme yapılabilen bir pozisyon, oluşturulduktan sonra ek dilimler alabilir; oluşturucusu ise henüz hak edilmemiş kısmı iptal edebilir.
Aynı tokena ait pozisyonlar VestingManager içinde tek bir bakiyeyi paylaşır ve protokol, her pozisyonun bu bakiyenin ne kadarına sahip olduğunu belirlemek için payları kullanır. Dolayısıyla muhasebesi birbiriyle ilişkili iki katmandan oluşur:
Global pool, per token
create / topUp: receive tokens and mint shares
release: convert releasable shares into tokens
_totalTokenShares[token]: active shares across all positions
Position record, per vesting ID
_depositedShares[id]: share basis assigned to the position
totalAmount: token amount represented by its tranches
released: shares already paid to the beneficiary
Yönetici, serbest bırakma işleminde önce takvimin ne kadarının hak edildiğini hesaplar. totalAmount değerinin bu oranını _depositedShares[vestingId] kullanarak hak edilmiş paylara dönüştürür, released değerini çıkarır ve sonuca releasableShares adını verir. Aşağıdaki formülde tokenBalance, takvimin tokenı için yöneticinin mevcut ERC-20 bakiyesidir ve bu tokena ait tüm pozisyonlar arasında paylaşılır; _totalTokenShares[token] ise buna karşılık gelen küresel aktif pay arzıdır. Token ödemesi, bu ikisi arasındaki orana bağlıdır:
amount = releasableShares * tokenBalance / _totalTokenShares[token]
Hak ediş işlevleri aracılığıyla yatırma yapmak hem token bakiyesini hem de pay arzını artırır. Yöneticiye doğrudan yapılan bir ERC-20 transferi yalnızca token bakiyesini değiştirir; dolayısıyla her payın temsil ettiği token sayısını değiştirir. Bu pay modeli, havuzlanmış pozisyonları ancak yaşam döngüsündeki her işlem pozisyon kayıtlarıyla küresel arzın mutabakatını korursa destekleyebilir.
Güvenlik Açığı Analizi
Ethereum üzerindeki etkilenen 0xA98F...99F5 adresli VestingManager, release(), topUpVesting() ve cancel() işlevleri boyunca istismar edilebilir hâle gelen hatalı bir toplam pay hesaplaması içerir. Diğer üç ağda da aynı şekilde etkilenen dağıtımlar bulunmaktadır.
Durum değişikliklerini karşılaştırmayı kolaylaştırmak için aşağıdaki analizde D, R ve V kısaltmaları kullanılıyor. Bu semboller sözleşme kodunda yer almaz; tablo, her birini karşılık gelen sözleşme değişkeni veya ifadesiyle eşleştirir. Eşleştirilen değerler iki miktar türüne ayrılır:
| Sembol | Sözleşme değişkeni / ifadesi | Miktar türü | Anlamı |
|---|---|---|---|
D |
_depositedShares[vestingId] |
Pay miktarı | Pozisyon için kaydedilen tüm yatırımlara atanan kümülatif pay bazı |
R |
schedule.released |
Pay miktarı | Lehtara hâlihazırda ödenmiş kümülatif paylar |
| - | schedule.totalAmount |
Token miktarı | Pozisyonun mevcut dilimleri genelinde kaydedilen toplam token miktarı |
| - | vestedAmount |
Token miktarı | totalAmount değerinin, bu dilimler kapsamında şu anda hak edilmiş kısmı |
V |
vestedShare |
Pay miktarı | Tek bir harmanlanmış hak edilmiş token oranının D değerine uygulanmasıyla hesaplanan kümülatif hak edilmiş paylar |
release() işlevinde sözleşme, releasableShares değerini hesaplar, schedule.released değerini artırır ve aynı miktarı _totalTokenShares[schedule.token] değerinden çıkarır. _depositedShares[vestingId] değerini azaltmaz. Bu nedenle serbest bırakma işleminden sonra pozisyon, saklanan pay bazı D olarak kalsa da küresel arza yalnızca D - R canlı pay katkısında bulunur.
function release(uint256 vestingId) external nonReentrant {
...
uint256 releasableShares = _getReleasableShares(vestingId);
uint256 balance = IERC20(schedule.token).balanceOf(address(this));
uint256 amount =
(releasableShares * balance) / _totalTokenShares[schedule.token];
schedule.released += releasableShares;
_totalTokenShares[schedule.token] -= releasableShares;
IERC20(schedule.token).safeTransfer(msg.sender, amount);
}
cancel(), pozisyonun toplam pay bazına tek bir harmanlanmış hak ediş oranı uygular. Önce vestedAmount değerini getVestedAmount(vestingId) aracılığıyla hesaplar, ardından token cinsinden ifade edilen hak edilmiş oranı V = (vestedAmount * D) / totalAmount olarak paylara dönüştürür. Son olarak işlev, D - V değerini pozisyonun çıkarılabilir, henüz hak edilmemiş payları olarak kabul eder. V, önceki ödemelerden sonra kalan hak edilmiş paylar değil, kümülatif bir değerdir.
function cancel(uint256 vestingId) external nonReentrant {
...
uint256 vestedAmount = getVestedAmount(vestingId);
uint256 remainingAmount = schedule.totalAmount - vestedAmount;
uint256 vestedShare =
vestedAmount * _depositedShares[vestingId] / schedule.totalAmount;
uint256 sharesToRemove =
_depositedShares[vestingId] - vestedShare;
_totalTokenShares[schedule.token] -= sharesToRemove;
_depositedShares[vestingId] = vestedShare;
schedule.totalAmount = vestedAmount;
schedule.cancelled = true;
IERC20(schedule.token).safeTransfer(schedule.creator, remainingAmount);
}
Hata, harmanlanmış hesaplamanın kendisindedir. Her dilimin ayrı bir token miktarı, hak ediş ilerlemesi ve eklendiği sırada geçerli olan havuz oranına göre basılmış pay sayısı vardır. Bu nedenle her dilimin hak edilmiş payları ayrı ayrı hesaplanıp ardından toplanmalıdır. Bunun yerine sözleşme, token miktarlarını ve pay bazını önce bir araya getirir, ardından yalnızca bir D ve bir totalAmount sakladığı için birleşik pozisyona tek bir hak ediş oranı uygular. Bu iki yaklaşım yalnızca dilimlerin token başına pay oranı veya hak ediş ilerlemesi aynı olduğunda eşdeğerdir. Doğrudan token transferi, ekleme yapılmadan önce havuz oranını değiştirir; böylece yeni dilim, ilk dilime göre token başına çok daha az pay alır. Her iki dilime tek bir harmanlanmış hak ediş oranı uygulamak, artık dilimlerin birleşik hak edilmiş paylarını temsil etmeyen bir V üretebilir.
R, pozisyonun kümülatif hak edilmiş paylarından zaten ödenmiş olduğundan, tutarlı bir takvim R <= V koşulunu sağlamalıdır. Bu eşitsizliğin ihlali, harmanlanmış hesaplama hatasının doğrudan belirtisidir. Kod, pozisyon için ayrı bir canlı pay bakiyesi tutmaz; D - V değerini doğrudan küresel _totalTokenShares değerinden çıkarır. Diğer pozisyonların 1000 pay katkısında bulunduğunu, bu pozisyonun ise D = 100 ve R = 80 değerlerine sahip olduğunu varsayalım. İptalden önce küresel arz 1,000 + (D - R) = 1,020 pay içerir. Harmanlanmış hesaplama V = 60 sonucunu üretirse cancel(), D - V = 40 değerini çıkarır ve geriye yalnızca 980 küresel pay bırakır; bu, diğer pozisyonların katkıda bulunduğu 1000 paydan 20 daha azdır. Küresel arz yalnızca bu pozisyonun 20 canlı payını içerseydi, 40 değerinin çıkarılması alt taşmaya yol açar ve işlem geri alınırdı; fazla çıkarma ancak paylaşılan havuz diğer pozisyonların katkıda bulunduğu payları içerdiği için başarılı olur. Ardından işlev _depositedShares[vestingId] = 60 değerini saklarken schedule.released, 80 olarak kalır ve pozisyon kaydını imkânsız olan D < R durumunda bırakır.
Kod ne R <= V koşulunu zorunlu kılar ne de sharesToRemove değerini pozisyonun canlı paylarıyla sınırlar. Bu nedenle iptal işlemi, artık küresel arzın parçası olmayan geçmiş payları çıkarabilir. Aynı zamanda, iadeyi havuzun pay oranı üzerinden dönüştürmeden totalAmount - vestedAmount miktarını doğrudan token birimleriyle transfer eder. İptal işleminin kendisi fazla ödeme üretmez; yalnızca küresel pay arzını pozisyonlar genelinde kaydedilen canlı payların altına indirerek daha sonraki bir fazla tahsilatın koşullarını oluşturur.
Saldırı Analizi
Aşağıdaki analiz, Ethereum üzerindeki temsilî bir işlem olan 0x352065...c23823 işlemini kullanır. Saldırı sözleşmesi, işlem içinde beş tokena karşı aynı temel diziyi yürüttü ve her token için flash likidite kullandı.
-
Adım 1: Saldırgan, hemen hak edilmiş küçük bir pozisyon olan
#5058ile hemen hak edilmiş, esnek ve sonradan ekleme yapılabilen çok daha büyük iki pozisyon olan#5059ve#5060pozisyonlarını oluşturdu. Küçük pozisyon son çekim için tutulacak, büyük pozisyonlar ise küresel muhasebeyi bozmak için gereken geçmiş pay kayıtlarını sağlayacaktı. -
Adım 2: Saldırgan, iki büyük pozisyonun hak edilmiş kısmını serbest bıraktı. Başlangıçtaki yaklaşık 1:1 havuz oranında her serbest bırakma işlemi, yaklaşık olarak ilgili net yatırımı geri verdi. Karşılık gelen paylar
_totalTokenSharesdeğerinden çıktı; ancak ilk değerler,#5059ve#5060pozisyonlarına ait_depositedShareskayıtlarında kaldı ve serbest bırakılmış olarak yalnızca her takvimin ayrıreleasedalanına kaydedildi. -
Adım 3: Saldırgan, tokenları doğrudan
VestingManagersözleşmesine transfer etti. Bu, serbest bırakılmış payları geri getirmeden token bakiyesini eski düzeyine çıkardı; böylece kalan her payın temsil ettiği token miktarı keskin biçimde arttı. -
Adım 4: Saldırgan, daha önce serbest bırakılmış büyük pozisyonlara ekleme yaptı. Ek yatırımlar, yeni yükselmiş olan pay başına token oranıyla fiyatlandığından, yüksek tutarlı token yatırımları yalnızca az sayıda yeni pay bastı. Pozisyonlar artık büyük, güncelliğini yitirmiş bir geçmiş pay bazı ile küçük bir canlı pay katkısını bir arada içeriyordu.
-
Adım 5: Saldırgan, ekleme yaptığı iki pozisyonu iptal etti. Her iptal işlemi, toplam yatırılmış pay geçmişine tek bir harmanlanmış hak ediş oranı uyguladı. Yeniden hesaplanan
vestedShare, hâlihazırda serbest bırakılmış payların altına düştü; böylecesharesToRemove, pozisyonun canlı katkısını aştı. Her pozisyon ekleme sırasında yalnızca az miktarda canlı pay katmış olmasına rağmen küresel arz, pozisyon başına yaklaşık5.0e25hayalî pay azaltıldı. -
Adım 6: Payda ciddi ölçüde düşük gösterilmişken saldırgan, elinde tuttuğu küçük pozisyonun hak edilmiş kısmını serbest bıraktı. İncelenen token örneğinde yaklaşık
5.73e22ham birimlik net yatırımla yaklaşık1.07e26ham birim çekildi; bu, yatırılan miktarın yaklaşık 1,873 katıydı. Saldırgan, bozulan orandan yararlanarak pozisyon oluşturma ve serbest bırakma döngülerini tekrarladı ve flash likiditeyi geri ödedi.

Resmî olay raporu, doğrulanmış 11 fon boşaltma işleminde aynı örüntüyü tespit etti. Bazı varyantlar tek bir atomik işlemde tamamlanırken diğerlerinde son serbest bırakma bağlantılı bir işlemde gerçekleştirildi. UNCX, yaklaşık 807,609 bin USD tutarındaki rakamı saldırganın ana parasını, geçici flash işlem hacmini ve iade edilen varlıkları hariç tutarak gerçekleşen veya yönlendirilen gelirlerden hesapladı [1].
Phalcon Explorer ile Başlayın
Bilinçli Hareket Etmek İçin İşlemleri Derinlemesine İnceleyin
Şimdi ücretsiz deneyinSonuç
Temel neden, hatalı toplam pay muhasebesiydi. Protokol, farklı token-pay oranlarında oluşturulmuş dilimleri birleştirdi ve ardından bunların toplam pay bazına tek bir hak ediş oranı uyguladı. Bu, kümülatif hak edilmiş payların yanlış hesaplanmasına yol açtı, bir pozisyonun hâlâ katkıda bulunduğundan daha fazla pay çıkarmasına olanak tanıdı ve pozisyon düzeyindeki kayıtların küresel pay arzından ayrışmasına neden oldu. Saldırganlar, olduğundan düşük gösterilen küresel arzdan yararlanarak paylaşılan bakiyeden, diğer hak ediş pozisyonlarının karşılanması için gerekli olmaya devam eden tokenları çekti.
Değişen pay oranlarında sonradan yatırmaya izin veren bir muhasebe modeli, dilimleri tek bir oran altında harmanlamak yerine her dilimin token miktarını, pay bazını ve hak ediş ilerlemesini ayrı ayrı korumalıdır. Ayrıca kümülatif ödemelerin kümülatif hak edilmiş payları hiçbir zaman aşmamasını ve bir pozisyonun canlı katkısından daha fazlasını çıkaramamasını zorunlu kılmalıdır.
Kaynaklar
[1] UNCX Network Smart Token Vesting V2 Olay Raporu
Web3 için En İyi Güvenlik Denetçisi
Yayına almadan önce tasarımı, kodu ve iş mantığını doğrulayın



