CertiK'in Skyfall ekibi kısa süre önce Aptos, StarCoin ve Sui dahil olmak üzere çeşitli blok zincirlerindeki Rust tabanlı RPC düğümlerinde birden fazla güvenlik açığı keşfetti. RPC düğümleri, dApp'leri ve temeldeki blok zincirini birbirine bağlayan kritik altyapı bileşenleri olduğundan, sağlamlıkları sorunsuz çalışma için kritik öneme sahiptir. Blockchain tasarımcıları, kararlı RPC hizmetlerinin önemini biliyorlar, bu nedenle RPC düğümlerini yok edebilecek yaygın güvenlik açıklarından kaçınmak için Rust gibi bellek açısından güvenli dilleri benimsiyorlar.
Rust gibi bellek açısından güvenli bir dili benimsemek, RPC düğümlerinin bellek bozulması güvenlik açıklarına dayalı birçok saldırıdan kaçınmasına yardımcı olur. Ancak yakın zamanda yapılan bir denetim sayesinde, bellek açısından güvenli Rust uygulamalarının bile, dikkatli bir şekilde tasarlanıp incelenmediği takdirde, RPC hizmetlerinin kullanılabilirliğini kesintiye uğratabilecek belirli güvenlik tehditlerine karşı savunmasız olabileceğini bulduk.
Bu yazıda, pratik vakalar aracılığıyla bir dizi güvenlik açığı keşfimizi tanıtacağız.
Blockchain RPC düğüm rolü
Blok zincirinin uzaktan prosedür çağrısı (RPC) hizmeti, Katman 1 blok zincirinin temel altyapı bileşenidir. Kullanıcılara önemli bir API ön ucu sağlar ve arka uç blok zincir ağına bir ağ geçidi görevi görür. Bununla birlikte, blockchain RPC hizmeti, kimlik doğrulaması olmadan kullanıcı etkileşimini kolaylaştırması bakımından geleneksel RPC hizmetinden farklıdır. Hizmetin sürekli kullanılabilirliği kritik öneme sahiptir ve hizmetteki herhangi bir kesinti, temeldeki blok zincirinin kullanılabilirliğini ciddi şekilde etkileyebilir.
Denetim perspektifi: geleneksel RPC sunucusuna karşı blockchain RPC sunucusu
Geleneksel RPC sunucularının denetimi temel olarak girdi doğrulama, yetkilendirme/kimlik doğrulama, siteler arası istek sahteciliği/sunucu tarafı istek sahteciliği (CSRF/SSRF), enjeksiyon güvenlik açıkları (SQL enjeksiyonu, komut enjeksiyonu gibi) ve bilgi sızıntısına odaklanır.
Ancak blockchain RPC sunucuları için durum farklıdır. İşlem imzalandığı sürece, istekte bulunan istemcinin kimliğini RPC katmanında doğrulamaya gerek yoktur. Blok zincirinin ön ucu olarak RPC hizmetinin ana hedeflerinden biri kullanılabilirliğini garanti etmektir. Başarısız olursa, kullanıcılar blok zinciri ile etkileşime giremez, bu da onların zincir üzerindeki verileri sorgulamasını, işlem göndermesini veya sözleşme işlevleri yayınlamasını engeller.
Bu nedenle, bir blockchain RPC sunucusunun en savunmasız yönü "kullanılabilirlik"tir. Sunucu çökerse, kullanıcılar blok zinciriyle etkileşim kurma yeteneğini kaybeder. Daha da vahimi, bazı saldırıların zincire yayılarak çok sayıda düğümü etkilemesi ve hatta tüm ağın felç olmasına yol açmasıdır.
Yeni blok zinciri neden bellek açısından güvenli RPC kullanacaktır
Aptos ve Sui gibi bazı iyi bilinen Katman 1 blok zincirleri, RPC hizmetlerini uygulamak için bellek açısından güvenli programlama dili Rust'u kullanır. Güçlü güvenliği ve sıkı derleme zamanı denetimleri sayesinde Rust, programları yığın taşmaları ve boş işaretçi başvurusu ve serbest bırakıldıktan sonra yeniden başvuru güvenlik açıkları gibi bellek bozulması güvenlik açıklarına karşı neredeyse bağışık hale getirir.
Kod tabanını daha da güvenli hale getirmek için geliştiriciler, güvenli olmayan kod sunmamak gibi en iyi uygulamaları kesinlikle takip eder. Güvenli olmayan kodun engellendiğinden ve filtrelendiğinden emin olmak için kaynak kodunda #![forbid(unsafe_code)] kullanın.
Tamsayı taşmalarını önlemek için, geliştiriciler genellikle basit toplama ve çıkarma (+, -) yerine check_add, check_sub, saturating_add, saturating_sub, vb. gibi işlevleri kullanır. Uygun zaman aşımlarını, istek boyutu sınırlarını ve istek öğesi sınırlarını ayarlayarak kaynak tükenmesini azaltın.
Geleneksel anlamda bellek güvensizliğine karşı savunmasız olmasa da, RPC düğümleri saldırganlar tarafından kolayca manipüle edilen girdilere maruz kalır. Bellek açısından güvenli bir RPC uygulamasında, hizmet reddine yol açabilecek birkaç durum vardır. Örneğin, bellek yükseltmesi hizmetin belleğini tüketebilirken, mantık sorunları sonsuz döngülere neden olabilir. Ek olarak, yarış koşulları, eşzamanlı operasyonların beklenmedik olaylar dizisine sahip olabileceği ve sistemi tanımsız bir durumda bırakabileceği bir tehdit oluşturabilir. Ek olarak, yanlış yönetilen bağımlılıklar ve üçüncü taraf kitaplıkları, sisteme bilinmeyen güvenlik açıkları getirebilir.
Bu gönderide amacımız, Rust'ın çalışma zamanı korumalarının tetiklenerek hizmetlerin kendi kendilerini durdurmasına neden olabilecek daha acil yollara dikkat çekmektir.
Açık Pas Paniği: RPC hizmetlerini doğrudan sonlandırmanın bir yolu
Geliştiriciler, kasıtlı veya kasıtsız olarak açık panik kodu ekleyebilir. Bu kodlar genellikle beklenmeyen veya istisnai durumların üstesinden gelmek için kullanılır. Bazı yaygın örnekler şunları içerir:
iddia!(): Bir koşulun karşılanması gerektiğinde bu makroyu kullanın. Belirtilen koşul başarısız olursa, program panikleyerek kodda ciddi bir hata olduğunu belirtir.
panik!(): Bu işlev, programın kurtaramayacağı ve devam edemeyeceği bir hatayla karşılaştığında çağrılır.
unreachable!(): Bir kod parçasının çalıştırılmaması gerektiğinde bu makroyu kullanın. Bu makro çağrılırsa, ciddi bir mantık hatası olduğunu gösterir.
unimplemented!() ve todo!(): Bu makrolar, uygulanmamış işlevsellik için yer tutuculardır. Bu değere ulaşılırsa program çöker.
unwrap(): Bu method Option veya Result türleri için kullanılır.Err değişkeni veya None ile karşılaşıldığında program çöker.
Güvenlik açığı 1: Move Verifier'da onaylamayı tetikleyin!
Aptos blok zinciri, bayt kodunun soyut bir yorumu aracılığıyla referans güvenlik analizi gerçekleştirmek için Move bayt kodu doğrulayıcısını kullanır. ute() işlevi, TransferFunctions niteliğinin uygulanmasının bir parçasıdır ve bayt kodu yönergelerinin temel bloklarda yürütülmesini simüle eder.
ute_inner() işlevinin görevi, mevcut bayt kodu talimatını yorumlamak ve durumu buna göre güncellemektir. İndeks == last_index ile gösterildiği gibi, temel bloktaki son talimatı yürüttüysek, işlev, yığının boş olduğundan emin olmak için iddia!(self.stack.is_empty()) işlevini çağıracaktır. Bu davranışın arkasındaki amaç, tüm işlemlerin dengeli olmasını garanti etmektir, bu da her itişin karşılık gelen bir pop'a sahip olduğu anlamına gelir.
Normal yürütme akışında, soyut yorumlama sırasında yığın her zaman dengelenir. Bu, bayt kodunu yorumlamadan önce doğrulayan Yığın Denge Denetleyicisi tarafından garanti edilir. Ancak bakış açımızı soyut yorumlayıcılar alanına genişlettiğimizde, yığın dengesi varsayımının her zaman geçerli olmadığını görürüz.
AbstractInterpreter'daki analyze_function güvenlik açığı için yama
Özünde, soyut bir yorumlayıcı, bayt kodunu temel blok düzeyinde taklit eder. Orijinal uygulamasında, ute_block sırasında bir hatayla karşılaşmak, analiz sürecini hatayı günlüğe kaydetmeye ve kontrol akış grafiğindeki bir sonraki bloğa yürütmeye devam etmeye yönlendirirdi. Bu, yürütme bloğundaki bir hatanın yığının dengesiz olmasına neden olabileceği bir durum yaratabilir. Bu durumda yürütme devam ederse, yığının boş olmaması durumunda paniğe neden olan bir onay!
Bu, saldırganlara yararlanma fırsatı verir. Saldırgan, ute_block() içinde belirli bir bayt kodu tasarlayarak bir hatayı tetikleyebilir ve ardından ute(), yığın boş değilse bir iddia yürüterek, iddia kontrolünün başarısız olmasına neden olabilir. Bu, RPC hizmetini daha fazla panikleyecek ve sonlandırarak kullanılabilirliğini etkileyecektir.
Bunu önlemek için, uygulanan düzeltme, ute_block işlevi ilk kez bir hatayla karşılaştığında tüm analiz sürecinin durdurulmasını sağlar, böylece hatalardan kaynaklanan yığın dengesizliği nedeniyle analiz devam ederken meydana gelebilecek müteakip çökme riskinden kaçınılır. Bu değişiklik, paniğe neden olabilecek koşulları ortadan kaldırır ve soyut yorumlayıcının sağlamlığını ve güvenliğini artırmaya yardımcı olur.
** Güvenlik Açığı 2: StarCoin'de paniği tetikleyin! **
Starcoin blok zinciri, Move uygulamasının kendi çatalına sahiptir. Bu Move deposunda, Struct türünün oluşturucusunda bir panik var! Sağlanan StructDefinition, Native alan bilgisine sahipse, panik açıkça tetiklenecektir! .
Normalleştirme rutinlerinde başlatılan yapılar için açık panikler
Bu potansiyel risk, modüllerin yeniden dağıtılması sürecinde mevcuttur. Yayınlanan modül veri deposunda zaten varsa, hem mevcut modül hem de saldırgan tarafından kontrol edilen giriş modülü için modül normalleştirmesi gerekir. Bu işlem sırasında, "normalized::Module::new" işlevi, modül yapısını saldırgan tarafından kontrol edilen giriş modüllerinden oluşturarak bir "panik!" tetikler.
Normalleştirme yordamı için ön koşullar
Bu panik, istemciden özel hazırlanmış bir yük gönderilerek tetiklenebilir. Bu nedenle, kötü niyetli aktörler RPC hizmetlerinin kullanılabilirliğini bozabilir.
Yapı başlatma panik düzeltme eki
Starcoin yaması, Native durumunu ele almak için yeni bir davranış sunar. Şimdi panik yerine boş bir ec döndürür. Bu, kullanıcıların paniğe neden olan veri gönderme olasılığını azaltır.
Örtülü Pas Paniği: RPC hizmetlerini sonlandırmanın kolayca gözden kaçan bir yolu
Açık panikler kaynak kodunda kolayca tanımlanabilirken, örtük paniklerin geliştiriciler tarafından göz ardı edilmesi daha olasıdır. Örtülü panikler genellikle standart veya üçüncü taraf kitaplıkları tarafından sağlanan API'leri kullanırken ortaya çıkar. Geliştiricilerin API belgelerini baştan sona okuması ve anlaması gerekir, aksi takdirde Rust programları beklenmedik bir şekilde durabilir.
BTreeMap'te örtülü panik
Örnek olarak Rust STD'den BTreeMap'i ele alalım. BTreeMap, sıralanmış bir ikili ağaçta anahtar-değer çiftlerini düzenleyen, yaygın olarak kullanılan bir veri yapısıdır. BTreeMap, değerleri anahtarla almak için iki yöntem sunar: get(&self, key: &Q) ve index(&self, key: &Q).
get(&self, key: &Q) yöntemi, anahtarı kullanarak değeri alır ve bir Seçenek döndürür. Seçenek Some(&V) olabilir, anahtar varsa, değerin referansını döndürün, anahtar BTreeMap'te bulunmuyorsa, Yok'u döndürün.
Öte yandan, index(&self, key: &Q) doğrudan anahtara karşılık gelen değere bir başvuru döndürür. Ancak, büyük bir riski vardır: Anahtar BTreeMap'te yoksa örtülü bir paniği tetikleyecektir. Düzgün yönetilmezse, program beklenmedik bir şekilde çökebilir ve bu da onu potansiyel bir güvenlik açığı haline getirebilir.
Aslında, index(&self, key: &Q) yöntemi, std::ops::Index özelliğinin temel uygulamasıdır. Bu özellik, değişmez bir bağlamdaki bir indeks işlemidir (örn. [index] ) uygun sözdizimsel şeker sağlar. Geliştiriciler doğrudan btree_map kullanabilir [key] , index(&self, key: &Q) yöntemini çağırın. Ancak, anahtar bulunmazsa bu kullanımın paniğe kapılabileceğini ve dolayısıyla programın kararlılığına örtülü bir tehdit oluşturabileceğini göz ardı edebilirler.
Güvenlik açığı 3: Sui RPC'de üstü kapalı bir paniği tetikleyin
Sui modülü serbest bırakma yordamı, kullanıcıların modül yüklerini RPC aracılığıyla göndermelerine olanak tanır. RPC işleyicisi, talebi bayt kodu doğrulaması için arka uç doğrulama ağına iletmeden önce alınan modülü doğrudan sökmek için SuiCommand::Publish işlevini kullanır.
Bu sökme işlemi sırasında gönderilen modülün code_unit bölümü bir VMControlFlowGraph oluşturmak için kullanılır. Oluşturma işlemi, "'bloklar'" adlı bir BTreeMap'te depolanan temel blokların oluşturulmasından oluşur. Süreç, belirli koşullar altında üstü kapalı bir paniğin tetiklendiği Haritanın oluşturulmasını ve manipüle edilmesini içerir.
İşte basitleştirilmiş bir kod:
VMControlFlowGraph oluştururken örtülü panik
Bu kodda, kodu gözden geçirerek ve her kod birimi için yeni bir temel blok oluşturarak yeni bir VMControlFlowGraph oluşturulur. Temel bloklar, BTreeMap adlı bir blokta saklanır.
Blok haritası, ENTRY_BLOCK_ID ile başlatılan yığın üzerinde yinelenen bir döngüde blok[&blok] kullanılarak indekslenir. Buradaki varsayım, blok haritasında en az bir ENTRY_BLOCK_ID olduğudur.
Ancak, bu varsayım her zaman geçerli değildir. Örneğin, taahhüt edilen kod boşsa, "temel blok oluştur" işleminden sonra "blok haritası" yine boş olacaktır. Kod daha sonra for succ in &blocks[&block].successors kullanarak blok eşlemesini geçmeye çalıştığında, anahtar bulunmazsa örtük bir panik ortaya çıkabilir. Bunun nedeni,blocks[&block] ifadesinin temelde index() yöntemine yapılan bir çağrı olmasıdır; bu yöntem, daha önce bahsedildiği gibi, anahtar BTreeMap'te yoksa paniğe kapılır.
Uzaktan erişime sahip bir saldırgan, boş bir code_unit alanıyla hatalı biçimlendirilmiş bir modül yükü göndererek bu işlevdeki güvenlik açığından yararlanabilir. Bu basit RPC isteği, tüm JSON-RPC sürecini çökertir. Saldırgan bu tür hatalı biçimlendirilmiş yükleri minimum çabayla göndermeye devam ederse, hizmetin sürekli olarak kesintiye uğramasına neden olur. Bir blockchain ağında bu, ağın yeni işlemleri onaylayamayacağı ve bunun sonucunda bir hizmet reddi (DoS) durumuyla sonuçlanabileceği anlamına gelir. Ağ işlevselliği ve sistemdeki kullanıcı güveni ciddi şekilde etkilenecektir.
Sui'nin düzeltmesi: RPC sorun rutininden sökmeyi kaldırın
Code_unit bölümünün asla boş olmamasını sağlamaktan Move Bytecode Verifier içindeki CodeUnitVerifier'ın sorumlu olduğunu belirtmekte fayda var. Ancak işlem sırası, RPC işleyicilerini olası güvenlik açıklarına maruz bırakır. Bunun nedeni, doğrulama işleminin, RPC'nin giriş modüllerini işlemesinden sonraki bir aşama olan Doğrulayıcı düğümünde gerçekleşmesidir.
Bu soruna yanıt olarak Sui, modülün yayın RPC yordamındaki sökme işlevini kaldırarak güvenlik açığını giderdi. Bu, RPC hizmetlerinin potansiyel olarak tehlikeli, doğrulanmamış bayt kodunu işlemesini önlemenin etkili bir yoludur.
Ayrıca, nesne aramalarıyla ilgili diğer RPC yöntemlerinin de sökme yetenekleri içerdiğini, ancak boş kod hücrelerinin kullanımına karşı savunmasız olmadıklarını belirtmekte fayda var. Bunun nedeni, her zaman mevcut yayınlanmış modülleri sorgulamaları ve parçalarına ayırmalarıdır. Yayınlanan modüller doğrulanmış olmalıdır, bu nedenle bir VMControlFlowGraph oluştururken boş olmayan kod hücrelerinin varsayımı her zaman geçerlidir.
Geliştiriciler için öneriler
Geliştiriciler, blokaj zincirlerindeki RPC hizmetlerinin kararlılığına yönelik açık ve örtülü paniklerden kaynaklanan tehditleri anladıktan sonra, bu riskleri önlemek veya azaltmak için stratejilerde ustalaşmalıdır. Bu stratejiler, plansız hizmet kesintisi olasılığını azaltabilir ve sistemin dayanıklılığını artırabilir. Bu nedenle CertiK'in uzman ekibi aşağıdaki önerileri ortaya koymakta ve Rust programlama için en iyi uygulamalar olarak sıralamaktadır.
Rust Panik Soyutlama: Panikleri yakalamak ve bunları hata mesajlarına dönüştürmek için mümkün olduğunda Rust'ın catch_unwind işlevini kullanmayı düşünün. Bu, tüm programın çökmesini önler ve geliştiricilerin hataları kontrollü bir şekilde ele almasına olanak tanır.
API'leri dikkatli kullanın: Örtük panikler genellikle standart veya üçüncü taraf kitaplıklar tarafından sağlanan API'lerin kötüye kullanılması nedeniyle ortaya çıkar. Bu nedenle, API'yi tam olarak anlamak ve olası hataları uygun şekilde ele almayı öğrenmek çok önemlidir. Geliştiriciler her zaman API'lerin başarısız olabileceğini varsaymalı ve bu tür durumlara hazırlıklı olmalıdır.
Uygun hata işleme: paniğe başvurmak yerine hata işleme için Sonuç ve Seçenek türlerini kullanın. İlki, hataları ve özel durumları işlemek için daha kontrollü bir yol sağlar.
Belge ve yorum ekleyin: Kodunuzun iyi belgelendiğinden emin olun ve kritik bölümlere (paniğin meydana gelebileceği yerler dahil) yorumlar ekleyin. Bu, diğer geliştiricilerin potansiyel riskleri anlamalarına ve bunlarla etkili bir şekilde başa çıkmalarına yardımcı olacaktır.
Özetle
Rust tabanlı RPC düğümleri, Aptos, StarCoin ve Sui gibi blockchain sistemlerinde önemli bir rol oynamaktadır. DApp'leri ve temeldeki blok zinciri bağlamak için kullanıldıklarından, güvenilirlikleri blok zinciri sisteminin sorunsuz çalışması için kritik öneme sahiptir. Bu sistemler bellek açısından güvenli Rust dilini kullansa da, yine de kötü tasarım riski vardır. CertiK'in araştırma ekibi, bu riskleri, bellek açısından güvenli programlamada dikkatli ve dikkatli tasarım ihtiyacını gösteren gerçek dünyadan örneklerle araştırdı.
View Original
This page may contain third-party content, which is provided for information purposes only (not representations/warranties) and should not be considered as an endorsement of its views by Gate, nor as financial or professional advice. See Disclaimer for details.
Çökmekte Olan RPC: Bellek İçin Güvenli Blok Zinciri RPC Düğümlerinde Yeni Bir Güvenlik Açığı Türü Analizi
CertiK'in Skyfall ekibi kısa süre önce Aptos, StarCoin ve Sui dahil olmak üzere çeşitli blok zincirlerindeki Rust tabanlı RPC düğümlerinde birden fazla güvenlik açığı keşfetti. RPC düğümleri, dApp'leri ve temeldeki blok zincirini birbirine bağlayan kritik altyapı bileşenleri olduğundan, sağlamlıkları sorunsuz çalışma için kritik öneme sahiptir. Blockchain tasarımcıları, kararlı RPC hizmetlerinin önemini biliyorlar, bu nedenle RPC düğümlerini yok edebilecek yaygın güvenlik açıklarından kaçınmak için Rust gibi bellek açısından güvenli dilleri benimsiyorlar.
Rust gibi bellek açısından güvenli bir dili benimsemek, RPC düğümlerinin bellek bozulması güvenlik açıklarına dayalı birçok saldırıdan kaçınmasına yardımcı olur. Ancak yakın zamanda yapılan bir denetim sayesinde, bellek açısından güvenli Rust uygulamalarının bile, dikkatli bir şekilde tasarlanıp incelenmediği takdirde, RPC hizmetlerinin kullanılabilirliğini kesintiye uğratabilecek belirli güvenlik tehditlerine karşı savunmasız olabileceğini bulduk.
Bu yazıda, pratik vakalar aracılığıyla bir dizi güvenlik açığı keşfimizi tanıtacağız.
Blockchain RPC düğüm rolü
Blok zincirinin uzaktan prosedür çağrısı (RPC) hizmeti, Katman 1 blok zincirinin temel altyapı bileşenidir. Kullanıcılara önemli bir API ön ucu sağlar ve arka uç blok zincir ağına bir ağ geçidi görevi görür. Bununla birlikte, blockchain RPC hizmeti, kimlik doğrulaması olmadan kullanıcı etkileşimini kolaylaştırması bakımından geleneksel RPC hizmetinden farklıdır. Hizmetin sürekli kullanılabilirliği kritik öneme sahiptir ve hizmetteki herhangi bir kesinti, temeldeki blok zincirinin kullanılabilirliğini ciddi şekilde etkileyebilir.
Denetim perspektifi: geleneksel RPC sunucusuna karşı blockchain RPC sunucusu
Geleneksel RPC sunucularının denetimi temel olarak girdi doğrulama, yetkilendirme/kimlik doğrulama, siteler arası istek sahteciliği/sunucu tarafı istek sahteciliği (CSRF/SSRF), enjeksiyon güvenlik açıkları (SQL enjeksiyonu, komut enjeksiyonu gibi) ve bilgi sızıntısına odaklanır.
Ancak blockchain RPC sunucuları için durum farklıdır. İşlem imzalandığı sürece, istekte bulunan istemcinin kimliğini RPC katmanında doğrulamaya gerek yoktur. Blok zincirinin ön ucu olarak RPC hizmetinin ana hedeflerinden biri kullanılabilirliğini garanti etmektir. Başarısız olursa, kullanıcılar blok zinciri ile etkileşime giremez, bu da onların zincir üzerindeki verileri sorgulamasını, işlem göndermesini veya sözleşme işlevleri yayınlamasını engeller.
Bu nedenle, bir blockchain RPC sunucusunun en savunmasız yönü "kullanılabilirlik"tir. Sunucu çökerse, kullanıcılar blok zinciriyle etkileşim kurma yeteneğini kaybeder. Daha da vahimi, bazı saldırıların zincire yayılarak çok sayıda düğümü etkilemesi ve hatta tüm ağın felç olmasına yol açmasıdır.
Yeni blok zinciri neden bellek açısından güvenli RPC kullanacaktır
Aptos ve Sui gibi bazı iyi bilinen Katman 1 blok zincirleri, RPC hizmetlerini uygulamak için bellek açısından güvenli programlama dili Rust'u kullanır. Güçlü güvenliği ve sıkı derleme zamanı denetimleri sayesinde Rust, programları yığın taşmaları ve boş işaretçi başvurusu ve serbest bırakıldıktan sonra yeniden başvuru güvenlik açıkları gibi bellek bozulması güvenlik açıklarına karşı neredeyse bağışık hale getirir.
Kod tabanını daha da güvenli hale getirmek için geliştiriciler, güvenli olmayan kod sunmamak gibi en iyi uygulamaları kesinlikle takip eder. Güvenli olmayan kodun engellendiğinden ve filtrelendiğinden emin olmak için kaynak kodunda #![forbid(unsafe_code)] kullanın.
Rust programlama uygulamalarını uygulayan blockchain geliştiricilerine örnekler
Tamsayı taşmalarını önlemek için, geliştiriciler genellikle basit toplama ve çıkarma (+, -) yerine check_add, check_sub, saturating_add, saturating_sub, vb. gibi işlevleri kullanır. Uygun zaman aşımlarını, istek boyutu sınırlarını ve istek öğesi sınırlarını ayarlayarak kaynak tükenmesini azaltın.
Katman 1 Blok Zincirinde Bellek Güvenliği RPC Tehditleri
Geleneksel anlamda bellek güvensizliğine karşı savunmasız olmasa da, RPC düğümleri saldırganlar tarafından kolayca manipüle edilen girdilere maruz kalır. Bellek açısından güvenli bir RPC uygulamasında, hizmet reddine yol açabilecek birkaç durum vardır. Örneğin, bellek yükseltmesi hizmetin belleğini tüketebilirken, mantık sorunları sonsuz döngülere neden olabilir. Ek olarak, yarış koşulları, eşzamanlı operasyonların beklenmedik olaylar dizisine sahip olabileceği ve sistemi tanımsız bir durumda bırakabileceği bir tehdit oluşturabilir. Ek olarak, yanlış yönetilen bağımlılıklar ve üçüncü taraf kitaplıkları, sisteme bilinmeyen güvenlik açıkları getirebilir.
Bu gönderide amacımız, Rust'ın çalışma zamanı korumalarının tetiklenerek hizmetlerin kendi kendilerini durdurmasına neden olabilecek daha acil yollara dikkat çekmektir.
Açık Pas Paniği: RPC hizmetlerini doğrudan sonlandırmanın bir yolu
Geliştiriciler, kasıtlı veya kasıtsız olarak açık panik kodu ekleyebilir. Bu kodlar genellikle beklenmeyen veya istisnai durumların üstesinden gelmek için kullanılır. Bazı yaygın örnekler şunları içerir:
iddia!(): Bir koşulun karşılanması gerektiğinde bu makroyu kullanın. Belirtilen koşul başarısız olursa, program panikleyerek kodda ciddi bir hata olduğunu belirtir.
panik!(): Bu işlev, programın kurtaramayacağı ve devam edemeyeceği bir hatayla karşılaştığında çağrılır.
unreachable!(): Bir kod parçasının çalıştırılmaması gerektiğinde bu makroyu kullanın. Bu makro çağrılırsa, ciddi bir mantık hatası olduğunu gösterir.
unimplemented!() ve todo!(): Bu makrolar, uygulanmamış işlevsellik için yer tutuculardır. Bu değere ulaşılırsa program çöker.
unwrap(): Bu method Option veya Result türleri için kullanılır.Err değişkeni veya None ile karşılaşıldığında program çöker.
Güvenlik açığı 1: Move Verifier'da onaylamayı tetikleyin!
Aptos blok zinciri, bayt kodunun soyut bir yorumu aracılığıyla referans güvenlik analizi gerçekleştirmek için Move bayt kodu doğrulayıcısını kullanır. ute() işlevi, TransferFunctions niteliğinin uygulanmasının bir parçasıdır ve bayt kodu yönergelerinin temel bloklarda yürütülmesini simüle eder.
ute_inner() işlevinin görevi, mevcut bayt kodu talimatını yorumlamak ve durumu buna göre güncellemektir. İndeks == last_index ile gösterildiği gibi, temel bloktaki son talimatı yürüttüysek, işlev, yığının boş olduğundan emin olmak için iddia!(self.stack.is_empty()) işlevini çağıracaktır. Bu davranışın arkasındaki amaç, tüm işlemlerin dengeli olmasını garanti etmektir, bu da her itişin karşılık gelen bir pop'a sahip olduğu anlamına gelir.
Normal yürütme akışında, soyut yorumlama sırasında yığın her zaman dengelenir. Bu, bayt kodunu yorumlamadan önce doğrulayan Yığın Denge Denetleyicisi tarafından garanti edilir. Ancak bakış açımızı soyut yorumlayıcılar alanına genişlettiğimizde, yığın dengesi varsayımının her zaman geçerli olmadığını görürüz.
AbstractInterpreter'daki analyze_function güvenlik açığı için yama
Özünde, soyut bir yorumlayıcı, bayt kodunu temel blok düzeyinde taklit eder. Orijinal uygulamasında, ute_block sırasında bir hatayla karşılaşmak, analiz sürecini hatayı günlüğe kaydetmeye ve kontrol akış grafiğindeki bir sonraki bloğa yürütmeye devam etmeye yönlendirirdi. Bu, yürütme bloğundaki bir hatanın yığının dengesiz olmasına neden olabileceği bir durum yaratabilir. Bu durumda yürütme devam ederse, yığının boş olmaması durumunda paniğe neden olan bir onay!
Bu, saldırganlara yararlanma fırsatı verir. Saldırgan, ute_block() içinde belirli bir bayt kodu tasarlayarak bir hatayı tetikleyebilir ve ardından ute(), yığın boş değilse bir iddia yürüterek, iddia kontrolünün başarısız olmasına neden olabilir. Bu, RPC hizmetini daha fazla panikleyecek ve sonlandırarak kullanılabilirliğini etkileyecektir.
Bunu önlemek için, uygulanan düzeltme, ute_block işlevi ilk kez bir hatayla karşılaştığında tüm analiz sürecinin durdurulmasını sağlar, böylece hatalardan kaynaklanan yığın dengesizliği nedeniyle analiz devam ederken meydana gelebilecek müteakip çökme riskinden kaçınılır. Bu değişiklik, paniğe neden olabilecek koşulları ortadan kaldırır ve soyut yorumlayıcının sağlamlığını ve güvenliğini artırmaya yardımcı olur.
** Güvenlik Açığı 2: StarCoin'de paniği tetikleyin! **
Starcoin blok zinciri, Move uygulamasının kendi çatalına sahiptir. Bu Move deposunda, Struct türünün oluşturucusunda bir panik var! Sağlanan StructDefinition, Native alan bilgisine sahipse, panik açıkça tetiklenecektir! .
Normalleştirme rutinlerinde başlatılan yapılar için açık panikler
Bu potansiyel risk, modüllerin yeniden dağıtılması sürecinde mevcuttur. Yayınlanan modül veri deposunda zaten varsa, hem mevcut modül hem de saldırgan tarafından kontrol edilen giriş modülü için modül normalleştirmesi gerekir. Bu işlem sırasında, "normalized::Module::new" işlevi, modül yapısını saldırgan tarafından kontrol edilen giriş modüllerinden oluşturarak bir "panik!" tetikler.
Normalleştirme yordamı için ön koşullar
Bu panik, istemciden özel hazırlanmış bir yük gönderilerek tetiklenebilir. Bu nedenle, kötü niyetli aktörler RPC hizmetlerinin kullanılabilirliğini bozabilir.
Yapı başlatma panik düzeltme eki
Starcoin yaması, Native durumunu ele almak için yeni bir davranış sunar. Şimdi panik yerine boş bir ec döndürür. Bu, kullanıcıların paniğe neden olan veri gönderme olasılığını azaltır.
Örtülü Pas Paniği: RPC hizmetlerini sonlandırmanın kolayca gözden kaçan bir yolu
Açık panikler kaynak kodunda kolayca tanımlanabilirken, örtük paniklerin geliştiriciler tarafından göz ardı edilmesi daha olasıdır. Örtülü panikler genellikle standart veya üçüncü taraf kitaplıkları tarafından sağlanan API'leri kullanırken ortaya çıkar. Geliştiricilerin API belgelerini baştan sona okuması ve anlaması gerekir, aksi takdirde Rust programları beklenmedik bir şekilde durabilir.
BTreeMap'te örtülü panik
Örnek olarak Rust STD'den BTreeMap'i ele alalım. BTreeMap, sıralanmış bir ikili ağaçta anahtar-değer çiftlerini düzenleyen, yaygın olarak kullanılan bir veri yapısıdır. BTreeMap, değerleri anahtarla almak için iki yöntem sunar: get(&self, key: &Q) ve index(&self, key: &Q).
get(&self, key: &Q) yöntemi, anahtarı kullanarak değeri alır ve bir Seçenek döndürür. Seçenek Some(&V) olabilir, anahtar varsa, değerin referansını döndürün, anahtar BTreeMap'te bulunmuyorsa, Yok'u döndürün.
Öte yandan, index(&self, key: &Q) doğrudan anahtara karşılık gelen değere bir başvuru döndürür. Ancak, büyük bir riski vardır: Anahtar BTreeMap'te yoksa örtülü bir paniği tetikleyecektir. Düzgün yönetilmezse, program beklenmedik bir şekilde çökebilir ve bu da onu potansiyel bir güvenlik açığı haline getirebilir.
Aslında, index(&self, key: &Q) yöntemi, std::ops::Index özelliğinin temel uygulamasıdır. Bu özellik, değişmez bir bağlamdaki bir indeks işlemidir (örn. [index] ) uygun sözdizimsel şeker sağlar. Geliştiriciler doğrudan btree_map kullanabilir [key] , index(&self, key: &Q) yöntemini çağırın. Ancak, anahtar bulunmazsa bu kullanımın paniğe kapılabileceğini ve dolayısıyla programın kararlılığına örtülü bir tehdit oluşturabileceğini göz ardı edebilirler.
Güvenlik açığı 3: Sui RPC'de üstü kapalı bir paniği tetikleyin
Sui modülü serbest bırakma yordamı, kullanıcıların modül yüklerini RPC aracılığıyla göndermelerine olanak tanır. RPC işleyicisi, talebi bayt kodu doğrulaması için arka uç doğrulama ağına iletmeden önce alınan modülü doğrudan sökmek için SuiCommand::Publish işlevini kullanır.
Bu sökme işlemi sırasında gönderilen modülün code_unit bölümü bir VMControlFlowGraph oluşturmak için kullanılır. Oluşturma işlemi, "'bloklar'" adlı bir BTreeMap'te depolanan temel blokların oluşturulmasından oluşur. Süreç, belirli koşullar altında üstü kapalı bir paniğin tetiklendiği Haritanın oluşturulmasını ve manipüle edilmesini içerir.
İşte basitleştirilmiş bir kod:
VMControlFlowGraph oluştururken örtülü panik
Bu kodda, kodu gözden geçirerek ve her kod birimi için yeni bir temel blok oluşturarak yeni bir VMControlFlowGraph oluşturulur. Temel bloklar, BTreeMap adlı bir blokta saklanır.
Blok haritası, ENTRY_BLOCK_ID ile başlatılan yığın üzerinde yinelenen bir döngüde blok[&blok] kullanılarak indekslenir. Buradaki varsayım, blok haritasında en az bir ENTRY_BLOCK_ID olduğudur.
Ancak, bu varsayım her zaman geçerli değildir. Örneğin, taahhüt edilen kod boşsa, "temel blok oluştur" işleminden sonra "blok haritası" yine boş olacaktır. Kod daha sonra for succ in &blocks[&block].successors kullanarak blok eşlemesini geçmeye çalıştığında, anahtar bulunmazsa örtük bir panik ortaya çıkabilir. Bunun nedeni,blocks[&block] ifadesinin temelde index() yöntemine yapılan bir çağrı olmasıdır; bu yöntem, daha önce bahsedildiği gibi, anahtar BTreeMap'te yoksa paniğe kapılır.
Uzaktan erişime sahip bir saldırgan, boş bir code_unit alanıyla hatalı biçimlendirilmiş bir modül yükü göndererek bu işlevdeki güvenlik açığından yararlanabilir. Bu basit RPC isteği, tüm JSON-RPC sürecini çökertir. Saldırgan bu tür hatalı biçimlendirilmiş yükleri minimum çabayla göndermeye devam ederse, hizmetin sürekli olarak kesintiye uğramasına neden olur. Bir blockchain ağında bu, ağın yeni işlemleri onaylayamayacağı ve bunun sonucunda bir hizmet reddi (DoS) durumuyla sonuçlanabileceği anlamına gelir. Ağ işlevselliği ve sistemdeki kullanıcı güveni ciddi şekilde etkilenecektir.
Sui'nin düzeltmesi: RPC sorun rutininden sökmeyi kaldırın
Code_unit bölümünün asla boş olmamasını sağlamaktan Move Bytecode Verifier içindeki CodeUnitVerifier'ın sorumlu olduğunu belirtmekte fayda var. Ancak işlem sırası, RPC işleyicilerini olası güvenlik açıklarına maruz bırakır. Bunun nedeni, doğrulama işleminin, RPC'nin giriş modüllerini işlemesinden sonraki bir aşama olan Doğrulayıcı düğümünde gerçekleşmesidir.
Bu soruna yanıt olarak Sui, modülün yayın RPC yordamındaki sökme işlevini kaldırarak güvenlik açığını giderdi. Bu, RPC hizmetlerinin potansiyel olarak tehlikeli, doğrulanmamış bayt kodunu işlemesini önlemenin etkili bir yoludur.
Ayrıca, nesne aramalarıyla ilgili diğer RPC yöntemlerinin de sökme yetenekleri içerdiğini, ancak boş kod hücrelerinin kullanımına karşı savunmasız olmadıklarını belirtmekte fayda var. Bunun nedeni, her zaman mevcut yayınlanmış modülleri sorgulamaları ve parçalarına ayırmalarıdır. Yayınlanan modüller doğrulanmış olmalıdır, bu nedenle bir VMControlFlowGraph oluştururken boş olmayan kod hücrelerinin varsayımı her zaman geçerlidir.
Geliştiriciler için öneriler
Geliştiriciler, blokaj zincirlerindeki RPC hizmetlerinin kararlılığına yönelik açık ve örtülü paniklerden kaynaklanan tehditleri anladıktan sonra, bu riskleri önlemek veya azaltmak için stratejilerde ustalaşmalıdır. Bu stratejiler, plansız hizmet kesintisi olasılığını azaltabilir ve sistemin dayanıklılığını artırabilir. Bu nedenle CertiK'in uzman ekibi aşağıdaki önerileri ortaya koymakta ve Rust programlama için en iyi uygulamalar olarak sıralamaktadır.
Rust Panik Soyutlama: Panikleri yakalamak ve bunları hata mesajlarına dönüştürmek için mümkün olduğunda Rust'ın catch_unwind işlevini kullanmayı düşünün. Bu, tüm programın çökmesini önler ve geliştiricilerin hataları kontrollü bir şekilde ele almasına olanak tanır.
API'leri dikkatli kullanın: Örtük panikler genellikle standart veya üçüncü taraf kitaplıklar tarafından sağlanan API'lerin kötüye kullanılması nedeniyle ortaya çıkar. Bu nedenle, API'yi tam olarak anlamak ve olası hataları uygun şekilde ele almayı öğrenmek çok önemlidir. Geliştiriciler her zaman API'lerin başarısız olabileceğini varsaymalı ve bu tür durumlara hazırlıklı olmalıdır.
Uygun hata işleme: paniğe başvurmak yerine hata işleme için Sonuç ve Seçenek türlerini kullanın. İlki, hataları ve özel durumları işlemek için daha kontrollü bir yol sağlar.
Belge ve yorum ekleyin: Kodunuzun iyi belgelendiğinden emin olun ve kritik bölümlere (paniğin meydana gelebileceği yerler dahil) yorumlar ekleyin. Bu, diğer geliştiricilerin potansiyel riskleri anlamalarına ve bunlarla etkili bir şekilde başa çıkmalarına yardımcı olacaktır.
Özetle
Rust tabanlı RPC düğümleri, Aptos, StarCoin ve Sui gibi blockchain sistemlerinde önemli bir rol oynamaktadır. DApp'leri ve temeldeki blok zinciri bağlamak için kullanıldıklarından, güvenilirlikleri blok zinciri sisteminin sorunsuz çalışması için kritik öneme sahiptir. Bu sistemler bellek açısından güvenli Rust dilini kullansa da, yine de kötü tasarım riski vardır. CertiK'in araştırma ekibi, bu riskleri, bellek açısından güvenli programlamada dikkatli ve dikkatli tasarım ihtiyacını gösteren gerçek dünyadan örneklerle araştırdı.