Slither漏洞偵測/程式掃描 預防智慧合約遭駭

隨著區塊鏈技術的成熟,去中心化系統已逐漸融入大眾的數位生活中。當使用者在平台上購買一枚熱門的非同質化代幣(NFT)、參與Web3遊戲的寶物交易,或是將手頭的加密資產投入去中心化金融(DeFi)平台賺取額外收入時,背後默默運作的正是「智慧合約」。作為區塊鏈的核心技術,它透過自動執行的程式碼取代傳統的銀行或仲介機構,在提升交易效率與透明度的同時,也大幅改變了原本的信任模式,成為推動Web3發展的關鍵驅動力。然而,這份便利背後所隱藏的安全問題也逐漸浮上檯面。由於智慧合約具備「一經部署便極難修改」的特性,若程式碼在設計階段存在微小缺陷,就會成為駭客攻擊的缺口。

本文將介紹智慧合約的基礎知識,深入分析合約在程式碼設計與實作階段常見的典型漏洞。並實際操作自動化安全檢測工具,說明如何透過靜態分析技術在部署前發掘潛在弱點。最後,依循OWASP SCSVS安全指南歸納各類漏洞的風險層級,透過實務情境演練,理解如何落實合約防禦,從根本上提升智慧合約的資安防護。

智慧合約背景知識

首先介紹什麼是智慧合約、用何種語言編寫以及運作方式,並說明智慧合約在區塊鏈系統中的作用與特性。

什麼是智慧合約
智慧合約最早由密碼學家Nick Szabo於1994年提出,其核心構想為「以程式碼實現並自動執行合約條款」,但受限於當時的技術,智慧合約僅停留在理論層面,直到以太坊(Ethereum)平台於2015年推出,透過在區塊鏈上提供可程式化的執行環境,實現智慧合約的構想並實際應用。

智慧合約本身是一段部署於區塊鏈上的程式碼,當程式設定的條件被滿足後,合約會自動執行對應的邏輯,像是轉帳、狀態更新等,且智慧合約的執行結果會被記錄於區塊鏈帳簿中,且區塊鏈網路中的每一個節點都會在自己的本地環境運算,只有當網路中的大多數節點確認結果一致後,合約的狀態更新才會被記錄在區塊鏈上。簡單來說,就是當設定的條件達成時,程式會自動執行合約內容。

智慧合約運作方式
智慧合約的核心邏輯是以簡單的If邏輯,遵循程式中的「若…則…」,先在合約中設定條件,再部署到區塊鏈上,自動監測是否達成條件後執行。從技術角度看,智慧合約可以視為一個狀態轉換函數,從原來的帳本狀態,在執行合約後,轉換到新狀態。

在以太坊虛擬機執行的智慧合約,為了防止惡意程式利用無限迴圈或資源濫用,以太坊使用Gas機制,限制每筆交易可使用的計算資源,當Gas耗盡時,交易將無法執行。

智慧合約的編寫語言
智慧合約的編寫使用特定的程式語言,且通常被設計成可以在區塊鏈上的虛擬機執行,不同的區塊鏈平台也會選擇不同的語言,目前主流的三種為Rust、Vyper及Solidity。本文將以Solidity編寫的智慧合約,演示後續的檢測及修復。

‧Rust:一種效能高、強調記憶體安全性的程式語言,在編寫階段就能檢查多數記憶體與型別錯誤但開發門檻較高,學習也相對較難,用於Solana區塊鏈平台。

‧Vyper:以太坊平台支援的智慧合約程式語言,設計架構較為簡潔及安全,語法與Python類似,且刻意移除可能造成安全風險的進階功能,在功能上相對受限,但合約複雜度較低,減少了程式邏輯錯誤的可能性。

‧Solidity:為目前最廣泛使用的智慧合約程式語言,語法設計上類似JavaScript與C++,是屬於物件導向的語言,提供許多專為智慧合約設計的語法結構,主要用於開發此語言的以太坊平台及相容的區塊鏈平台,例如BNB Chain和Polygon。由於Solidity開發較為成熟,相關的安全函式庫與檢測工具相對較多也更完善,因此在資安研究與漏洞分析中,是最常見的語言。

智慧合約的特性
智慧合約具備以下幾種特性:

‧去中心化性:智慧合約部署於區塊鏈後,由多個節點共同維護與執行,不受單一機構控制。
‧不可竄改性:合約程式碼與執行結果一經部署,原則上無法任意修改,為確保系統的可信度,若合約有安全風險將無法修改。
‧自動化執行:合約條件一旦成立,系統就會自動執行合約邏輯,降低人為干預與操作風險。
‧透明與可驗證性:多數區塊鏈的智慧合約程式碼與交易紀錄都可公開查詢,有助於審查與驗證。

智慧合約常見風險類型及預防建議

