Ana içeriğe geç

Akıllı sözleşme hatalarını bulmak için Slither nasıl kullanılır?

solidity
akıllı kontratlar
güvenlik
test etmek
Gelişmiş
Trailofbits
9 Haziran 2020
6 dakikalık okuma

Slither Nasıl Kullanılır?

Bu öğreticinin amacı, akıllı sözleşmelerdeki hataları otomatik olarak bulmak için Slither'ın nasıl kullanılacağını göstermektir.

Kurulum

Slither, Python >= 3.6 gerektirir. Pip veya docker kullanılarak kurulabilir.

Pip aracılığıyla Slither:

pip3 install --user slither-analyzer

Docker aracılığıyla Slither:

docker pull trailofbits/eth-security-toolbox
docker run -it -v "$PWD":/home/trufflecon trailofbits/eth-security-toolbox

Son komut, geçerli dizininize erişimi olan bir docker'da eth-security-toolbox'ı çalıştırır. Dosyaları ana makinenizden değiştirebilir ve docker'dan dosyalar üzerindeki araçları çalıştırabilirsiniz

Docker içinde şunu çalıştırın:

solc-select 0.5.11
cd /home/trufflecon/

Bir betik çalıştırma

Python 3 ile bir python betiği çalıştırmak için:

python3 script.py

Komut satırı

Komut satırı ve kullanıcı tanımlı komut dosyaları. Slither, birçok yaygın hatayı bulan bir dizi önceden tanımlanmış algılayıcıyla birlikte gelir. Slither'ı komut satırından çağırmak tüm algılayıcıları çalıştırır, ayrıntılı statik analiz bilgisi gerekmez:

slither project_paths

Dedektörlere ek olarak Slither, yazıcılarıopens in a new tab ve araçlarıopens in a new tab aracılığıyla kod inceleme yeteneklerine sahiptir.

Özel dedektörlere ve GitHub entegrasyonuna erişmek için crytic.ioopens in a new tab kullanın.

Statik analiz

Slither statik analiz çatısının yetenekleri ve tasarımı, blog gönderilerinde (1opens in a new tab, 2opens in a new tab) ve bir akademik makaledeopens in a new tab açıklanmıştır.

Statik analizin farklı türleri mevcuttur. clangopens in a new tab ve gccopens in a new tab gibi derleyicilerin bu araştırma tekniklerine dayandığını büyük olasılıkla fark etmişsinizdir, ancak bu aynı zamanda Inferopens in a new tab, CodeClimateopens in a new tab, FindBugsopens in a new tab ve Frama-Copens in a new tab ile Polyspaceopens in a new tab gibi resmi yöntemlere dayalı araçların da temelini oluşturur.

Burada statik analiz tekniklerini ve araştırmalarını kapsamlı bir şekilde incelemeyeceğiz. Bunun yerine, hataları bulmak ve kodu anlamak amacıyla onu daha etkili bir şekilde kullanabilmeniz için Slither'ın nasıl çalıştığını anlamak için gerekenlere odaklanacağız.

Kod temsili

Tek bir yürütme yolunu değerlendiren dinamik analizin aksine, statik analiz aynı anda tüm yolları değerlendirir. Bunu yapmak için farklı bir kod temsiline dayanır. En yaygın iki tanesi soyut sözdizimi ağacı (AST) ve kontrol akış grafiğidir (CFG).

Soyut Sözdizimi Ağaçları (AST)

AST, derleyici kodu her ayrıştırdığında kullanılır. Bu, muhtemelen statik analizin gerçekleştirilebileceği en temel yapıdır.

Özetle bir AST, genellikle her yaprağın bir değişken veya bir sabit içerdiği ve iç düğümlerin işlenenler veya kontrol akışı işlemleri olduğu yapılandırılmış bir ağaçtır. Aşağıdaki kodu göz önünde bulundurun:

1function safeAdd(uint a, uint b) pure internal returns(uint){
2 if(a + b <= a){
3 revert();
4 }
5 return a + b;
6}

İlgili AST şurada gösterilir:

AST

Slither, solc tarafından dışa aktarılan AST'yi kullanır.

