TMT观察网_独特视角观察TMT行业

警惕!Solidity缺陷易使合約狀態失控區塊鏈

巴比特資訊 2018-08-07 10:45
分享到:
導讀

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

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

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

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

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

1111

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

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

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

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

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

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

聰明的你可以仔細思考一下,競猜數字 _number 應該填多少?

honeypot-contract

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

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

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

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

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

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

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

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

image.png

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

storage-code

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

luckyNum 的正確答案應該是調用者自己的地址。

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

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

OpenAddressLottery

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

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

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

image.png

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

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

結合 BancorLender 代碼具體分析。

code

BancorLender 合約 offerToLend() 函數中聲明了一個結構體(struct)變量 BorrowAgreement agreement。

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

作為由結構體 BorrowAgreement 構成的動態數組,agreements 變量占據了 storage 的開始位置(slot 0×00),并按照動態數組的規則存放在 storage 上。

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

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

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

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

image.png

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

warning

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

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

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

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

注:在 Solidity 術語中,引用與指針兩個概念并不做區分。

response

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

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

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

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

pullrequest

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

image.png


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

image.png


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

image.png


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

image.png


同樣也無法通過編譯。

Solidity 0.4.25 應該很快進入正式發布階段。

很明顯,0.4.24 以來,Solidity 語法上新增了很多更嚴格的要求,強制要求開發者寫出更嚴謹的合約代碼。

案例帶來的提示

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

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

智能合約開發者需要搞清楚 storage 和 memory 等關鍵詞的意義和用法,盡量顯示標明

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

編譯器作為基礎工具,設計得當則可在一定程度上杜絕特定安全問題

編譯器開發和程序語言設計一定要嚴謹,從底層設計層面規避因使用者應理解偏差或使用不當帶來的風險

我們欣慰地看到,Solidity 語言正變得越來越嚴謹。有理由相信以太坊 Solidity 開發生態將迎來更大的發展。

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

1.TMT觀察網遵循行業規范,任何轉載的稿件都會明確標注作者和來源;
2.TMT觀察網的原創文章,請轉載時務必注明文章作者和"來源:TMT觀察網",不尊重原創的行為TMT觀察網或將追究責任;
3.作者投稿可能會經TMT觀察網編輯修改或補充。


專題報道

主站蜘蛛池模板: 恒湿机_除湿加湿一体机_恒湿净化消毒一体机厂家-杭州英腾电器有限公司 | 无硅导热垫片-碳纤维导热垫片-导热相变材料厂家-东莞市盛元新材料科技有限公司 | 切铝机-数控切割机-型材切割机-铝型材切割机-【昆山邓氏精密机械有限公司】 | 电表箱-浙江迈峰电力设备有限公司-电表箱专业制造商 | 泰兴市热钻机械有限公司-热熔钻孔机-数控热熔钻-热熔钻孔攻牙一体机 | 深圳办公室装修-写字楼装修设计-深圳标榜装饰公司 | 根系分析仪,大米外观品质检测仪,考种仪,藻类鉴定计数仪,叶面积仪,菌落计数仪,抑菌圈测量仪,抗生素效价测定仪,植物表型仪,冠层分析仪-杭州万深检测仪器网 | 成都LED显示屏丨室内户外全彩led屏厂家方案报价_四川诺显科技 | 低噪声电流前置放大器-SR570电流前置放大器-深圳市嘉士达精密仪器有限公司 | 消泡剂_水处理消泡剂_切削液消泡剂_涂料消泡剂_有机硅消泡剂_广州中万新材料生产厂家 | 上海三信|ph计|酸度计|电导率仪-艾科仪器 | 招商帮-一站式网络营销服务|搜索营销推广|信息流推广|短视视频营销推广|互联网整合营销|网络推广代运营|招商帮企业招商好帮手 | 新能源汽车电池软连接,铜铝复合膜柔性连接,电力母排-容发智能科技(无锡)有限公司 | 紧急切断阀_气动切断阀_不锈钢阀门_截止阀_球阀_蝶阀_闸阀-上海上兆阀门制造有限公司 | 安徽控制器-合肥船用空调控制器-合肥家电控制器-合肥迅驰电子厂 安徽净化板_合肥岩棉板厂家_玻镁板厂家_安徽科艺美洁净科技有限公司 | 智能汉显全自动量热仪_微机全自动胶质层指数测定仪-鹤壁市科达仪器仪表有限公司 | 智能风向风速仪,风速告警仪,数字温湿仪,综合气象仪(气象五要素)-上海风云气象仪器有限公司 | uv机-uv灯-uvled光固化机-生产厂家-蓝盾机电 | 防水套管-柔性防水套管-刚性防水套管-上海执品管件有限公司 | 阴离子_阳离子聚丙烯酰胺厂家_聚合氯化铝价格_水处理絮凝剂_巩义市江源净水材料有限公司 | 深圳美安可自动化设备有限公司,喷码机,定制喷码机,二维码喷码机,深圳喷码机,纸箱喷码机,东莞喷码机 UV喷码机,日期喷码机,鸡蛋喷码机,管芯喷码机,管内壁喷码机,喷码机厂家 | 单柱拉力机-橡胶冲片机-哑铃裁刀-江都轩宇试验机械厂 | 结晶点测定仪-润滑脂滴点测定仪-大连煜烁 | 天津暖气片厂家_钢制散热器_天津铜铝复合暖气片_维尼罗散热器 | 阳光模拟试验箱_高低温试验箱_高低温冲击试验箱_快速温变试验箱|东莞市赛思检测设备有限公司 | 卷筒电缆-拖链电缆-特种柔性扁平电缆定制厂家「上海缆胜」 | 深圳南财多媒体有限公司介绍| 智能家居全屋智能系统多少钱一套-小米全套价格、装修方案 | 轴承振动测量仪电箱-轴承测振动仪器-测试仪厂家-杭州居易电气 | 亿诺千企网-企业核心产品贸易| 丹佛斯压力传感器,WISE温度传感器,WISE压力开关,丹佛斯温度开关-上海力笙工业设备有限公司 | 铣刨料沥青破碎机-沥青再生料设备-RAP热再生混合料破碎筛分设备 -江苏锡宝重工 | 青岛代理记账_青岛李沧代理记账公司_青岛崂山代理记账一个月多少钱_青岛德辉财税事务所官网 | 北京宣传片拍摄_产品宣传片拍摄_宣传片制作公司-现像传媒 | 全温度恒温培养摇床-大容量-立式-远红外二氧化碳培养箱|南荣百科 | 河南正规膏药生产厂家-膏药贴牌-膏药代加工-修康药业集团官网 | 冲锋衣滑雪服厂家-冲锋衣定制工厂-滑雪服加工厂-广东睿牛户外(S-GERT) | 江苏全风,高压风机,全风环保风机,全风环形高压风机,防爆高压风机厂家-江苏全风环保科技有限公司(官网) | 伟秀电气有限公司-10kv高低压开关柜-高低压配电柜-中置柜-充气柜-欧式箱变-高压真空断路器厂家 | 户外环保不锈钢垃圾桶_标识标牌制作_园林公园椅厂家_花箱定制-北京汇众环艺 | 广州小程序开发_APP开发公司_分销商城系统定制_小跑科技 |