在OWASP的SCSVS指南所歸納的安全要求中,智慧合約常見風險可分為五種,這些風險大多來自程式設計缺陷、邏輯錯誤或對區塊鏈執行環境理解有誤,且合約部署上鏈後,將難以修補並造成重大財產損失。

重入攻擊(Reentrancy Attack)
重入攻擊之所以列為首要風險,是因其為智慧合約中最具代表性且危害極高的漏洞類型之一。當合約在尚未完成關鍵狀態更新前即進行外部呼叫(例如轉帳至外部地址或呼叫其他合約),攻擊者便可能透過惡意合約的回呼機制(Callback)重複觸發同一函式,使合約在狀態尚未更新完成的情況下被「重新進入」(Re-enter),進而造成資產被多次提領或合約狀態不一致,導致重大財務損失。依OWASP智慧合約安全標準SCSVS之分類,重入攻擊屬於SCSVS Level 2(中等安全性)需要重點驗證的風險項目之一,亦為去中心化金融(DeFi)攻擊事件中常見的核心漏洞之一。

圖1 帶有重入漏洞的智慧合約。

含有重入攻擊漏洞的合約如圖1所示,方框第9行的withdraw()函數執行了一個外部呼叫,將ETH發送到msg.sender,就會給攻擊者可以插入惡意程式碼的入口。外部呼叫後,才更新關鍵變數balances[msg.sender],導致攻擊者可以在第15行的balances[msg.sender]=0執行前,再次呼叫withdraw(),而因balances[msg.sender]尚未歸零,導致攻擊者可以重複提款,造成資金損失。

SCSVS建議採取預防措施是Checks-Effects-Interactions(CEI)原則,說明如下:

‧check:執行所有先決條件檢查,例如是否有執行此操作的權限。
‧effect:執行所有狀態變更操作,例如更新合約內部餘額。
‧interaction:進行所有外部互動,代幣發送到其他合約,並確認前面的狀態都已更新完成。即使外部呼叫嘗試重新進入合約,合約的重要狀態已確認更新,阻止攻擊者重複操作。

三者的關係就像是民眾到銀行櫃檯提款時,工作人員會先請民眾出示身分證或相關證件,並確認銀行帳戶及帳戶內是否有足夠金額,此為「check」;完成驗證後,工作人員會在銀行系統將提款的金額從民眾的帳戶中扣除,完成帳戶的更新,此為「effect」;最後工作人員才會將民眾欲提款的金額,以點鈔機確認金額正確後,將鈔票交給民眾,此為「interaction」。

存取控制失效(Access Control Issues)
智慧合約未能正確實施權限驗證時,允許未經授權的實體執行敏感操作的漏洞,攻擊者得以提取權限,任意鑄造代幣、轉移資產或更改關鍵參數,如圖2所示。

圖2 帶有存取控制漏洞合約。

如圖3所示,合約中destroy()函數,允許任何人調用它,並觸發合約的自毀機制。因攻擊者可隨時銷毀合約,讓合約使用者無法使用。

圖3 Suicidal合約。

SCSVS提供一些預防建議,像是最小權限原則僅提供完成執行所需的權限。

例如,使用msg.sender而非tx.origin進行授權,msg.sender關注合約現在的交易對象,使權限能逐層傳遞;而tx.origin是關注初始發起交易的人,可能導致合約被釣魚攻擊,使用msg.sender避免來自惡意合約的潛在濫用;實施白名單限制並驗證發送者為外部擁有帳戶。

整數溢位與下溢(Overflow/Underflow)
算術運算超過資料類型的最大值或最小值時,可能出現意外行為,造成餘額計算錯誤、資產被無限制增減。在Solidity 0.8以後的版本,算術運算預設受到Checked Arithmetic,也就是內建的機制保護,會自動避免整數溢位或下溢漏洞,但在使用unchecked{}區塊來節省燃料時,則必須管理其中的算術運算,以防止意外的溢位或下溢。

SCSVS同樣提供在Solidity 0.8版本以前的預防建議,第一個是驗證整數算術運算不會超出設定界限,防止整數溢位或下溢漏洞;第二個是使用安全數學庫透過導入SafeMath(一個Solidity函式庫),用來包裝整數運算(加、減、乘、除,如表1所示),在發生溢位或下溢時自動避免錯誤結果。使用指令「using SafeMath for uint256;」。Solidity 0.8版本以後,語言內建SafeMath不需要再導入,只在需要用到uncheck函數時須特別注意,在高風險合約時配合動態、靜態分析即可防範此漏洞。

