Ethernaut: 12. 프라이버시

Play the level

// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

contract Privacy {
  bool public locked = true;
  uint256 public ID = block.timestamp;
  uint8 private flattening = 10;
  uint8 private denomination = 255;
  uint16 private awkwardness = uint16(now);
  bytes32[3] private data;

  constructor(bytes32[3] memory _data) public {
    data = _data;
  }

  function unlock(bytes16 _key) public {
    require(_key == bytes16(data[2]));
    locked = false;
  }

  /*
    A bunch of super advanced solidity algorithms...

      ,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`
      .,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,
      *.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^         ,---/V\
      `*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.    ~|__(o.o)
      ^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'  UU  UU
  */
}


이는 EVM 저장소를 읽는 8단계Vault와 유사합니다. 여기에 추가로 a small optimization of EVMhow casting works 에 대해 알아봅니다.

EVM은 상태 변수를 32바이트 청크로 저장합니다. 연속 변수가 32바이트 공간을 구성하는 경우(예: 이 경우 8 + 8 + 16 = 32) 동일한 청크에 저장됩니다. 다른 곳에 작성했다면 이 최적화가 발생하지 않았을 수 있습니다. await web3.eth.getStorageAt(contract.address, i)의 다음 값에 대해 i의 결과를 확인합니다.
  • 0: 0x0000000000000000000000000000000000000000000000000000000000000001이것은 1로 저장된 bool public locked = true입니다.
  • 1: 0x0000000000000000000000000000000000000000000000000000000062bc6f36이것은 uint256 public ID = block.timestamp이며 16진수로 된 UNIX 타임스탬프입니다. 62bc6f36
  • 2: 0x000000000000000000000000000000000000000000000000000000006f36ff0a이것은 6f36ff0a에서 모두 캡처한 3개 변수의 32바이트 청크입니다.
  • uint8 private flattening = 100a
  • uint8 private denomination = 255ff
  • uint16 private awkwardness = uint16(now)6f36입니다.
    음, 그 awkwardness 변수는 16비트로 캐스팅된 block.timestamp일 뿐입니다. 우리는 이미 위의 타임스탬프의 실제 256비트(32바이트) 값을 알고 있습니다: 62bc6f36 . 16비트로 변환하면 6f36(4 x 4비트 16진수)가 됩니다.

  • 3: 0x0ec18718027136372f96fb04400e05bac5ba7feda24823118503bff40bc5eb55이것은 data[0] 입니다.
  • 4: 0x61a99635e6d4b7233a35f3d0d5d8fadf2981d424110e8bca127d64958d1e68c0이것은 data[1] 입니다.
  • 5: 0x46b7d5d54e84dc3ac47f57bea2ca5f79c04dadf65d3a0f3581dcad259f9480cf이것은 data[2] 입니다.

  • 이제 data[2]bytes16 로 캐스팅하면 됩니다. 아주 짧은 단어로 캐스팅이 작동하는 방식은 다음과 같습니다.
  • 더 작은 유형으로 변환하면 더 많은 비트가 필요합니다. (예: uint32 -> uint16 )
  • 상위 유형으로 변환하면 패딩 비트가 왼쪽에 추가됩니다. (예: uint16 -> uint32 )
  • 더 작은 바이트로 변환하면 덜 중요한 비트가 소모됩니다. (예: bytes32 -> bytes16 )
  • 더 큰 바이트로 변환하면 패딩 비트가 오른쪽에 추가됩니다. (예: bytes16 -> bytes32 )

  • 따라서 data[2]를 캐스팅하면 '0x46b7d5d54e84dc3ac47f57bea2ca5f79c04dadf65d3a0f3581dcad259f9480cf'.slice(0, 2 + 32)await contract.unlock('0x46b7d5d54e84dc3ac47f57bea2ca5f79')의 왼쪽 절반을 얻게 됩니다. 그게 다야! 읽기 저장에 대한 좋은 기사는 다음과 같습니다. block .

    좋은 웹페이지 즐겨찾기