아래의 컨트랙트 코드를 잘 살펴보고 다음의 문제를 해결하자.
1. 컨트랙트의 소유권을 탈취해라.
2. 컨트랙트의 이더 잔액(balance)을 0으로 만들어라.
pragma solidity ^0.8.0;
contract Fallback {
mapping(address => uint256) public contributions;
address public owner;
constructor() {
owner = msg.sender;
contributions[msg.sender] = 1000 * (1 ether);
}
modifier onlyOwner() {
require(msg.sender == owner, "caller is not the owner");
_;
}
function contribute() public payable {
require(msg.value < 0.001 ether);
contributions[msg.sender] += msg.value;
if (contributions[msg.sender] > contributions[owner]) {
owner = msg.sender;
}
}
function getContribution() public view returns (uint256) {
return contributions[msg.sender];
}
function withdraw() public onlyOwner {
payable(owner).transfer(address(this).balance);
}
receive() external payable {
require(msg.value > 0 && contributions[msg.sender] > 0);
owner = msg.sender;
}
}
fallback 함수는 컨트랙트에 정의되어 있지 않은 함수가 호출될 경우 자동으로 실행되는 함수이다.
예를 들어, 외부에서 컨트랙트에 존재하지 않는 함수를 호출하거나, 데이터가 포함된 트랜잭션을 전송했을 때 이 함수가 실행된다.
또한, 단순히 계정 간 이더를 보내는 transfer() 또는 send()와 같은 함수도 내부적으로 컨트랙트 호출이기 때문에, 해당 컨트랙트에 정의된 함수가 없을 경우 fallback이 호출된다.
| 종류 | 호출 조건 | 용도 |
|---|---|---|
| receive() | calldata가 비어 있고, 함수명이 없으며, 컨트랙트가 ETH를 받기 위한 용도일 때 | 순수 이더 수신용 |
| fallback() | calldata가 존재하거나, receive() 함수가 없을 경우 | 데이터 포함 호출 대응 |
이번 문제에서 owner를 탈취할 수 있는 방법은 2가지가 존재한다