無知

갈 길이 먼 공부 일기

기술 공부/블록체인

스마트 컨트랙트 (6) | 보안 : 해킹 및 버그 사례

moozii 2022. 4. 27. 20:32

 

Smart Contract Security for Pentesters  https://iosiro.com/blog/smart-contract-security-pentesters

 

해킹 및 버그 사례

해당 내용은, 블록체인 및 스마트 컨트랙트 분야에서 코드 및 시스템의 안정성과 보안, 호환성 등의 문제가 얼마나 중요한 사안인지를 체감할 수 있는 현실 사례들의 모음이라고 보면 된다. 

 

As many supporters argue, bugs are to be expected on any new platform. However, in ethereum, code issues have proven uniquely hazardous, since its smart contracts are supposed to be 'immutable' (that is, they can't be changed after the fact), including any and all errors. The most notorious coding issue so far was ethereum's biggest project, The DAO, which collapsed as a result last summer. There have been lesser-known bugs too, such as one in the programming language Solidity.

https://www.coindesk.com/markets/2017/03/21/aborted-ens-launch-marks-latest-setback-for-ethereum-apps/ 

 

 

 

 

 

(1) 비트코인 버그

 

A. 비트코인 스크립트 코드 버그

https://bitcointalk.org/index.php?topic=822.0

Integer Overflow 현상으로 인해 두 정수 값의 합이 음수임을 확인하지 않고 결과값을 unsigned integer로 잘못 취급하여, 비트코인 스크립트의 out value가 920억 비트코인이 되는 오류가 발생한 바 있다. 이로 인해 비트코인 네트워크는 해당 트랜잭션 이전으로 복원되고 오류를 수정한 바 있다. 

 

 

 

B. 비트코인 코어 시스템 버그

https://bitcointalk.org/index.php?topic=152030.0

비트코인 코어 시스템이 0.7에서 0.8로 업그레이드 되면서 신규 버전 시스템과 구 버전 시스템의 호환성 문제가 발생한 바 있다. 이로 인해 과거 블록들이 신규 시스템에서 인식되지 않는 문제가 발생해, 비트코인 커뮤니티는 이전 버전으로 되돌아가고, 신규 블록들은 폐기한 바 있다. 

 

 

 

 

 

(2) 스마트 컨트랙트 안정성 문제

 

A. DAO Hack

 

Each time, the contract checks that the user's withdrawable balance and sends it out. So, the user will get twice their balance out of the contract. When the code resolves, the user's balance will be set to 0 however many times the contract was called. Now, as I've written the attacking wallet code, this (purposely) won't work exactly. There are a few caveats and things to do. But, hopefully this strikes some fear in your heart.


https://vessenes.com/more-ethereum-attacks-race-to-empty-is-the-real-deal/ 
More Ethereum Attacks: Race-To-Empty is the Real Deal
09 JUNE 2016, Peter Vessenes
 

More Ethereum Attacks: Race-To-Empty is the Real Deal

Chriseth at github casually pointed out a terrible, terrible attack on wallet contracts that I had not considered. If there were a responsible disclosure avenue for ethereum contract developers, I would use it, but there doesn't seem to be. Not only that,

vessenes.com

 

The DAO was a decentralized autonomous organization (DAO) that was launched in 2016 on the Ethereum blockchain. After raising $150 million USD worth of ether (ETH) through a token sale, The DAO was hacked due to vulnerabilities in its code base. The Ethereum blockchain was eventually hard forked to restore the stolen funds, but not all parties agreed with this decision, which resulted in the network splitting into two distinct blockchains: Ethereum and Ethereum Classic.

...

The token sale was set to last 28 days, during which the tokens were “locked up,” and after which the DAO would begin to operate. By three weeks into the token sale, The DAO had raised more than $150 million from more than 11,000 investors, making it one of the largest crowdfunding campaigns in history at the time. However, even before the token sale had concluded, several onlookers expressed concerns about vulnerabilities in The DAO’s code. More specifically, computer scientists were concerned that a bug in The DAO’s wallet smart contracts would allow them to be drained. While programmers attempted to fix the bug, an attacker exploited the vulnerability and began siphoning funds from The DAO.

...

Initially, Ethereum founder Vitalik Buterin proposed a soft fork of the Ethereum network, adding a snippet of code that would effectively blacklist the attacker and prevent them from moving the stolen funds. However, shortly thereafter, the attacker — or someone posing as the attacker; it has not been verified — published an open letter to the Ethereum community claiming that the funds had been obtained in a “legal” way in accordance with the rules set out in the smart contract. The attacker also said they would take legal action against anyone who attempted to seize the ether.

...

Before the Ethereum community could proceed with the soft fork, a bug was discovered in the update’s code, making it vulnerable to attack. A second solution — a hard fork — was proposed and eventually executed after much debate. The hard fork effectively rolled back the Ethereum network’s history to before The DAO attack and reallocated The DAO’s ether to a different smart contract so that investors could withdraw their funds. This was extremely controversial — after all, blockchains are supposed to be immutable and censorship-resistant.

It was initially unclear as to whether the fork would be executed. Though it was proposed by Ethereum developers, they did not have the unilateral power to implement the change. Miners, exchanges, and node operators also had to agree to update their software. After more heated debate in public forums, on July 20, 2016, at block 192,000, the Ethereum hard fork was implemented.


https://www.gemini.com/cryptopedia/the-dao-hack-makerdao 
By Cryptopedia Staff Updated March 17, 2022
DAO는 탈중앙화 투자 신탁 조직으로, 누구나 투자 아이디어를 제시하고, 구성원들은 지분에 따라 투표로 의견을 표현해 과반의 동의에 따라 투자를 집행하고 투자 이익을 얻는다. 해당 규칙은 코드, 스마트 컨트랙트 형태로 구현되어 깃헙에 공개되었다. 

투자 의사 결정 과정에서 다수결의 횡포를 막기 위한 조치로, splitDAO 함수를 구현하였다. 해당 함수를 호출하면 자신이 투자한 해당 프로포절에서 본인이 투자한만큼 금액을 다시 인출해서 새로운 주소로 송금한다. 즉, 투자를 철회해서 새로운 프로포절로 투자금을 이전하여 프로젝트 방향 변경을 지지하는 것이다. 

splitDAO 내 문제 1. 
해당 함수를 구현한 코드 상, 기존 투자금을 신규 주소로 송금한 뒤 프로포절 내 투자 내역을 초기화하는데, 송금만 이뤄지고 프로포절 내 잔고 내역은 초기화되지 않는 오류의 가능성이 한가지 문제이다. 

splitDAO 내 문제 2. 
또한, 기존 투자금을 신규 주소로 송금하는 메소드 withdrawRewardFor은, splitDAO 함수를 재귀적으로 무한히 호출할 수 있다는 문제가 있다. 따라서 프로포절 내 모든 투자금을 강제로 무한히 옮겨버리는 문제가 발생할 수 있다는 것이다. 

무한 호출이라는 취약점을 악용해 해커들이 5000만 불의 투자금을 탈취했고, 이더리움은 하드포크를 통해 투자자 토큰 반환을 결정하면서 이더리움과 이더리움클래식이 분리되게 되었다. 
 

GitHub - TheDAO/DAO-1.0: The Standard DAO Framework at 0xbb9bc244d798123fde783fcc1c72d3bb8c189413

The Standard DAO Framework at 0xbb9bc244d798123fde783fcc1c72d3bb8c189413 - GitHub - TheDAO/DAO-1.0: The Standard DAO Framework at 0xbb9bc244d798123fde783fcc1c72d3bb8c189413

github.com

 

 

 

B. ENS Bug

Context: Each name registered with the service has an auction period (during which blind bids should be submitted) followed by a reveal period ( during which the contents of a bid should be revealed).

Bug #1: Nothing prevents a bid from being submitted during the reveal period, so anyone could see the winning bid, and then outbid it, or underbid it by only a small amount in order to force the winning bidder to pay a much higher price. Nick Johnson’s post does a good job of summarizing the issue, and you can take a look at the proposed fix.

Issue 2: A bid value could be “faked”, meaning you could indicate a high bid value, without actually submitting that value. It breaks the bidding process completely, allowing anyone to win any name while only paying 0.01 ETH.


https://medium.com/the-ethereum-name-service/security-lessons-from-the-ens-launch-c5bc96bf439e#.eea5zwbe0 
Security lessons from the ENS launch
Maurelian Mar 17, 2017
 

Security lessons from the ENS launch

(update: here is project lead Nick Johnson’s own post-mortem and next steps)

medium.com

 

tl;dr: The initial ENS registrar has a bug that will require anyone who already bid to take manual action to recover any bids they already placed, and resubmit their bid when the fix is deployed. Your ether is not at risk!

The auction registrar was designed with two distinct phases for each auction: bidding, and reveal. If users can bid during the reveal phase, they can wait until they know what their opponents bid, and either outbid them, do nothing, or underbid them by a small amount to force them to pay the maximum. To prevent this, the auction registrar was designed to prohibit bids during the reveal phase of the auction. A refactor accidentally removed that check, and while we have many unit tests, this edge case was not amongst them. We’ve since written more unit tests to cover this and other issues to prevent any recurrence of problems like this one.


https://medium.com/the-ethereum-name-service/about-the-ens-auction-7bc5eff908cc 
About the ENS auction
Nick Johnson Mar 14, 2017
 

About the ENS auction

Edit: We’ve since relaunched the ENS registry; for more details see https://ens.domains/!

medium.com

 

TL; DR: Should the comparisons in HashRegistrarSimplified.sol#L341 and HashRegistrarSimplified.sol#337 and HashRegistrarSimplified.sol#355 be against actualValue rather than _value?

Via shaBid(bytes32 hash, address owner, uint value, bytes32 salt), I can create a bid for associated with any value that I please. This value is part of the hash. I will always use this value to identify the bid. No ether is sent.

Via newBid(bytes32 sealedBid), I enter my bid into the auction. I may pay ether here, but there is no enforced relationship between the amount of ether I pay and the value I have declared when defining the bid. The value of my Deed will be defined by the amount I have paid, and the deed will own that amount.But this may not be the value I declared when defining the bid.

When I call unsealBid, I must identify the bid with its declared value _value, which may differ from the value of the Deed that represents it. The function will compute a quantity actualValue, which will be the minimum of the value I actually paid and the value declared. So, if I have underpaid, actualValue will be the value that I have actually paid.

However, when checking to see if I have won the auction (or if I have bid at least the minimum price), the declared value _value is used rather than actualValue to decide if I have won. If my declared value is sufficiently high, I eject the previous winner of the auction. The winning bid gets set to actualValue, which may be lower than my declared value, and lower than the prior winning bid.

So it looks to me like I could win auctions by declaring very high values but underfunding them (and unsealing my bids late in the reveal period, since my low actually-paid value becomes an easily displaceable highestBid).

I really do apologize for wasting your time if there is a constraint that I am not seeing that prevents this! I've tested nothing, I'm just trying to understand the code.

p.s. Thank you very much Martin Koeppelmann and Stefan George for taking a look at this! Any mistakes are obviously all my own, but they provided an extremely helpful quick review.


HashRegistrarSimplified.sol: Would it be possible to win a bid without depositing sufficient ether? #49
swaldman opened this issue on 14 Mar 2017
https://github.com/ensdomains/ens/issues/49 
 

HashRegistrarSimplified.sol: Would it be possible to win a bid without depositing sufficient ether? · Issue #49 · ensdomains/e

I'm sure I'm just missing something dumb. So apologies in advance! TL; DR: Should the comparisons in HashRegistrarSimplified.sol#L341 and HashRegistrarSimplified.sol#337 and HashRegistrarSi...

github.com

 

 

 

C. Parity 지갑 주소 변경 문제

1–1. Library 스마트 컨트랙트란? (...) 자주 사용되는 스마트 컨트랙트 코드를 한번만 배포하고 이를 이용하는 스마트 컨트랙트들은 delegateCall 을 이용해서 Library 스마트 컨트랙트의 함수를 호출하는 것이다. (...) library 키워드를 이용하면 이더리움을 받을 수 없고 state 변수들을 사용할 수 없다는 제약사항들이 있는데 contract 키워드를 이용해서 Library 용도의 스마트 컨트랙트를 만들면 이러한 제약사항들로 부터 자유롭다. Parity Multisig Wallet Library 스마트 컨트랙트가 Library 용으로 스마트 컨트랙트를 만들긴 했지만 library 키워드를 쓴 것이 아니라 contract 키워드를 사용했고 함수 호출을 위해서 delegateCall 을 사용했다.

1–2. DelegateCall 이란? (...) delegateCall 은 message call 의 특별한 형태이며, 함수 실행은 delegateCall 을 하는 컨트랙트의 컨텍스트에서 이뤄지고 msg.value , msg.sender 는 변경되지않는다. 마찬가지로 storage, balance등도 delegateCall 을 하는 스마트 컨트랙트의 정보를 참조하고 코드 실행만 delegateCall 로 호출당하는 스마트 컨트랙트의 것을 사용한다. (...) delegateCall 을 이용하면 호출하는 스마트 컨트랙트의 컨텍스트를 그대로 이용해서 호출당하는 함수의 코드를 실행시킬 수 있음을 알 수 있다.

1–3. Parity Multisig Wallet 구조
(...) Parity Multisig Wallet 스마트 컨트랙트는 크게 2개의 스마트 컨트랙트로 이뤄져있다. 첫번째는 WalletLibrary 스마트 컨트랙트로 Multisig Wallet 에서 제공하는 입출금같은 핵심기능들의 구현체이다. 다음으로 Wallet 스마트 컨트랙트로 Parity 클라이언트를 이용해서 지갑을 생성하면 지갑마다 Wallet 스마트 컨트랙트가 배포된다. 이번 지갑 동결에서 가장 많은 이더리움이 잠긴 Polkadot의 지갑도 Wallet 스마트 컨트랙트이다.

Wallet 스마트 컨트랙트는 지갑 사용자가 Parity 클라이언트를 이용해서 지갑에서 이더리움을 전송하는 트랜잭션을 발생시키면 WalletLibrary 스마트 컨트랙트의 함수를 delegateCall 한다.

위는 Wallet 스마트 컨트랙트의 fallback function 이다. 여기를 보면 msg.value 가 있는 경우(지갑에 이더리움을 입금하는경우)를 제외한 모든 경우 delegateCall 을 통해서 WalletLibrary 스마트 컨트랙트에 동작을 위임한다. 그래서 WalletLibrary 에 버그가 있을 경우 모든 Wallet 스마트 컨트랙트는 입금외에는 동작할 수 없게 되는 것이다.


https://medium.com/haechi-audit-kr/parity-multisig-wallet-%EB%8F%99%EA%B2%B0%EA%B3%BC-eip999-4dc303cd8e27 
Parity Multisig Wallet 동결과 EIP999
Jason Kim May 12, 2018
 

Parity Multisig Wallet 동결과 EIP999

2017년 11월 6일 02:33:47 PM +UTC, Parity사의 Multisig Wallet Library 스마트 컨트랙트에서 발견된 보안적 결함으로 인해 587개의 지갑과 그 안에 담겨있던 513,774.16 ETH 가 출금이 불가능하게…

medium.com

 

We can see that the first transaction is a call to initWallet (line 216 of WalletLibrary): This function was probably created as a way to extract the wallet’s constructor logic into a separate library. This uses a similar idea to the proxy libraries pattern we talked about in the past. The wallet contract forwards all unmatched function calls to the library using delegatecall, in line 424 of Wallet:

This causes all public functions from the library to be callable by anyone, including initWallet, which can change the contract’s owners.

Unfortunately, initWallet has no checks to prevent an attacker from calling it after the contract was initialized.

The attacker exploited this and simply changed the contract’s m_owners state variable to a list containing only their address, and requiring just one confirmation to execute any transaction:

After that, it was just a matter of invoking the execute function to send all funds to an account controlled by the attacker: This execution was automatically authorized, since the attacker was then the only owner of the multisig, effectively draining the contract of all its funds.


https://blog.openzeppelin.com/on-the-parity-wallet-multisig-hack-405a8c12e8f7/ 
The Parity Wallet Hack Explained
JULY 19, 2017 | IN SECURITY AUDITS | BY SANTIAGO PALLADINO
 

The Parity Wallet Hack Explained - OpenZeppelin blog

Today, we witnessed the second largest hack, in terms of ETH stolen, in the history of the Ethereum network. As of 12:19 pm UTC, the attacker’s account had drained 153,037 ETH from three high-profile multi-signature...

blog.openzeppelin.com