針對區塊鏈安全問題,鏈安科技團隊每一週都將出智能合約安全漏洞解析連載,希望能幫助程序員寫出更加安全牢固的合約,防患於未然。

引子:

秦人不暇自哀,而後人哀之;後人哀之而不鑑之,亦使後人而復哀後人也。

—— 《阿房宮賦》

上回講到 溢出漏洞引增發,幣值一落千萬丈,黑客造假本領大,SafeMath 來救駕。

眼觀目前鏈圈幣圈行進的步伐越來越急促,似乎我們已無暇回首當初那些輝煌與挫敗,只能低着頭繼續跟從與追趕。回首 2016 年的戰場,似乎一切都已冰封,少有人記得這裏發生過什麼。

我們將當時的拒絕服務攻擊事件挖掘出來,並將原理分析和漏洞修復技巧與大家分享,時刻牢記過去的教訓。

微信圖片 _20180726104010

事件回顧

2016 年 2 月 6 日至 8 日 The King of the Ether Throne (以下簡稱 KotET)“紛爭時代”(Turbulent Age)期間,許多遊戲中的退位君王的補償和未接受款項無法退回用戶玩家的錢包 [1]

具有諷刺意味的是同年 6 月,連龐氏騙局 GovernMental 的合約也遭遇 DoS 攻擊,當時 1100 以太幣是通過使用 250 萬 gas 交易獲得 [2],這筆交易超出了合約能負荷的 gas 上限,帶來交易活動的暫停。

1

無論是蓄意破壞交易正常流程還是阻塞交易通道,都用到了一個互聯網時代已經盛行已久的攻擊方式——DoS,也就是我們所說的拒絕服務攻擊。這種攻擊方式可以讓合約執行的正常的交易操作被擾亂,中止,凍結,更嚴重的是讓合約本身的邏輯無法運行。

何爲 DoS

DoS 是 DenialOfService,拒絕服務的縮寫 [3],從字面上來理解,就是用戶所需要的服務請求無法被系統處理。

2

打個比方來形容 DoS,火車站是爲大家提供乘車服務的,如果想要 DoS 火車站的話,方法有很多,可以佔用過道不上車,堵住售票點不付錢,阻撓列車員或者司機不讓開車,甚至用破壞鐵軌等更加極端的手段來影響車站服務的正常運營。

過去針對互聯網的 DoS 有很多種方法,但基本分爲三大類:利用軟件實現的缺陷,利用協議的漏洞,利用資源壓制 [3]。

此外還有 DDoS,稱爲分佈式 DoS,其區別就是攻擊者利用遠程操控的計算機同時向目標發起進攻,在上面的比喻中可以理解爲僱傭了幾百個地痞流氓來做同樣的事影響車站的運作。

3

智能合約 DoS 攻擊原理分析及相應漏洞修復

無處不在的 DoS 當然也會對基於 Solidity 語言的以太坊合約產生威脅。

針對智能合約的 DoS 攻擊屬於利用協議漏洞進行的手段,具體的攻擊方法有三種 [4],其目的是使合約在一段時間或者永久無法正常運行。通過 DoS 攻擊,可以使合約中的 Ether 永遠無法提取出來,區塊成爲“冰凍廢土”。

4

在對於原始代碼進行分析後,我們發現 KotET 事件中對君王稱號進行鎖定的 DoS 攻擊屬於:

1.通過 (Unexpected) Revert 發動 DoS

如果智能合約的狀態改變依賴於外部函數執行的結果,又未對執行一直失敗的情況做出防護,那麼該智能合約就可能遭受 DOS 攻擊 [5]。

我們使用案例合約還原 KotET 的競拍機制,進行模擬分析:

5

以上案列合約是一個簡化版的 KotET 的競拍爭奪王位的合約,如果當前交易的攜帶的 ether 大於目前 highestBid,那麼 highestBid 所對應的 ether 就退回給 currentLeader,然後設置當前競拍者爲 currentLeader,currentLeader 改爲 msg.value。但是當惡意攻擊者部署如下圖所示合約,並通過合約來競拍將會出現問題:

6

攻擊者先通過攻擊合約向案例合約轉賬成爲 currentLeader,然後新的 bider 競標的時候,執行到 require(currentLeader.send(highestBid)) 會因爲攻擊合約的 fallback() 函數(這裏指 function () external payable 函數)無法接收 ether 而一直爲 false,最後攻擊合約以較低的 ether 贏得競標。

漏洞修復

如果需要對外部函數調用的結果進行處理才能進入新的狀態,請考慮外部調用可能一直失敗的情況,也可以添加基於時間的操作,防止外部函數調用一直無法滿足 require 判斷。

對 GovernMental 事件中交易的 gas 值遠遠超出天際的原理分析,其屬於通過區塊 Gas Limit 發動 DoS。

2. 通過區塊 Gas Limit 發動 DoS

一次性向所有人轉賬,很可能會導致達到以太坊區塊 gas limit 的上限。以太坊規定了每一個區塊所能花費的 gas limit,如果超過交易便會失敗。

即使沒有故意的攻擊,這也可能導致問題。然而,最爲糟糕的是如果 gas 的花費被攻擊者操控。在先前的例子中,如果攻擊者增加一部分收款名單,並設置每一個收款地址都接收少量的退款。這樣一來,更多的 gas 將會被花費從而導致達到區塊 gas limit 的上限,整個轉賬的操作也會以失敗告終。如以下簡化版案例合約所示:

7

這個案例合約遍歷可被人爲操縱的 investors[] 數組。攻擊者可以創建許多賬戶,使得 investors[] 數組變的很大,使得執行 for 循環所消耗的 gas 超過塊 gas 極限,使得 distribute 函數一直處於 out-of-gas (OOG)狀態,而一直無法執行成功,合約正常功能實現受到影響。

漏洞修復

合約不應該循環對可以被外部用戶人爲操縱的數據結構進行批量操作,建議使用取回模式而不是發送模式,每個投資者可以使用 withdrawFunds 取回自己應得的代幣;

如果實在必須通過遍歷一個變長數組來進行轉賬,最好估計完成它們大概需要多少個區塊以及多少筆交易。然後你還必須能夠追蹤得到當前進行到哪以便當操作失敗時從那裏開始恢復,舉個例子:

8

3.所有者操作發動 DoS

另外, 我們聯繫之前提到的 Owner 權限過大,“超中心化”的問題,發現目前很多代幣合約都有一個 Owner 賬戶,其擁有開啓 / 暫停交易的權限,如果對 owner 保管不善,代幣合約可能被一直凍結交易,導致非主觀的拒絕服務攻擊,例如如下 owner 權限中的功能:

9

此 owner 權限的侷限性在於,在 ICO 結束後,如果特權用戶丟失其私鑰或變爲非活動狀態,Owner 無法調用 finalize(),用戶則一直不可以發送代幣,即令牌生態系統的整個操作取決於一個地址。

漏洞修復

可以設置多個擁有 owner 權限的地址,或者設置暫停交易的期限,超過期限就可以恢復交易,如:require(msg.sender == owner || now > unlockTime)

以銅爲鏡,可以正衣冠

以古爲鏡,可以知興替

以人爲鏡,可以明得失

  1. 對於調用外部函數的代碼一定要考慮周全,對於例外情況的判定要加入代碼中。
  2. 遍歷變長數組來逐個支付的方法需要全方位考慮和估計。合約中不應存在外部人員操縱的成分。
  3. 強調再三的去中心化特徵也應該應用到 Owner 權限這個概念上來。

從被 DoS 到交易系統異常,到項目被冰封直至被遺忘,以上的例子也經歷了互聯網發展初期所遭受的苦難,但是隻要我們銘記教訓,就能穩固地保持區塊鏈技術的發展。

引用:

  1. Post-Mortem Investigation
  2. A survey of attacks on Ethereum smart contract
  3. 張昆蒼等.操作系統原理 DOS 篇(第 2 版):清華大學出版社,2000 年 9 月
  4. https://blog.sigmaprime.io/solidity-security.html
  5. 以太坊智能合約 — 最佳安全開發指南 通過 (Unexpected)Throw 發動 DoS
  6. 以太坊智能合約 — 最佳安全開發指南 通過區塊 GasLimit 發動 DoS

相關閱讀:

鏈安團隊漏洞分析連載第一期一一溢出漏洞

版權聲明:
by
nc"
sa
作者保留權利。文章爲作者獨立觀點,不代表巴比特立場。
發文時比特幣價格 ¥54323.15

來源鏈接:www.8btc.com