Oluşturulması basit olsa da, AST iç içe geçmiş bir yapıdır. Bazen, bunu analiz etmek pek kolay olmayabilir. Örneğin, a + b <= a ifadesinin kullandığı işlemleri belirlemek için önce <= ve ardından +yı analiz etmelisiniz. Yaygın bir yaklaşım, ağaçta özyinelemeli olarak gezinen ziyaretçi desenini kullanmaktır. Slither, ExpressionVisitoropens in a new tab içinde jenerik bir ziyaretçi içerir.

Aşağıdaki kod, ifadenin bir toplama işlemi içerip içermediğini tespit etmek için ExpressionVisitor kullanır:

1from slither.visitors.expression.expression import ExpressionVisitor
2from slither.core.expressions.binary_operation import BinaryOperationType
3
4class HasAddition(ExpressionVisitor):
5
6 def result(self):
7 return self._result
8
9 def _post_binary_operation(self, expression):
10 if expression.type == BinaryOperationType.ADDITION:
11 self._result = True
12
13visitor = HasAddition(expression) # expression, test edilecek ifadedir
14print(f'{expression} ifadesi bir toplama içeriyor: {visitor.result()}')
Tümünü göster

Kontrol Akış Grafiği (CFG)

İkinci en yaygın kod temsili kontrol akış grafiğidir (CFG). Adından da anlaşılacağı gibi, bu, tüm yürütme yollarını ortaya çıkaran graf tabanlı bir gösterimdir. Her düğüm bir veya daha fazla talimat içerir. Grafikteki kenarlar kontrol akışı işlemlerini (if/then/else, döngü vb.) temsil eder. Önceki örneğimizin CFG'si şöyledir:

CFG

CFG, analizlerin çoğunun üzerine inşa edildiği temsildir.

Daha birçok kod gösterimi mevcuttur. Her bir gösterimin, gerçekleştirmek istediğiniz analize göre avantajları ve dezavantajları vardır.

Analiz

Slither ile gerçekleştirebileceğiniz en basit analiz türleri sözdizimsel analizlerdir.

Sözdizimi analizi

Slither, desen eşleştirme benzeri bir yaklaşım kullanarak tutarsızlıkları ve kusurları bulmak için kodun farklı bileşenleri ve onların gösterimleri arasında gezinebilir.

Örneğin, aşağıdaki dedektörler söz dizimiyle ilgili sorunları arar:

Anlamsal analiz

Sözdizimi analizinin aksine, anlamsal bir analiz daha derine iner ve kodun "anlamını" analiz eder. Bu aile, bazı genel analiz türlerini içerir. Bunlar daha güçlü ve kullanışlı sonuçlara yol açar, ancak yazılmaları da daha karmaşıktır.

Anlamsal analizler, en gelişmiş güvenlik açığı tespitleri için kullanılır.

Veri bağımlılığı analizi

variable_a'nın değerinin variable_b'den etkilendiği bir yol varsa, variable_a değişkeninin variable_b'ye veri bağımlı olduğu söylenir.

Aşağıdaki kodda variable_a değişkeni variable_b'ye bağımlıdır:

1// ...
2variable_a = variable_b + 1;

Slither, ara gösterimi (ileriki bir bölümde ele alınacaktır) sayesinde yerleşik veri bağımlılığıopens in a new tab yetenekleriyle birlikte gelir.