燃料最佳化(Gas Optimization)
在智慧合約中,Gas不僅決定交易成本,也直接影響合約功能是否能被順利執行,進而關係到整體可用性與安全性。若合約在設計階段未妥善評估Gas消耗,可能導致部分函式在特定情境下因超出Gas上限而執行失敗(Out-of-Gas),使交易頻繁revert,甚至造成合約功能無法使用。此類問題亦可能被攻擊者利用形成拒絕服務攻擊(DoS),例如透過刻意觸發高耗能流程、操控資料結構膨脹,或使合約進入需要大量迴圈運算的狀態,進而提高合約操作成本,導致一般使用者無法完成交易或被迫放棄操作。由此可見,Gas消耗管理不僅是效能與成本議題,更是智慧合約安全設計中不可忽視的重要環節。

代幣特定漏洞(Token-Specific Vulnerabilities)
在智慧合約中,Token代幣的安全性,通常直接影響使用者資產與整體系統穩定度主要與ERC20、ERC721以及ERC1155的代幣標準的管理有關,導致重複花費、轉帳失敗或合約整合漏洞,在實際操作中極易被忽略,但影響重大的風險。

如圖4所示,合約用於中轉ERC-20代幣,定義了標準ERC20的transfer函式,然而,實務上部分ERC-20代幣可能未完全遵循EIP-20規範,在轉帳失敗時不會拋出revert表示錯誤,而僅有回傳false表示失敗。且若合約端忽略了檢查回傳值,便可能在轉帳未成功的情況下仍誤判操作已完成,導致帳務狀態與實際資產流向不一致,進而引發資產損失或後續邏輯錯誤。因此,在處理ERC-20轉帳時,應確實驗證回傳結果,或採用如OpenZeppelin SafeERC20等安全封裝機制,以降低因代幣實作差異所造成的風險。

圖4 Unchecked-transfer漏洞合約。

模擬情境演練

辰辰是一位對加密貨幣操作有經驗的人士,平時常透過去中心化金融平台做理財和投資。在朋友小文的推薦下,得知一個新推出名為「上倫」的去中心化理財平台,特色是資金全都由智慧合約進行管理,且客戶能隨時自由地存入和提領資產,辰辰在了解區塊鏈技術之後,覺得沒什麼大問題,於是他將部分的加密資產存入「上倫」平台的智慧合約中。

一開始,平台運作十分正常,能夠順利完存入和提領的操作,帳戶餘額也隨操作如期更新。但在某日,辰辰提領完較大金額的資產後發現,發現實際收到的金額與合約中顯示的餘額有不太尋常的地方,在查詢交易紀錄後發現同一次交易中,合約有多次對外轉出資金的操作,而辰辰並無印象有轉出資金,而察覺合約似乎有問題,於是請教加密貨幣領域的專家星星,幫助他解決這次危機。

在平台的資安疑慮被新聞媒體公開後,平台證實了智慧合約遭到駭客攻擊。經調查後發現,合約本身的提領流程存在設計缺陷,使駭客能在資金轉出後、合約內部餘額尚未更新前,重複呼叫提領函式,使資金被多次轉移,合約內資金迅速減少,對使用者產生不少的損失。

智慧合約最初發展的核心目的,是為了在沒有第三方仲介的環境下建立起堅實的信任基礎,然而令人沒想到的是,這項技術如今卻因為程式碼的資安漏洞,反而帶來了更嚴重的信任崩潰與嚴重的安全隱憂。事件過後,辰辰還有許多加密貨幣的投資,為了避免下次遇到同樣的危機而不能自救,於是請專家星星傳授更多智慧合約的相關風險的知識及合約程式相關的問題,於是星星來到辰辰的工作室建立模擬環境,重現該智慧合約的運作方式,並熟悉智慧合約的檢測工具,星星建議辰辰選擇模擬此次事件的重入漏洞合約,用靜態分析工具Slither進行檢測,實際的安裝及實作過程如下:

安裝Solidity和Slither
進行智慧合約的編寫,須先建立Solidity編寫環境,於電腦終端機中進行,並確認已安裝Python3(3.12.3版本),完成Solidity安裝後,即可安裝Slither(0.11.3版本)。

安裝Solidity
使用Python套件管理工具pip3安裝Solidity編譯器版本管理工具solc-select。此工具可協助使用者下載並切換不同版本之Solidity編譯器,可以在不同合約版本需求下分析合約。指令為「pip3 install solc-select」。完成solc-select安裝後,即可用它下載不同版本的Solidity。辰辰選擇安裝Solidity 0.8.2版本並設為目前使用的Solidity版本。指令為「solc-select install 0.8.2」及「solc-select use 0.8.2」。最後,透過查詢Solidit版本的方式,確認是否成功建立,指令為「solc –version」,結果如圖5所示。

圖5 驗證Solidity安裝版本。

安裝Slither
與安裝Solidity相同,使用pip3安裝Slither分析工具,指令為「pip3 install slither-analyzer」。完成安裝後,即可查詢Slither版本資訊,驗證工具是安裝成功,指令為「slither –version」,結果如圖6所示。

