Self Destruct ile Akıllı Sözleşme Hacklemek: Ether Kara Deliği

Self Destruct Nedir?

Dr. Doofenshmirtz’ün inatörlerinin vazgeçilmez özelliği kendini yok etme butonunu hatırlıyor musunuz? Vitalik’e esin kaynağı olmuş olacak ki benzer bir özellik ile Ethereum’da da karşılaşıyoruz: Self destruct. Ethereum projesinin başlarından itibaren mevcut olan bu işlem kodu (opcode) eskiden SUICIDE olarak adlandırılıyordu. Bazı durumlarda çok tehlikeli olabilen bu özelliği birlikte inceleyelim.

Self Destruct Fonksiyonu Nedir?

Self destruct, Ethereum projesinde çok eskilerden beri bulunan bir özelliktir. Akıllı sözleşmeyi blok zincirinden kaldırmak için kullanılır (ayrıca belirtmekte fayda var, akıllı sözleşmeleri modifiye edebildiğimiz tek fonksiyondur). Vitalik’in kendi yayınladığı dökümanda şöyle bir tanımlama yaptığını görüyoruz:

SUICIDE(Rx): Destroys the contract and clears all memory, sending the entire balance plus the negative fee from clearing memory minus TXFEE to the address at Rx

Buradan yola çıkarak fonksiyonun temel özelliğinin daha az işlem ücreti ödeyerek akıllı sözleşmeyi yok etmek, içerisindeki Etherleri belirttiğimiz adrese (Rx) göndermek olduğunu söyleyebiliriz. Self destruct işleminden sonra akıllı sözleşmenin bytecode’u blok zincirinden silinir ve fonksiyonları işlevsiz kalır. Zincirde kapladığımız alanı temizlediğimiz için gas iadesi (gas refund) mekanizması devreye girer ve Self destruct özelliğini çağırmak için daha az işlem ücreti ödemiş olunur.
(Temel konsept bu şekildeydi ancak London hard fork’u sonrasında gas iadesi kaldırıldı. Ancak hala bytecode siliniyor.)

GasToken

Tokenize Edilmiş Gas: GasToken

Bu özelliğin en popüler kullanım alanlarından birisi şüphesiz GasToken idi. Ethereum’un yüksek işlem ücretlerine bir çözüm olarak geliştirilen GasToken, gas ücretini tokenize etmekteydi. Gas fiyatları düşükken gas depolayıp fiyatlar arttığında elinizdeki GasToken’leri yok ederek (self destruct) gas iadesi almanızı sağlıyordu. Ancak London hard fork’u sonrasında bu token de işlevini yitirdi.

Self Destruct ile Dapp Hacklemek

Self Destruct ile Kara Delik Oluşturmak

Düz bir akıllı sözleşmeye Ethereum göndermek isterseniz işleminiz başarısız (revert) olacaktır. Bunun sebebi “payable” özelliğine sahip fonksiyon bulunmadığı sürece Ether kabul edilmemesidir. Örneğin aşağıdaki kod ile oluşturulmuş bir akıllı sözleşmeye Ether gönderemezsiniz:

pragma solidity ^0.8.7;
contract AkilliSozlesme {}

Ancak eğer receive adında payable bir fonksiyon eklersek sözleşmemiz Ether kabul etmeye başlayacaktır:

pragma solidity ^0.8.7;
contract AkilliSozlesme {
    receive() external payable {}
}

Self destruct fonksiyonu istisnai bir şekilde (payable fonksiyon bulunmasa bile) her türlü adrese Ether gönderebilir. Bu sebepten ötürü bazı Dapplerde güvenlik zafiyeti riski ortaya çıkmaktadır.

Örnek Zafiyet Senaryosu

Oyunun amacı akıllı sözleşmedeki Ether miktarını 7 yapmak olarak yapmak olsun. Her kullanıcı 1 kez 1 ETH’lik gönderim yapabilir. Bu durumda 7. gönderim yapan kullanıcı ödülü alacaktır.

(Görsel kaynağı: Slowmist)

Örnek oyun kodumuz:

contract EtherOyunu {
    uint public kazanmakIcinGerekenMiktar = 7 ether;
    address public kazanan;

    mapping (address => bool) gonderenler;

    function paraYatir() public payable {
        require(!gonderenler[msg.sender], "Zaten para yatirdin.");
        require(msg.value == 1 ether, "Sadece 1 ether gonderebilirsin.");

        uint bakiye = address(this).balance;
        require(bakiye <= kazanmakIcinGerekenMiktar, "Oyun bitti");

        gonderenler[msg.sender] = true;

        if (bakiye == kazanmakIcinGerekenMiktar) {
            kazanan = msg.sender;
        }
    }

    function oduluAl() public {
        require(msg.sender == kazanan, "Kazanan sen degilsin.");

        (bool sent, ) = msg.sender.call{value: address(this).balance}("");
        require(sent, "Ether gonderimi basarisiz.");
    }
}

Saldırgan, Self destruct‘ın yukarıda bahsettiğimiz özelliğinden faydalanarak akıllı sözleşmeye 6 ETH gönderir ve 7. ETH’yi kendi cüzdanından manuel göndererek ödülü alır. Saldırı için aşağıdaki akıllı sözleşmeyi kullanabilir:

contract Saldiri {
    function saldiri() public payable {
        // burada ether oyununun sözleşme adresini girecek
        address payable addr = payable(address(0x1234567));
        selfdestruct(addr);
    }
}

Not: Kod Solidity by Example‘den alınıp düzenlenmiştir.

Kara Delik: Para Nereye Gitti?

Bildiğiniz gibi token yakımı işlemi için 0x0000…dead gibi kimsenin gizli anahtarını bilmediği adreslere gönderim yapılıyor. Pratikte bunlara erişmek imkansız olsa da teoride (yüzyıllar sürse de) brute-force gibi yöntemler ile bu cüzdanlar ele geçirilebilir. Ancak eğer self destruct kullanarak bir “kara delik” yaratırsak Etherlerimizi kimsenin hiçbir şekilde erişemeyeceği bir yere gönderebiliriz.

Bunu gerçekleştirmek için önce boş bir akıllı sözleşme oluşturalım:

pragma solidity ^0.8.7;
contract KaraDelik {}

Ardından bu kara deliğe gönderim yapmak için yardımcı bir akıllı sözleşme oluşturalım:

pragma solidity ^0.8.7;
contract KaraDelikeYolculuk {
    receive () external payable {

        // burada ilk deploy edilen sözleşmenin adresini girin
        selfdestruct(payable(0x1234.....000));
    }
}

İşlemi tamamladık! İkinci akıllı sözleşmeye herhangi bir miktarda Ether gönderirsek ilk oluşturduğumuz sözleşmeye transfer olacak ve hiç kimse erişemeyecek. Siz de bunu testnet üzerinde deneyebilirsiniz.

Self Destruct Kaldırılıyor

Bu fonksiyonun temel özelliklerinden biri olan gas iadesi zaten Londra güncellemesi ile kaldırılmıştı. Vitalik bir blog yazısında fonksiyonun tamamen kaldırılacağına işaret ediyor. EIP-4758 koduyla kayıtlı teklif şu an için taslak halinde ancak aktif edilmesi durumunda Self Destruct’ın “yok etme” özelliğinin tamamen kaldırılması planlanıyor.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir