---
# System prepended metadata

title: CTF

---


CTF
https://capturetheether.com/

Не проходил как положено, потому что нужно подключение к Ропстену, а он уже мертв. Для некоторых заданий даже критично чтобы посмотреть саму сеть (а сейчас даже сканер не запускается). Поэтому решал как задачи в учебнике, иногда с эксперементами в Ремиксе.
Есть резюме по всем заданиям.
Есть по каждому заданию отдельное описание от меня, как оно у меня шло в деталях.
Некоторые задания не получались, пришлось смотреть в решения других людей и усваивать.


Задание | Сделал ли | Насколько сложно было
--- | ---| ---
Guess the number | Да | Легко
Guess the secret number | Да | Сложно
Guess the random number | Да | Легко
Guess the new number | Да | Легко
Predict the future | Да | Легко
Predict the block hash | Нет | Сложно
Token Sale | Да | Легко
Token whale | Да | Легко
Retirement fund | Да | Легко
Mapping | Почти | Сложно
Donation | Почти | Сложно
Fifty years | Почти | Сложно
Fuzzy Identity | Да | Легко
Public Key | Нужен доступ Ропстен | Средне
Account Takeover | Нужен доступ Ропстен | Легко
Assume ownership | Да | Легко
Token bank | Да | Легко



# 1) Guess the number

Тут нужно угадать номер, который просто в коде контракта захардкожен.

#### Сложно ли было:
Совсем нет

# 2) Guess the secret number

Тут я очень долго тупил, не понимал как это вообще возможно подобрать хэш.
```
contract GuessTheSecretNumberChallenge { 
    bytes32 answerHash = 0xdb81b4d58595fbbbb592d3661a34cdca14d7ab379441400cbfa1b78bc447c365; 

...
function guess(uint8 n) public payable { 
	require(msg.value == 1 ether); 
	if (keccak256(n) == answerHash) {
		msg.sender.transfer(2 ether); 
	}
} 
}
```
Потом осенило что в инпуте uint8, а значит мало вариантов которых надо просто перебрать.

#### Сложно ли было:
Для меня было Сложно

# 3) Guess the random number

Я даже не понял в чем сложность, похоже на задание 1. Видимо переменная приватная, и это классическое задание на прочитание слота в сторедже.

````
contract GuessTheRandomNumberChallenge {
    uint8 answer;

    function GuessTheRandomNumberChallenge() public payable {
        require(msg.value == 1 ether);
        answer = uint8(keccak256(block.blockhash(block.number - 1), now));
    }

    function isComplete() public view returns (bool) {
        return address(this).balance == 0;
    }

    function guess(uint8 n) public payable {
        require(msg.value == 1 ether);

        if (n == answer) {
            msg.sender.transfer(2 ether);
        }
    }
}
````



#### Сложно ли было:
Легко

# 4) Guess the new number

Тут похожий перебор uint8, но внутри в качестве исходных параметров используются block.number и block.timestamp
```solidity
    function guess(uint8 n) public payable {
        require(msg.value == 1 ether);
        uint8 answer = uint8(keccak256(block.blockhash(block.number - 1), now));

        if (n == answer) {
            msg.sender.transfer(2 ether);
        }
    }
```
Поэтому задача заключается в подборе правильного тайминга.
Проще послать в сеть много транзакций, ревертить неудачные, а удачная рано или поздно пройдет.
Можно на будущее расчитать, куда сделать транзакцию, если есть уверенность попадания именно в этот момент.
Ничего иного не придумал.

#### Сложно ли было:
Легко

# 5) Predict the future

Очень похожая история.
Только надо сперва залочить средства, и потом их вытащить в нужный момент в будущем.
Формула тоже очень похожая.
```solidity
uint8 answer = uint8(keccak256(block.blockhash(block.number - 1), now<)) % 10;
```
Но тут есть взятие модуля.
Поэтому надо тоже сперва залочить, а дальше посылать много транзакций на попытку вывода - какая-то точно пройдет в вероятность 1/10.

#### Сложно ли было:
Легко

# 6) Predict the block hash

Тут не решил.
Пришлось подсматривать.
Тут надо знать оказывается, что взятие blockhash начинает возвращать 0 после 256 блока.
Как будто совсем непрактичное знание.