圖6 驗證Slither安裝版本。

實作Slither分析
接下來,進行實作Slither分析。

建立實作目錄
使用mkdir指令建立一個新的目錄作為實作環境,指令為「mkdir slither_test」。然後,使用cd指令進入該目錄,讓接下來的檔案建立及分析皆在指定目錄中進行,指令為「cd slither_test」。成功進入目錄,會在終端機指令輸入的地方顯示,如圖7所示,右下角的藍色字體即為成功進入目錄。

圖7 成功進入目錄後的指令列。

合約漏洞分析
完成目錄建立後,即可開始進行實作分析。使用終端機內建的文字編輯器「nano」建立一個智慧合約檔案,模擬實際可能存在漏洞的智慧合約,本次實作以重入漏洞為例,執行指令為「nano Reentrancy-eth.sol」,將欲分析的合約存入建立的檔案中,如圖8所示。

圖8 模擬重入漏洞的合約。

接著返回終端機,使用Slither對合約進行分析檢測,指令為「slither Reentrancy-eth.sol」,分析結果如圖9所示,為紅色高風險,分析結果可以很明顯地看到重入漏洞的特徵,在第14行的msg.sender.call{value: amount}()函式先執行外部互動,才在第16行執行內部狀態更新,給攻擊者有重複提款的機會。

圖9 重入漏洞合約的分析結果。

辰辰在完成使用Slither成功分析重入漏洞的合約後,星星告訴辰辰在OWASP的網站中,可以找到與智慧合約風險有關的安全標準SCSVS,收錄各種有關智慧合約的潛在風險,以及漏洞的修復與預防方法,若能對此做一些功課,就能更有效地避免再次遇到同樣問題,導致更大的損失。

在星星的建議下,辰辰開始了解SCSVS的內容,發現其中就有此事件中重入漏洞的內容,發現重入攻擊是智慧合約相當典型的漏洞之一,SCSVS標準中提供的防護方法,主要分為兩種,第一個是針對程式邏輯,使用Checks-Effects-Interactions(CEI)原則,在做任何外部呼叫前,先完成內部狀態的檢查與更新,如圖10所示,將合約程式碼中的balances[msg.sender]=0移至與外部合約交互前,遵守CEI原則,可以發現再次經Slither檢測後,如圖11所示,已從紅色高風險警報轉為綠色,表示合約重入漏洞的風險被修復,屬安全的檢測結果,此方法能最有效的避免被重入攻擊。

圖10 依照CEI原則修復的合約。
圖11 CEI原則修復結果。

第二種是使用重入保護機制,在函式中使用NonReentrant修飾符,可以在執行任何其他邏輯或權限檢查,合約就鎖定了重入保護狀態,故NonReentrant修飾符必須套用於該函式中的其他修飾符之前,確保在執行任何可能導致外部互動的邏輯或檢查其他條件前,重入保護機制就已經到位並生效,如圖12所示,方框部分即為修飾符。

圖12 加入NonReentrant修飾符的合約。

然而,辰辰發現使用重入保護機制加入修飾符後,Slither仍然將其判定為有重入風險,星星解釋出現這樣狀況的原因在於此修飾符在靜態下無法展現其功能,實際動態使用上才能顯現其防護機制,與直接修復程式邏輯不同,靜態分析仍會將其視為有邏輯上的漏洞,因此最好的方式還是使用CEI原則再配合修飾符,就能夠發揮最完整的保護作用,避免意外情形的發生。

經過此事件後,平台暫停了相關合約功能,進行全面的安全檢查及修復。辰辰也在星星的指導下對智慧合約的相關知識,有更深入的認識,除了事件中的重入漏洞外,他也詳閱了其他在SCSVS標準的漏洞,對智慧合約的相關風險及預防,有初步的認識,事後也對其他公司的智慧合約進行分析。

結語

隨著攻擊技術的不斷進化,主動防禦與持續的安全審查已具有不可忽略的必然性與重要性。智慧合約的資安風險多源自於設計與實作階段對細節的疏忽。唯有正視合約防禦的必要性,將安全設計加入開發生命週期的核心,並在程式邏輯上嚴格檢查,才能在享受智慧合約帶來便利與創新的同時,建構出真正穩固且值得信賴的去中心化生態系。

<本文作者:社團法人台灣E化資安分析管理協會(ESAM, https://www.esam.io/)
中央警察大學資訊密碼暨建構實驗室 & 情資安全與鑑識科學實驗室(ICCL and SECFORENSICS)
1998年成立,目前由王旭正教授領軍,並致力於資訊安全、情資安全與鑑識科學,資料隱藏與資料快速搜尋之研究,以為人們於網際網路(Internet)世界探索的安全保障(https://hera.secforensics.org/)>