麻豆国内精品欧美在线-麻豆国内精品久久久久久-麻豆国产在线观看一区二区-麻豆国产在线观看免费-麻豆国产原创-麻豆国产一区二区在线观看

警惕!Solidity缺陷易使合約狀態(tài)失控區(qū)塊鏈

巴比特資訊 2018-08-07 10:45
分享到:
導(dǎo)讀

。野指針問題是Solidity語言的最初設(shè)計欠缺考慮,而且Solidity編譯器為了向前兼容,對這類安全問題僅采取警告提示,而開發(fā)者往往又很容易忽視這些提示,最終導(dǎo)致問題代碼部署上線。

本文以蜜罐合約和 BancorLender 合約為例,詳細(xì)介紹 Solidity 語言中「未初始化的 storage 指針」問題,并追蹤 Solidity 編譯器關(guān)于此問題的開發(fā)進(jìn)展。

安比(SECBIT)實驗室在 BancorLender (0x2d820ea3A6b9302c500feeb7F6361bA1DdfA5aBa) 合約中發(fā)現(xiàn)野指針問題(uninitialized-wild-pointer)。該合約中的一個狀態(tài)變量會意外地被另一個函數(shù)修改,偏離原本設(shè)計意圖。目前項目方不明確。建議項目方應(yīng)立即廢棄該合約,并重新發(fā)布修復(fù)后的合約。野指針問題是 Solidity 語言的最初設(shè)計欠缺考慮,而且 Solidity 編譯器為了向前兼容,對這類安全問題僅采取警告提示,而開發(fā)者往往又很容易忽視這些提示,最終導(dǎo)致問題代碼部署上線。

下面我們通過一個蜜罐例子來解釋「未初始化的 storage 指針」這個缺陷。

蜜罐合約:別人看中的是你的本金

1111

在計算機(jī)領(lǐng)域,蜜罐(Honeypot)通常指故意偽裝成看似有利用價值并故意留有 bug 的系統(tǒng),用來吸引黑客攻擊,從而達(dá)到分析、監(jiān)控、收集證據(jù)、拖延攻擊等目的。

而以太坊主網(wǎng)上存在這樣一類游戲合約:以高額回報為誘餌,并故意露出破綻,讓參與者誤認(rèn)為自己有很高的概率可以獲勝,誘導(dǎo)參與者轉(zhuǎn)入以太參與游戲而損失本金。通常稱這類合約為“蜜罐合約”。

“蜜罐”這個詞,其實很形象:罐子里有可口的蜂蜜,吸引著熊去吃,但周邊其實有暗藏的陷阱,真正目的是為了抓住熊。

“蜜罐合約”的部署者通常利用各種技巧使代碼部分特殊用途不易被參與者發(fā)現(xiàn),利用當(dāng)中的信息不對稱,使參與者產(chǎn)生錯誤判斷,從而被騙取本金。

「未初始化的 storage 指針」正是“蜜罐合約”部署者最常用的一種技巧。這個問題源于 Solidity 語言以及編譯器設(shè)計上的失誤。

我們結(jié)合下面這個名為 Honeypot 的簡化合約說明。這是一個競猜合約,參與者調(diào)用 guess() 接口,傳入 _number 數(shù)字進(jìn)行競猜,如果猜的數(shù)字等于合約中的 luckyNum,則競猜成功,參與者可獲取兩倍回報。

聰明的你可以仔細(xì)思考一下,競猜數(shù)字 _number 應(yīng)該填多少?

honeypot-contract

終極答案是 42 嗎?由于變量 luckyNum 在最開始(第 2 行)被賦值 42,并且沒有其他被賦值操作,因此絕大多數(shù)人都會猜 42。

然而這個合約極具迷惑性,42 并不是正確答案。到底哪里出了問題?變量 luckyNum 什么時候被修改了?

讓我們來理一理:函數(shù) guess() 先把參與者的地址和競猜數(shù)字放入 gameHistory 數(shù)組中保存(第 12 ~ 15 行)。而數(shù)組 gameHistory 由 Game 結(jié)構(gòu)體(Struct)構(gòu)成。函數(shù)開始先通過 Game game 聲明了一個結(jié)構(gòu)體變量 game(第 12 行),再分別對成員變量進(jìn)行賦值(第 13 ~ 14 行),最后將變量 game 塞到 gameHistory 數(shù)組中(第 15 行)。