Veri bağımlılığı kullanımına bir örnek, tehlikeli katı eşitlik dedektöründeopens in a new tab bulunabilir. Burada Slither, tehlikeli bir değere katı eşitlik karşılaştırması arar (incorrect_strict_equality.py#L86-L87opens in a new tab) ve bir saldırganın sözleşmeyi tuzağa düşürmesini önlemek için kullanıcıya == yerine >= veya <= kullanması gerektiğini bildirir. Diğer şeylerin yanı sıra dedektör, balanceOf(address) çağrısının dönüş değerini tehlikeli olarak kabul eder (incorrect_strict_equality.py#L63-L64opens in a new tab) ve kullanımını izlemek için veri bağımlılığı motorunu kullanır.

Sabit nokta hesaplaması

Analiziniz CFG içinde gezinir ve kenarları takip ederse, daha önce ziyaret edilmiş düğümleri görmeniz olasıdır. Örneğin, bir döngü aşağıda gösterildiği gibi sunulursa:

1for(uint i; i < range; ++){
2 variable_a += 1
3}

Analizinizin ne zaman duracağını bilmesi gerekir. Burada iki ana strateji vardır: (1) her düğümde sonlu sayıda yineleme yapmak, (2) sabit nokta adı verilen bir şeyi hesaplamak. Sabit nokta, temel olarak bu düğümü analiz etmenin artık anlamlı bir bilgi sağlamadığı anlamına gelir.

Kullanılan bir sabit nokta örneği, yeniden giriş dedektörlerinde bulunabilir: Slither düğümleri araştırır ve harici çağrıları, depolamaya yazma ve okuma işlemlerini arar. Bir sabit noktaya ulaştığında (reentrancy.py#L125-L131opens in a new tab), keşfi durdurur ve farklı yeniden giriş desenleri (reentrancy_benign.pyopens in a new tab, reentrancy_read_before_write.pyopens in a new tab, reentrancy_eth.pyopens in a new tab) aracılığıyla bir yeniden girişin mevcut olup olmadığını görmek için sonuçları analiz eder.

Verimli sabit nokta hesaplaması kullanarak analizler yazmak, analizin kendi bilgilerini nasıl yaydığını iyi anlamayı gerektirir.

Ara temsil

Bir ara temsil (IR), statik analize orijinalinden daha elverişli olması amaçlanan bir dildir. Slither, Solidity'yi kendi IR'si olan SlithIRopens in a new tab'ye çevirir.

Yalnızca temel kontroller yazmak istiyorsanız SlithIR'ı anlamanız gerekli değildir. Ancak, gelişmiş anlamsal analizler yazmayı planlıyorsanız kullanışlı olacaktır. SlithIRopens in a new tab ve SSAopens in a new tab yazıcıları, kodun nasıl çevrildiğini anlamanıza yardımcı olacaktır.

API Temelleri

Slither'ın, sözleşmenin ve fonksiyonlarının temel niteliklerini keşfetmenizi sağlayan bir API'si vardır.

Bir kod tabanını yüklemek için:

1from slither import Slither
2slither = Slither('/path/to/project')
3

Sözleşmeleri ve fonksiyonları keşfetme

Bir Slither nesnesi şunlara sahiptir:

  • contracts (list(Contract): sözleşme listesi
  • contracts_derived (list(Contract): başka bir sözleşme tarafından kalıtılmayan sözleşmelerin listesi (sözleşmelerin alt kümesi)
  • get_contract_from_name (str): Adından bir sözleşme döndürür

Bir Contract nesnesi şunlara sahiptir:

  • name (str): Sözleşmenin adı
  • functions (list(Function)): Fonksiyon listesi
  • modifiers (list(Modifier)): Niteleyici listesi
  • all_functions_called (list(Function/Modifier)): Sözleşme tarafından erişilebilen tüm dahili fonksiyonların listesi
  • inheritance (list(Contract)): Kalıtılmış sözleşmelerin listesi
  • get_function_from_signature (str): İmzasından bir Fonksiyon döndürür
  • get_modifier_from_signature (str): İmzasından bir Niteleyici döndürür
  • get_state_variable_from_name (str): Adından bir StateVariable (Durum Değişkeni) döndürür

Bir Function veya Modifier nesnesi şunlara sahiptir:

  • name (str): Fonksiyonun adı
  • contract (contract): fonksiyonun bildirildiği sözleşme
  • nodes (list(Node)): Fonksiyonun/niteleyicinin CFG'sini oluşturan düğümlerin listesi
  • entry_point (Node): CFG'nin giriş noktası
  • variables_read (list(Variable)): Okunan değişkenlerin listesi
  • variables_written (list(Variable)): Yazılan değişkenlerin listesi
  • state_variables_read (list(StateVariable)): Okunan durum değişkenlerinin listesi (okunan değişkenlerin alt kümesi)
  • state_variables_written (list(StateVariable)): Yazılan durum değişkenlerinin listesi (yazılan değişkenlerin alt kümesi)

Sayfanın son güncellenmesi: 3 Şubat 2025

Bu rehber yararlı oldu mu?