在2018上海區塊鏈國際周技術開放日上,慢霧安全負責人海賊王分享了《DApp 亡靈軍團》,站在攻擊者的角度(特指的亡靈軍團),通過一個個真實的攻擊案例對以太坊、EOS 等區塊鏈生態裡 DApp 的安全窘狀進行了剖析,並丟擲了相關安全深度思考及如何應對。
這張圖總結了智慧合約攻防的各個方面,分為兩大部分:鏈上攻防和鏈下攻防。
鏈上攻防
對於鏈上攻防,海賊王選取了5個方面來詳細剖析,他說到“Talk is cheap. Show me the code.” 並開始展示了原始碼進行詳細解讀。
- 溢位
因為沒有使用 SaceMath導致的溢位造成金額可以在攻擊者構造的資料中被任意控制,從而導致條件判斷成立最終攻擊者成功攻擊了該智慧合約。
此類問題避免方法:在做數字計算的相關程式碼處嚴格使用 SafeMath 進行做算術運算,防止溢位產生。
- 許可權控制
此類許可權控制屬於許可權問題中的一種,由於合約開發人員的不專業導致的代理轉賬函式中未進行授權判斷,從而導致任何人都可以轉走別人的錢。
此類問題避免方法:找專業的開發人員進行合約開發,同時合約開發人員也需要及時提高自己的開發技能以及專業能力,由於區塊鏈的去中心和Token的特殊性很容易因為一個小的漏洞導致專案方從此身敗名裂,所以建議專案上線之前找專業的安全審計團隊出職業的審計報告,此類問題慢霧在審計的過程中一定能夠發現並幫助專案方避免這種尷尬的情況。
- 條件競爭
此類問題為條件競爭導致的跨合約呼叫多次惡意轉賬最終攻擊者成功完成攻擊,在跨合約呼叫方面一直存在很大的問題,由於開發者的專業技能不夠專業或者是合約設計的不合理等問題都會導致此類條件競爭。
此類問題避免方法:瞭解 Solidity 語言本身的函式使用場景以及底層實現,並且在業務邏輯上先扣除需要減去的賬戶金額再新增到對應賬戶並及時清零中間變數的臨時值,避免因為事務的問題導致多入賬導致損失,同時也要了解清楚Solidity中someAddress.call.value()方法 和 someAddress.transfer() 方法還有someAddress.send() 方法的區別。非常不建議使用 someAddress.call.value(),具體原因請大家一定要自己動手去查一下。
- 假充值
假充值這類問題是一個系列,比如XRP的假充值和USDT的假充值等,在這裡只講 ERC20 的假充值問題,這種問題是由於合約開發人員的邏輯判斷程式碼不規範導致在以太坊的區塊瀏覽器中可以看到原本一筆失敗的轉賬狀態為Success交易所在進行充值入賬的時候也沒有嚴格進行狀態的判斷和金額的校驗從而導致了此類問題,此類問題殺傷力強,並且一次足以讓一個交易所虧損上百萬及千萬,無論是開發者還是交易所都需要足夠重視假充值的問題。
此類問題避免方法:合約開發中程式碼判斷邏輯的地方使用 require()或者 assert()進行判斷,如果條件不滿足會直接導致 transfer 的失敗同時狀態也是 fail ,交易所錢包業務開發人員需要在充值的時候注意嚴格校驗轉賬狀態是成功還是失敗,同時對充值金額也進行校驗,確認真的到賬後此筆交易才算成功。
5. 惡意事件
此處需要說明這裡的程式碼僅僅是我們編寫作為演示使用的,目前暫時沒有發現這種真是的攻擊行為。由於區塊鏈的資料都記錄在鏈上,惡意記錄 event 的事件可以直接修改對應的引數,如果此程式碼真是存在則上面講的假充值就真的成了真充值了。
此類問題避免方法:做好 Code Review 和找專業的程式碼審計。
鏈下攻防
對於鏈下攻防,海賊王同樣選取了5個方面來詳細剖析。
- WEB
在之前發生的MyEtherWallet發生的域名劫持事件前,實際上相關的安全機構已經給出了中級風險的提示(如下圖),但是它並沒有對此重視。
終端
對於硬體錢包的安全,也可能存在各種的安全隱患如:是否使用加密晶片,工業設計是否安全,做工是否很簡陋不考慮丟失以及意外破損和能否以應對低溫高溫的情況下正常使用,最終需要考慮你到底是買了個硬體還是真正的硬體錢包?
- 節點
比如以太坊黑色情人節事件,攻擊者通過在網路上通過 P2P協議發現新的以太坊全節點,然後構造好攻擊指令碼做好工程化等待時機對新搭建的全節點進行攻擊,由於很多小白使用者不懂得如何防禦此類攻擊所以到目前位置還是有很多團隊不斷被盜ETH
詳見慢霧的專題頁:4294967296.io/eth214/
專題介紹:mp.weixin.qq.com/s/-Yiul1QtS…
慢霧的防禦方法:mp.weixin.qq.com/s/Kk2lsoQ16…
- 礦工
礦工有可能作惡,針對Dapp,進行選擇性的打包。針對礦池,針對塊代扣攻擊等
- 後端
後端的攻擊舉例為,USDT 假充值。
攻擊步驟為:
1)向交易所錢包構造併發起⽆效(虛假)轉賬交易;
2)由於邏輯判斷缺陷交易所將⽆效交易⼊賬並計⼊到⽤戶在交易所的資⾦賬戶;
3)⽤戶發起提幣;
4)交易所處理⽤戶提幣將幣打到⽤戶⾃⼰錢包地址;⽤戶⾃⼰充幣環節USDT 沒有任何損失,提幣環節交易所把⾃⼰真實的 USDT 幣打給⽤戶,造成交易所損失。
這背後的原理是,
USDT 是基於 Bitcoin 區塊的 OMNI 協議資產型別,利⽤ Bitcoin 的 OP_RETURN 承載相關交易資料;
Bitcoin 本⾝並不會校驗 OP_RETURN 資料的“合法性”,可以是任意資料;
Bitcoin 交易當區塊確認數達到 6 的時候,就會被Bitcoin 節點承認;
那麼,USDT 的交易在 OMNI 的節點上如何被確認的呢?
int64_t nBalance =
getMPbalance(sender,property, BALANCE);
複製程式碼
說明:通過上⾯的程式碼來看,OMNI內部有⾃⼰的⼀套基於地址的記賬模型,通過地址可以獲取地址的餘額。
因此,為了避免以上問題。
⼀筆 USDT 的交易合法的,要⾄少滿⾜以下兩個條件:
(1)要通過⽐特幣的交易來構造,要符合⽐特幣的餘額驗證(BTC)及交易規則驗證
(2)要通過 USDT ⾃⼰的餘額(USDT)驗證
對於USDT 假充值,當餘額不足時會發生什麼?
{"amount": "28.59995822",
"block": 502358,
"blockhash": "0000000000000000005968fa48c49d7c4fb2363369d59db82897853fd937c71a",
"blocktime": 1514985094,
"confirmations": 37854,
"divisible": true,
"fee": "0.00200000",
"flags": null,
"invalidreason": "Sender has insufficient balance",
"ismine": false,
"positioninblock": 301,
"propertyid": 31,
"propertyname": "TetherUS",
"referenceaddress": "1Po1oWkD2LmodfkBYiAktwh76vkF93LKnh",
"sendingaddress": "18DmsHjHU6YM2ckFzub4pBneD8QXCXRTLR",
"txid": "1b5c80f487d2bf8b69e1bbba2b1979aacb1aca7a094c00bcb9abd85f9af738ea",
"type": "Simple Send",
"type_int": 0,
"valid": false,
"version": 0 }複製程式碼
最後,他提醒道,有些黑客非常有心機,在一些百度回答等貼上錯誤的程式碼,如果程式設計師不仔細檢查或者對智慧合約不熟悉,直接複製貼上程式碼,則容易遭受攻擊。要如何提升安全呢?首先開發人員擁有安全開發意識,其次還是要找專業的安全團隊做專業的審計。
本文為以太中文網原創釋出。