본문 바로가기
Block Chain/Hack Series

[Hack Series] 컨트렉트 주소를 지갑주소로 속이는 방법 || Solidity 0.8 | Zero Code Size ||

by 개발이 체질인 나그네 2023. 1. 31.
반응형

Web3 Hack Series : Fake Address( CA | EOA ) With Zero Code Size

안녕하세요. 스마트 컨트렉트 개발자 개발이 체질의 최원혁입니다.

이번 Web3 Hack 시리즈는 Smart Contract의 Zero Code Size를 이용하여 컨트렉트 주소(CA)를 지갑주소(EOA)로 속이는 방법에 대해 알아보겠습니다.

 

Solidity에서 Smart Contract를 구현할 때, 지갑주소(EOA)와 컨트렉트 주소(CA)를 구분하여 로직은 구현하는 경우가 많습니다. 

특히, 디파이(DeFi)의 플래시론(Flash Loan)을 구현할 때, 트랜잭션과 트랜잭션을 하나로 이어, 하나의 블록체인에 데이터를 저장시킬 때, 트리거를 지갑주소, 즉 사람이 직접 실행하게 구현할 때 사용되기도 합니다.

 

하지만, 이때 Zero Code Size를 이용하여 컨트렉트를 지갑주소로 속여 트랜잭션을 악용하는 플래시론 어택(flash loan attacks) 사례가 발생했습니다.

 

지금부터 해당 해킹 방법론에 대해 알아보겠습니다.

 

✅  Example Code으로 다루는 Solidity 코드는 게시글 마지막에 github 링크를 참고해주세요.

📝 Example Code

function onlyHumanCanCall() 함수는 지갑주소, 즉 사람이 직접 트랜잭션을 보내야 실행되는 함수입니다. isContract의 코드를 통해

msg.sender가 지갑주소인지, 컨트렉트 주소인지 판단을 합니다.

 

assembly {
            size := extcodesize(account) // return address's code hash
        }

isContract의 assembly 코드를 보면 extcodesize Opcode를 통해 해당 주소의 code hash 데이터를 조회합니다.

이때 address의 code hash의 length가 0이면 지갑주소(EOA), address의 code hash의 lenfth가 0보다 크면 컨트렉트 주소(CA)입니다.

code hash를 모른다면, 해당 내용은 아래 링크에서 [🧐  Explain How It Work] 부분을 참고해 주세요.
 

[Solidity] 지갑주소(EOA)와 컨트렉트 주소(CA) 구분하기 || Solidity 0.8 | isContract ||

안녕하세요. 스마트 컨트렉트 개발자 개발이 체질의 최원혁입니다. 이번 게시글에서 Solidity에서 지갑 주소(EOA : Externally Owned Account)와 컨트렉트 주소(CA : Contract Address)를 구분하여 예외처리하는

borntodevelop.tistory.com

 

 

때문에, 위와 같이 ExampleSmartContract의 onlyHumanCanCall() 함수를 다른 컨트렉트(FailedAttack)에서 호출(Call)하게 되면 isContract()에 의해 msg.sender가 FailedAttack의 컨트렉트 주소가 되어 트랜잭션은 실패하게 됩니다. 그 이유는 msg.sender의 code hash에 컨트렉트의 바이트코드가 저장되어 있기 때문입니다.

 

하지만, 위와 같이 constructor()를 통해 ExampleSmartContract의 onlyHumanCanCall() 함수를 실행시키면 다른 컨트렉트가 호출했음에도 불구하고 트랜잭션은 성공합니다.

 

그 이유는, 컨트렉트가 최초로 생성될 때, code hash는 비어 있기 때문입니다. code hash는 Soldity코드를 Abi.encoding을 통해 바이트코드로 변환되어 code hash에 저장됩니다. 이때, Soldity코드의 Abi.encoding은 constructor가 실행된 후에 최종적으로 바이트코드를 생성하여 code hash에 저장합니다. 즉, constructor가 먼저 실행되고, code hash에 데이터가 저장되는 EVM의 데이터 저장 구조입니다.

 

때문에 onlyHumanCanCall() 함수는 컨트렉트에서 호출되었지만, isContract()에서 msg.sender를 지갑주소로 잘못 판단하여 onlyHumanCanCall() 실행은 성공하게 됩니다.

 

이는 EVM의 데이터 레이아웃에서 발생한 취약점으로, Contract의 Zero Code Size Attack라고 합니다.

 

 

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol

Openzzepelin에서 제공하는 isContract를 보면, 플래시론 어택에 매우 취약하다는 걸 강조하고 있습니다. 그리고 함수의 호출을 방지하는걸 절대 권장하지 않고 있습니다. 

 

 

지금까지 Zero Code Size를 이용하여 컨트렉트 주소(CA)를 지갑주소(EOA)로 속이는 방법에 대해 알아봤습니다.

감사합니다.

 


🔎 전체 코드 Github :

 

GitHub - imelon2/My-Solidity-Playground

Contribute to imelon2/My-Solidity-Playground development by creating an account on GitHub.

github.com

 

 


 

반응형

댓글