#### Сложно ли было:
Сложно

# 7) Token sale

Нужно оверфлоу сделать указав большое число в инпут, устарело

```solidity
uint256 constant PRICE_PER_TOKEN = 1 ether;

function buy(uint256 numTokens) public payable {
    require(msg.value == numTokens * 1 ether);

    balanceOf[msg.sender] += numTokens;
}
```

#### Сложно ли было:
Легко

# 8) Token whale

Напутали msg.sender и from, у меня уже глаз наточен такое замечать

```solidity
function _transfer(address to, uint256 value) internal {
    balanceOf[msg.sender] -= value;
    balanceOf[to] += value;
}

function transferFrom(address from, address to, uint256 value) public {
    require(balanceOf[from] >= value);
    require(balanceOf[to] + value >= balanceOf[to]);
    require(allowance[from][msg.sender] >= value);

    allowance[from][msg.sender] -= value;
    _transfer(to, value);
}
```

#### Сложно ли было:
Легко

# 9) Retirement fund

Легчайший, там надо кинуть эфиры на адрес который не хочет их принимать, надо через selfdestruct.
И в итоге будет андерфлоу и можно пройти проверку.
```
        uint256 withdrawn = startBalance - address(this).balance;
        require(withdrawn > 0);
```

#### Сложно ли было:
Легко

# 10) Mapping 

Это интересный уровень, я долго тупил.
Надо в этом контракте перезаписать первую переменную.

Я в целом угадал правильное направление - чтобы слот хранящий элемент списка был 0. Через эдакий оверфлоу. И сходил в статью Миксбайтс.

```solidity
pragma solidity ^0.4.21;

contract MappingChallenge {
    bool public isComplete;
    uint256[] map;

    function set(uint256 key, uint256 value) public {
        // Expand dynamic array as needed
        if (map.length <= key) {
            map.length = key + 1;
        }

        map[key] = value;
    }

    function get(uint256 key) public view returns (uint256) {
        return map[key];
    }
}
```
А детали уже подсмотрел в ответах.

#### Сложно ли было:
Сложно

# 11) Donation

Очень похоже но посложнее - здесь еще появляются struct + хитрость с округлением (очевиднейшая), я угадал с направлением мысли и куда копать, но а детальное решение подлягел в ответах.

#### Сложно ли было:
Сложно

# 12) Fifty years

Похож на предыдущий, но еще сложнее. Все то же самое, просто контракт запутанее.
Просто прочитал готовое решение.

#### Сложно ли было:
Сложно

# 13) Fuzzy Identity

Тут надо забрутфорсить свой адрес смарт-контракта в котором будут подряд идти символы badc0de. Не быстро, но выполнимо.

#### Сложно ли было:
Легко

# 14) Public Key

Сложно, но я понял куда надо капать - найти транзакции в сети подписанные от целевого адреса, посмотреть подпись. Почитал готовое решение.
```
pragma solidity ^0.4.21;

contract PublicKeyChallenge {
    address owner = 0x92b28647ae1f3264661f72fb2eb9625a89d88a31;
    bool public isComplete;

    function authenticate(bytes publicKey) public {
        require(address(keccak256(publicKey)) == owner);

        isComplete = true;
    }
}
```


#### Сложно ли было:
Сложно

# 15) Account Takeover

Тут надо смотреть историю подписей, а ропстен недоступен. Но почитал в чем решение - там якобы одинаковый `r` у двух подписей, это я знаю.
```
pragma solidity ^0.4.21;

contract AccountTakeoverChallenge {
    address owner = 0x6B477781b0e68031109f21887e6B5afEAaEB002b;
    bool public isComplete;

    function authenticate() public {
        require(msg.sender == owner);

        isComplete = true;
    }
}
```

#### Сложно ли было:
Легко

# 16) Assume ownership

Старое упражнение со времен когда конструктур назывался именем контракта, и была допущена ошибка в этом названии конструктора.

#### Сложно ли было:
Легко

# 17) Token bank

Тут все просто, хукабл токен + нарушенный CEI паттерн = можно через реентранси весь баланс вывести

#### Сложно ли было:
Легко