> For the complete documentation index, see [llms.txt](https://whs3-doge5k.gitbook.io/smartcontract-security-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://whs3-doge5k.gitbook.io/smartcontract-security-docs/ethernaut/17.-recovery.md).

# 17. Recovery

\#nonce

> 어떤 컨트랙트 개발자가 매우 간단한 토큰 팩토리 컨트랙트를 구축했습니다. 누구나 손 쉽게 새로운 토큰을 생성할 수 있고, 해당 개발자는 첫 번째 토큰 컨트랙트를 배포한 후 `0.001`이더를 전송하여 토큰을 추가로 얻었습니다. 하지만 지금은 그 컨트랙트의 주소를 잃어버렸습니다.
>
> 이 레벨의 목표는 잃어버린 그 컨트랙트 주소로부터 `0.001` 이더를 회수 (또는 제거)하는 것입니다.

### 들어가기 전에

[논스(Nonce)](/smartcontract-security-docs/basics/4./nonce.md)

이 내용을 학습하면 컨트랙트 주소 생성 원리를 이해하고 주소 값을 계산할 수 있다.

***

### **원본 소스 코드**

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

contract Recovery {
    //generate tokens
    function generateToken(string memory _name, uint256 _initialSupply) public {
        new SimpleToken(_name, msg.sender, _initialSupply); // [1]
    }
}

contract SimpleToken {
    string public name;
    mapping(address => uint256) public balances;

    // constructor
    constructor(string memory _name, address _creator, uint256 _initialSupply) { // [2]
        name = _name;
        balances[_creator] = _initialSupply;
    }

    // collect ether in return for tokens
    receive() external payable {
        balances[msg.sender] = msg.value * 10;
    }

    // allow transfers of tokens
    function transfer(address _to, uint256 _amount) public {
        require(balances[msg.sender] >= _amount);
        balances[msg.sender] = balances[msg.sender] - _amount;
        balances[_to] = _amount;
    }

    // clean up after ourselves
    function destroy(address payable _to) public { // [3]
        selfdestruct(_to);
    }
}
```

```
[1] : Recovery 컨트랙트가 새로운 토큰 생성

[2] : 토큰 생성에 필요한 생성자 - _name(토큰명), _creator(컨트랙트), _initialSupply(이더)

[3] : 컨트랙트를 부수고 _to에 이더를 전송
```

***

### **공격 시나리오**

Recovery 컨트랙트가 만든 토큰(컨트랙트)의 주소를 알아야 한다.

컨트랙트가 생성한 새로운 주소는 다음과 같은 방법으로 계산할 수 있다.

```solidity
contract_address = keccak(rlp.encode([sender, nonce]))[12:]

```

* **address** : 새로운 컨트랙트를 생성한 컨트랙트 (Recovery)
* **nonce** : 지갑 또는 컨트랙트가 배포한 컨트랙트 수 (Recovery의 nonce)

0.001 ether가 위치한 주소를 address와 nonce 값을 통해 계산하는 방법이 있다.

그러나 solidity는 rlp를 지원하지 않기 때문에 js 파일로 작성해아 한다. 과정이 복잡하기 때문에 [etherscan](https://sepolia.etherscan.io/)을 이용할 수 있다. Etherscan에서는 컨트랙트의 논스 값 및 새로 생성한 컨트랙트 주소까지 모두 확인할 수 있다.

<figure><img src="https://3512650551-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvLae9iTcYKyLdHhMtRVd%2Fuploads%2F8DsowicAwqkHhM1DWSqJ%2Fimage.png?alt=media&amp;token=b2e9fa73-962c-49dd-8fd1-da703698e71b" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3512650551-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvLae9iTcYKyLdHhMtRVd%2Fuploads%2FldjkiUJfTAvQJ8n3cEJr%2Fimage.png?alt=media&amp;token=4dfb7a4a-bacf-4510-8aa8-9ef244792533" alt=""><figcaption></figcaption></figure>

etherscan에서 찾은 컨트랙트 주소의 destroy를 실행시켜 내 지갑 주소로 이더를 전송하는 과정으로 푼다.

***

### **공격 코드**

```
cast send <새로 생성된 컨트랙트 주소> "destroy(address)" <내 지갑 주소> \
    --private-key <private key>
    --rpc-url <rpc url>
```

잃어버린 컨트랙트에서 destroy를 실행하여 내 지갑으로 0.001ether를 보내는 명령어로 문제를 풀 수 있다.

***

### :bangbang:**더 알아보기** :bangbang:

> 컨트랙트 주소는 결정적으로 생성됩니다. 주소는 `keccak256(creator_address, nonce)`과 같은 방식으로 계산됩니다.
>
> 여기서 `address`는 컨트랙트의 주소(또는 트랜잭션을 생성한 이더리움 주소)이고, `nonce`는 생성된 컨트랙트의 수(또는 일반 계정의 트랜잭션 nonce)입니다.
>
> 따라서 미리 정해진 주소(개인 키가 없는 주소)로 이더를 전송한 뒤, 나중에 해당 주소에 컨트랙트를 배포하여 이더를 회수할 수 있습니다. 이 방법은 직관적이지 않고, 다소 은밀하게 보일 수 있지만 개인 키 없이 이더를 저장하는 위험한 방식으로 사용되기도 합니다.
>
> 이와 관련해 Martin Swende의 [블로그 글](https://swende.se/blog/Ethereum_quirks_and_vulns.html)에서는 이 기법의 다양한 활용 사례를 소개하고 있습니다.
>
> 단, 이 방법을 구현할 떄는 반드시 정확한 nonce값을 계산해야 합니다. 실수하면 이더가 영원히 사라질 수 있습니다.
