Ethernaut: 17. 복구
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import '@openzeppelin/contracts/math/SafeMath.sol';
contract Recovery {
//generate tokens
function generateToken(string memory _name, uint256 _initialSupply) public {
new SimpleToken(_name, msg.sender, _initialSupply);
}
}
contract SimpleToken {
using SafeMath for uint256;
// public variables
string public name;
mapping (address => uint) public balances;
// constructor
constructor(string memory _name, address _creator, uint256 _initialSupply) public {
name = _name;
balances[_creator] = _initialSupply;
}
// collect ether in return for tokens
receive() external payable {
balances[msg.sender] = msg.value.mul(10);
}
// allow transfers of tokens
function transfer(address _to, uint _amount) public {
require(balances[msg.sender] >= _amount);
balances[msg.sender] = balances[msg.sender].sub(_amount);
balances[_to] = _amount;
}
// clean up after ourselves
function destroy(address payable _to) public {
selfdestruct(_to);
}
}
내 초기 솔루션은 내 수준 인스턴스의 계약 생성 트랜잭션의 내부 트랜잭션을 확인하는 것이 었습니다. 거기에서 우리는 "잃어버린"계약 주소를 아주 잘 볼 수 있고 거기에서
destroy
함수를 호출할 것입니다. 인수가 있는 함수를 호출하려면 calldata
를 제공해야 합니다(here 참조). 인수는 32바이트 단위로 제공되지만 calldata
의 처음 4바이트는 호출할 함수를 나타냅니다. 이는 함수 표준 형식의 처음 4바이트로 계산됩니다. 여러 가지 방법으로 찾을 수 있습니다.bytes4(keccak256("destory(address)"))
. 표준 형식을 손으로 작성해야 합니다. contract AAA {
// this is the same function from ethernaut
function destroy(address payable _to) public {
selfdestruct(_to);
}
// we can directly find its selector
function print() public pure returns (bytes4) {
return this.destroy.selector;
}
}
위의 방법 중 하나를 사용하면 함수 선택기가
0x00f55d9d
임을 알 수 있습니다. 그런 다음 다음과 같이 destroy
함수를 호출할 수 있습니다.const functionSelector = '0x00f55d9d';
await web3.eth.sendTransaction({
from: player,
to: '0x559905e90cF45D7495e63dA1baEFB54d63B1436A', // the lost & found address
data: web3.utils.encodePacked(functionSelector, web3.utils.padLeft(player, 64))
})
오리지널 솔루션
내 솔루션을 Ethernaut에 보내면 나중에 메시지에서 실제 솔루션을 배웠습니다! 계약 주소는 결정적이며
keccack256(RLP_encode(address, nonce))
로 계산됩니다. 컨트랙트의 nonce는 컨트랙트가 생성한 컨트랙트의 수입니다. 모든 nonce는 계약에 대해 0이지만 일단 생성되면 1이 됩니다(자신의 생성은 nonce를 1로 만듭니다).Ethereum 문서here에서 RLP 인코딩에 대해 읽어보십시오. 우리는 20바이트 주소의 RLP 인코딩과
[<20 byte string>, <1 byte integer>]
와 같은 목록에 해당하는 nonce 값 1을 원합니다.문자열의 경우:
if a string is 0-55 bytes long, the RLP encoding consists of a single byte with value 0x80 (dec. 128) plus the length of the string followed by the string. The range of the first byte is thus 0x80, 0xb7.
문자열과 nonce가 포함된 목록의 경우:
if the total payload of a list (i.e. the combined length of all its items being RLP encoded) is 0-55 bytes long, the RLP encoding consists of a single byte with value 0xc0 plus the length of the list followed by the concatenation of the RLP encodings of the items. The range of the first byte is thus 0xc0, 0xf7.
이것은 우리가 가질 것이라는 것을 의미합니다:
[
0xC0
+ 1 (a byte for string length)
+ 20 (string length itself)
+ 1 (nonce),
0x80
+ 20 (string length),
<20 byte string>,
<1 byte nonce>
]
한마디로:
[0xD6, 0x94, <address>, 0x01]
. 우리는 다음을 통해 찾을 수 있는 이 배열의 압축된 버전의 keccak256
를 찾아야 합니다.web3.utils.soliditySha3(
'0xd6',
'0x94',
// <instance address>,
'0x01'
)
soliditySha3
가 아닌 sha3
와 다른 점은 Solidity처럼 매개변수를 인코딩 압축한다는 점입니다. 나중에 해싱. 결과 다이제스트의 마지막 20바이트가 계약 주소가 됩니다! destroy
함수를 호출하는 방법은 위와 동일합니다.
Reference
이 문제에 관하여(Ethernaut: 17. 복구), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/erhant/ethernaut-17-recovery-5d8e텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)