728x90
Description
Look carefully at the contract's code below.
You will beat this level if
- you claim ownership of the contract
- you reduce its balance to 0
Things that might help
- How to send ether when interacting with an ABI
- How to send ether outside of the ABI
- Converting to and from wei/ether units (see help() command)
- Fallback methods
Code
// SPDX-License-Identifier: MIT
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;
}
}
Scenario
현재 player에겐 약 1이더가 있다.
주어진 조건을 만족시키기 위해서는 먼저 컨트랙트 owner를 나(player)로 바꿔주고, 그 ownership을 가지고 withdraw()를 호출하여 이 컨트랙트의 balance를 0으로 만들어주어야 한다.
owner를 msg.sender로 바꿔주는 함수가 두 개가 있는데, contribute()랑 receive()이다. contribute 함수는 먼저 contributions[msg.sender] 배열에 보낸 value만큼을 넣어주고, 그것이 owner의 contribution보다 크면 ownership을 변경해준다. 그런데 constructor를 보면, contributions[owner]는 현재 1000이더이므로 owner의 contribution보다 큰 value를 보내는 건 불가능하다.
receive함수에서는 value가 0보다 크고, contribution도 0보다 크면 ownership을 변경해준다.
따라서,
- contribute()에 0 초과 0.001 이더 미만의 value 송금
- 그럼 contributions[msg.sender] = 내가 보낸 value
- owner 은 아직 owner
- receive() 에 다시 한 번 0 초과 value 송금
- require문 통과 후
- owner 변경
- withdraw() → 이 컨트랙트에 있는 돈을 다 뺀다.
Exploit
728x90