[TIL] Part4 - DAY69 Project - 비밀번호와 owner 설정된 함수 뚫어보기

박동우·2023년 7월 3일

블록체인 스쿨 3기

목록 보기
65/72

[TIL] 2023-07-03 DAY69


Project

GIT 주소
https://github.com/dowoo303/SOLIDITY
https://github.com/dowoo303/hardhat


오늘 배운것들

  1. 비밀번호와 owner 설정된 함수 뚫어보기

각종 팁

회고

  1. 포트폴리오 팁: 처음에는 이런 결과물을 만들었었는데, 나중에 내가 더욱 공부를 해서 첫 결과물을 봤더니 이런점이 부족해서 어떤 것을 보완해서 더욱 완벽하게 만들었다.(스토리 텔링: 나는 지속적으로 발전하는 사람임)




💻 Solidity 코딩

✅ 비밀번호와 owner 설정된 함수 뚫어보기

  1. 터미널 node
    await web3.eth.getStorageAt(c_address, 0)
    await web3.eth.getStorageAt(c_address, 1)
    await web3.eth.getStorageAt(c_address, 2)
    await web3.eth.getStorageAt(c_address, 3)
    위 명령어를 이용해서 private한 정보들 빼내오기

  2. 똑같은 코드로 비밀번호만 다르게 컨트랙트 2개 배포
    Debug -> call data 들고와서
    https://text-compare.com/
    위 사이트에서 비교
    https://www.rapidtables.org/ko/convert/number/hex-to-decimal.html
    위 사이트에서 다른 숫자들을 10진수로 변환하면 비밀번호를 알아낼 수 있음


🟥 코드

contract C {
    uint public a;  // getStorageAt 0
    bytes32 private b;  // getStorageAt 1
    bytes32 private b2; // getStorageAt 2
    address owner;  // getStorageAt 3

    constructor(uint _pw) {
        owner = msg.sender;
        b = keccak256(abi.encodePacked(_pw));
        b2 = keccak256(abi.encodePacked(owner, _pw));
    }

    function deposit() public payable {}

    // 비밀번호만 검사
    function withdraw(uint _pw) public {
        bytes32 _b = keccak256(abi.encodePacked(_pw));
        require(b == _b);
        payable(msg.sender).transfer(address(this).balance);
    }

    // 비밀번호와 owner 검사
    function withdraw2(uint _pw) public {
        bytes32 _b2 = keccak256(abi.encodePacked(msg.sender, _pw));
        require(b2 == _b2);
        payable(msg.sender).transfer(address(this).balance);
    }
}
// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.2 <0.9.0;

contract Bank {
    uint public a;
    bytes32 private b;
    bytes32 private b2;
    address owner;

    constructor(uint _pw) {
        owner = msg.sender;
        b = keccak256(abi.encodePacked(_pw));
        b2 = keccak256(abi.encodePacked(owner, _pw));
    }

    function setA(uint _a) public {
        a = _a;
    }

    function getA() public view returns(uint){
        return a;
    }

    function deposit() public payable{}

    function withdraw(uint _pw) public {
        bytes32 _b = keccak256(abi.encodePacked(_pw));
        require(b == _b, "nope");
        payable(msg.sender).transfer(address(this).balance);
    }

    function withdraw2(uint _pw) public {
        bytes32 _b2 = keccak256(abi.encodePacked(msg.sender, _pw));
        require(b2 == _b2);
        payable(msg.sender).transfer(address(this).balance);
    }
}

contract Thief {
    Bank b;
    constructor(address _a) {
        b = Bank(_a);
    }

    function getA() public view returns(uint) {
        return b.a();
    }

    function withdraw_Bank(uint _pw) public {
        b.withdraw(_pw);
    }

    receive() external payable{}
}


✅ msg.sender의 위험성

withdraw를 특정 컨트랙트에게 허락해주고 코드 구성을 msg.sender로 하는 경우 돈을 가져갈 수는 없지만, 대신 돈을 계속 옮기는 행동은 외부에서 가능하다.
-> 따라서 컨트랙트와 컨트랙트 사이를 신뢰하는 구성은 위험할 수 있으며, 왠만하면 EOA(개인사용자)만을 신뢰하는 구성과 tx.origin을 섞는 것이 좋다.


🟥 코드

contract A {
    function getMSGSender() public view returns(address) {
        return msg.sender; // msg.sender가 EOA로
    }
}

contract B {
    A a = new A();

    function getMSG() public view virtual returns(address) {
        return a.getMSGSender(); // msg.sender가 B로
    }
}

contract D2 {
    // B b = new B();
    B b;
    constructor(address _b) { // 신뢰받는 B 컨트랙트 주소
        b = B(_b); // msg.sender가 B로
    }

    function getMSG() public view returns(address) {
        return b.getMSG();
    }
}

contract D is B{
    function getMSG() public view override returns(address) {
        return a.getMSGSender(); // msg.sender가 D로
    }
}


profile
HELLO!

0개의 댓글