看著“似乎”沒毛病。然而,這里有很嚴(yán)重的問題。

傳統(tǒng)編程語言中,我們在函數(shù)內(nèi)部申明一個變量,通常默認(rèn)是局部變量。但 Solidity 在語言設(shè)計上埋了個坑,在此處反直覺地默認(rèn)讓引用類型(Reference Type)變量 game(第 12 行)存儲位置為 storage,因此對變量 game 的修改,作用范圍是“全局”的。并且對于未初始化的 storage 指針(類似傳統(tǒng)語言中的空指針),Solidity 默認(rèn)其指向 storage 的起始地址,即指向合約開頭定義的狀態(tài)變量(第 2 ~ 3 行)。

變量 luckyNum 值不是 42,那么到底是多少呢?

Solidity 將源碼中的狀態(tài)變量(常量除外),根據(jù)一定規(guī)則,按照出現(xiàn)順序依次排列存儲在 storage 中。

而 luckyNum 變量正是這個合約中第一個被定義的狀態(tài)變量,占據(jù)了 storage 的開始位置(slot 0×00)。

image.png

因此以上代碼中的賦值操作會分別更新 storage slot 0×00 ~ 0×01 上的值,即將 luckyNum 值設(shè)為 msg.sender,將 last 值設(shè)為 _number。

storage-code

如果參與者猜 42,則會白白丟幣。

luckyNum 的正確答案應(yīng)該是調(diào)用者自己的地址。

安比(SECBIT)實驗室發(fā)現(xiàn),有很多人會利用 Solidity 語言以及編譯器的這種“特性”,再加上其他復(fù)雜的干擾條件或故意漏出的破綻,部署“蜜罐”合約欺騙其他人。在大部分案例里,參與者根本無法獲勝,而部署者有權(quán)限將合約里的幣全部轉(zhuǎn)走,并且通常中招者還具備不少智能合約安全常識。

再如另一個名為 OpenAddressLottery 的彩票合約(0x741F1923974464eFd0Aa70e77800BA5d9ed18902),根據(jù)參與者的地址“隨機(jī)”生成一個 0 至 7 間的整數(shù)。合約聲稱任何人均有八分之一的概率中獎而贏走 7 倍于投注金額的以太幣,中獎條件為生成的數(shù)等于代碼中的 LuckyNumber [2]。

OpenAddressLottery

與第一個例子類似,代碼中標(biāo)明了 LuckyNumber 值為 7(第 11 行),并且看上去沒有其他方法可以修改該變量。目前以太坊智能合約中很難生成無法預(yù)測的隨機(jī)數(shù)(其實這是部署者故意留的破綻)。有智能合約安全知識的人可能會躍躍欲試,利用在其他智能合約中調(diào)用的方法來預(yù)測隨機(jī)數(shù),從而獲取獎勵(不可能的,這輩子都不可能)。

注意 forceReseed() 函數(shù)中的 SeedComponents s(第 16 行),這與前面的問題代碼如出一轍,并且該函數(shù)只有 owner 才能調(diào)用。蜜罐部署者可利用該函數(shù)中第 20 行的 s.component4 = tx.gasprice * 7 來修改 LuckyNumber 為想要的任意值,從而使任何人都無法中獎。蜜罐部署者最終利用 selfdestruct() 將合約自毀,并把受害者轉(zhuǎn)入的以太幣轉(zhuǎn)出至自己的地址。

類似的蜜罐合約在以太坊主網(wǎng)上存在不少(不完全列表如下),大家牢記這個知識點,千萬別中招。

image.png

問題合約 BancorLender:從蜜罐到安全漏洞

除了“蜜罐合約”,「未初始化的 storage 指針」問題還會嚴(yán)重影響智能合約代碼質(zhì)量,導(dǎo)致合約代碼無法正常執(zhí)行,甚至留下安全漏洞。

結(jié)合 BancorLender 代碼具體分析。

code

BancorLender 合約 offerToLend() 函數(shù)中聲明了一個結(jié)構(gòu)體(struct)變量 BorrowAgreement agreement。

顯然開發(fā)者原本想將 agreement 作為局部變量使用,但未初始化的 storage 指針會指向第 1035 行定義的狀態(tài)變量 agreements。

作為由結(jié)構(gòu)體 BorrowAgreement 構(gòu)成的動態(tài)數(shù)組,agreements 變量占據(jù)了 storage 的開始位置(slot 0×00),并按照動態(tài)數(shù)組的規(guī)則存放在 storage 上。

如果熟悉動態(tài)數(shù)組在 storage 上的排列方式 [1],則知道 slot 0×00 位置保存的是當(dāng)前動態(tài)數(shù)組的大小,即 agreements 中的元素個數(shù),而其他位置則依次保存的是數(shù)組中的實際值。

回到上面的問題代碼,在這里,slot 0×00 被未初始化的 agreement storage 指針?biāo)赶?,因此,問題代碼中第 1051 行至 1054 行的賦值操作則會分別更新 storage slot 0×00 ~ 0×03 上的值。也就是說,slot 0×00 處原本存儲數(shù)組大小的值被設(shè)為 msg.sender。這完全不合情理,使得代碼邏輯十分混亂,代碼的功能完全無法正常完成,在一些情況下會造成很嚴(yán)重的后果。

那么,這里正確的代碼究竟該如何寫?

其實很簡單,只需給第 1050 行代碼,加上 memory 限定,即可標(biāo)明 agreement 是局部變量,而不會影響到 storage 上的值。

image.png

事實上,Solidity 編譯器對于這種“常見”錯誤寫法有警告,提示開發(fā)者使用關(guān)鍵字 storage 顯式標(biāo)明變量,以及未初始化的 storage 指針(Uninitialized storage pointer)警告。

warning

但是報 warning 并不會影響正常編譯,而開發(fā)者往往很容易忽略編譯器的各種警告提示(而且僅憑少量且模糊的警告信息,開發(fā)者并不知道如何正確修改代碼),繼續(xù)部署問題代碼進(jìn)行使用,從而留下極大的安全風(fēng)險。

Solidity 的 storage 空指針(引用)是一個設(shè)計缺陷

在傳統(tǒng)編程語言中(如C, C ),對空指針(Null Pointer)的訪問,通常會引起程序的報錯或崩潰??罩羔樀闹档扔诹?,但是語言和底層系統(tǒng)也同時保證內(nèi)存中地址為 0 的位置是不能存放有意義的值。而在例如 Java 或者 C# 中有 引用 的概念,但是它們都定義了一個空引用的值,"null"??找檬且粋€引用的安全保護(hù)值,保證這個引用不會指向任何數(shù)據(jù)。

但與傳統(tǒng)編程語言不同,以太坊智能合約語言 Solidity 中存在 memory 與 storage 的兩個數(shù)據(jù)存儲的概念,其中 storage 是一個外部的持久化存儲空間,位于區(qū)塊鏈上。然而,Solidity 語言卻允許定義一個指向外部存儲 storage 的指針(引用),這個引用在未初始化的情況下等于 0,而在 storage 地址為 0 的位置存放著有意義的數(shù)據(jù)。大家這時候可能已經(jīng)感覺到哪里不對了,在 Solidity 語言中,竟然允許存在一個沒有定義 空引用 狀態(tài)的數(shù)據(jù)引用,即一個未初始化的指針會默認(rèn)指向有意義的數(shù)據(jù),如果此時直接對「未初始化的 storage 引用」進(jìn)行賦值,那么就會錯誤覆蓋合約存儲在 storage 上面的狀態(tài)變量。如果 Solidity在設(shè)計初期考慮了 空引用 的值,或者像 C 那樣禁止定義 空引用,那么這類問題就能徹底避免。

注:在 Solidity 術(shù)語中,引用與指針兩個概念并不做區(qū)分。

response

并解釋之所以不提升為 error 是為了兼容部分特殊場景下代碼可編譯通過。

由于 Solidity 編譯器開發(fā)團(tuán)隊認(rèn)為修復(fù)該問題可能會帶來兼容性問題,于是在今年 3 月份將該問題的修復(fù)放到了下一個大版本(0.5.0)。開發(fā)者需使用 pragma experimental "v0.5.0" 標(biāo)記來觸發(fā)。

普通開發(fā)者很少會利用這個實驗特性,再加上普遍忽視警告信息,因此以太坊主網(wǎng)上一直部署著不少帶有此問題的代碼。

好消息是,安比(SECBIT)實驗室發(fā)現(xiàn) Solidity 編譯器開發(fā)團(tuán)隊于 20 多天前往 develop 分支合并了該問題的修復(fù)代碼,不區(qū)分是否是 0.5.0 以上的版本 [4]。也就是說上文中的所有問題代碼,不出意外在下一個版本(Solidity 0.4.25)都無法正常通過編譯。

pullrequest

安比(SECBIT)實驗室同步了最新編譯器代碼進(jìn)行驗證。

image.png


對于以上問題代碼,新版編譯器報錯如下:

image.png


明確提示 Error: Uninitialized storage pointer,無法通過編譯。

image.png


而對于沒有顯示聲明變量存儲位置(storage 或 memory)的代碼,報錯如下:

image.png


同樣也無法通過編譯。

Solidity 0.4.25 應(yīng)該很快進(jìn)入正式發(fā)布階段。

很明顯,0.4.24 以來,Solidity 語法上新增了很多更嚴(yán)格的要求,強(qiáng)制要求開發(fā)者寫出更嚴(yán)謹(jǐn)?shù)暮霞s代碼。

案例帶來的提示

回顧本文中 Solidity「未初始化的 storage 指針」問題。Solidity 中函數(shù)內(nèi)部聲明的引用類型變量默認(rèn)存儲位置為 storage,而未初始化的 storage 指針會指向 storage 的起始地址,從而合約開頭定義的若干個狀態(tài)變量會被覆蓋修改。

以此案例為教訓(xùn),安比(SECBIT)實驗室有下列提示:

智能合約開發(fā)者需要搞清楚 storage 和 memory 等關(guān)鍵詞的意義和用法,盡量顯示標(biāo)明

智能合約開發(fā)者必須重視合約編譯過程中的每一個 warning 信息

編譯器作為基礎(chǔ)工具,設(shè)計得當(dāng)則可在一定程度上杜絕特定安全問題

編譯器開發(fā)和程序語言設(shè)計一定要嚴(yán)謹(jǐn),從底層設(shè)計層面規(guī)避因使用者應(yīng)理解偏差或使用不當(dāng)帶來的風(fēng)險

我們欣慰地看到,Solidity 語言正變得越來越嚴(yán)謹(jǐn)。有理由相信以太坊 Solidity 開發(fā)生態(tài)將迎來更大的發(fā)展。

合約 Solidity 代碼 問題 storage
分享到:

1.TMT觀察網(wǎng)遵循行業(yè)規(guī)范,任何轉(zhuǎn)載的稿件都會明確標(biāo)注作者和來源;
2.TMT觀察網(wǎng)的原創(chuàng)文章,請轉(zhuǎn)載時務(wù)必注明文章作者和"來源:TMT觀察網(wǎng)",不尊重原創(chuàng)的行為TMT觀察網(wǎng)或?qū)⒆肪控?zé)任;
3.作者投稿可能會經(jīng)TMT觀察網(wǎng)編輯修改或補(bǔ)充。


專題報道

主站蜘蛛池模板: 水蜜桃一二二区视在线 | 麻豆网站视频国产在线观看 | 国产亚洲精品一区久久 | 日韩精品首页 | 美女用手扒自己下部 | 亚洲国产日韩成人综合天堂 | 久久AV国产麻豆HD真实乱 | 亚洲视频日韩 | 欧美国产合集在线视频 | 北条麻妃一区 | 九九九好热在线 | 免费大秀视频在线播放 | 网友偷自拍原创区 | 五月天在线视频观看 | 欧美国产日韩在线 | 91手机看片国产永久免费 | 小sao货水好多真紧h的视频 | 欧美一级h | 亚洲精品第五页中文字幕 | 欧美成人免费观看久久 | 国产成人精品免费视频大全五级 | 国产愉拍精品视频手机 | 日韩理论片 | a级亚洲片精品久久久久久久 | 亚洲性视频在线观看 | 日韩视频免费观看 | 羞羞色男人的天堂伊人久久 | 美女天天操 | 手机能看的黄色网站 | 99r在线播放 | 日本韩国在线 | 波多野结中文字幕在线69视频 | 久久精品视在线观看85 | 亚洲欧洲日产国码无码av | 国产香蕉在线视频 | 日本老熟老太hd | japanese日本护士 | 91久久国产露脸精品 | 国产成人综合一区人人 | 美女又爽又黄免费 | 丁香久久婷婷 |