1.概述
在有趣的智能合約蜜罐中我們對古老的欺騙手段和神奇的邏輯漏洞進行了講解和復現,在下部分中我們將會對新穎的賭博游戲和黑客的漏洞利用進行講解以及復現,從而進一步增加對智能合約蜜罐的了解。
同樣的,所有的智能合約蜜罐代碼都可以GitHub上找到,這里再次給出他們的網址:
smart-contract-honey
Solidlity-Vulnerable
2.新穎的賭博游戲
賭博行業從古至今一直存在,而區塊鏈的去中心化似乎給賭博行業帶了新的機會,它的進入會讓人們覺得賭博變得公平,然而我們都知道賭博結果往往都是必輸,那么接下來就通過分析四個基于區塊鏈的賭博游戲合約來介紹莊家是如何最后穩贏的。
2.1加密輪盤賭輪:CryptoRoulette
2.1.1蜜罐分析
第一個要介紹的是CryptoRoulette,它譯為「加密輪盤賭輪」。
GutHub地址:smart-contract-honeypots/CryptoRoulette.sol
Etherscan地址:CryptoRoulette|0x94602b0E2512DdAd62a935763BF1277c973B2758
蜜罐的完整代碼如下:
該合約設置了一個私有屬性的隨機數?secretNumber,在?shuffle()?函數中被指定范圍在1-20,玩家可以通過?play()?函數去盲猜這個隨機數,如果猜對了就可以將合約中的所有錢取走,每次調用?play()?函數后都會重置隨機數。
這么看來這個合約好像沒有什么問題,隨著猜錯的玩家越來越多,合約中的代幣余額也會積累的越多,如果碰巧猜對了就可以獲取所有的獎金,然而事實是這樣的嘛?
我們可以看到在這個蜜罐合約中,最重要的就是?shuffle()?和?play()?這兩個函數,下面就來分析下這兩個函數。
初始的?secretNumber?是在構造函數?CryptoRoulette?中調用?shuffle()?函數,而?shuffle()?函數中只有一行代碼,就是設置?secretNumber?的值,從代碼中也可以看出?secretNumber?的值既和區塊的數目有關,也和時間有關。函數代碼如下:
而?play()?函數就是提供給用戶進行賭博來猜這個隨機數的,玩家攜帶不小于0.1eth并傳入自己猜的數字?number,玩家猜的這個數字?number?去和?secretNumber?進行比較,如果相等就可以獲勝,轉走合約中的所有以太幣,但是在函數的開頭中有一個檢查require,其中后面要求玩家猜的數字不能大于10,而?secretNumber?我們在上面的函數中講到范圍是1-20,這樣看來雖然加大了難度,但是也存在猜對可能性,然而事實是?secretNumber?一定會大于10,玩家永遠都不可能猜對數字,合約所有者卻可以通過調用?kill()?函數轉走合約中的所有以太幣。
聲音 | Joseph Young:隨著貨幣戰爭加劇 比特幣和黃金迎來了非常有趣的時刻:加密貨幣分析師Joseph Young發布推文評論“英國央行行長呼吁建立全球貨幣體系以取代美元”的消息稱,隨著地緣風險上升,貨幣戰爭加劇,比特幣和黃金迎來了非常有趣的時刻。BTC價格走勢是獨立的,短期趨勢似乎是小幅下降,但如果外匯戰繼續下去,可能會有更多投資者轉而關注其它SoV(價值存儲)選擇。[2019/8/24]
這里會有人問了,secretNumber?為啥一定會大于10呢?原因就是結構體game的初始化對存儲數據?secretNumber?的覆蓋,我們在函數里直接初始化結構體必須加memory關鍵字,因為memory是使用內存來進行存儲,這樣一來就可以避免占用storage的存儲位,而蜜罐合約中并未使用memory關鍵字,從而導致了變量覆蓋。
該問題在Solidity0.5.0版本以前只是進行了提示,并沒有做出錯誤警告,所以在老版本編譯器中要注意該問題。在下面的代碼復現中可以看到問題所在。
2.1.2代碼復現
將蜜罐合約的代碼復制到RemixIDE中,為了方便我們查看?secretNumber?的值,我們將?secretNumber?的類型設置為public,這樣就可以在RemixIDE中直接看到它的值了。
甚至有些蜜罐部署者為了誘惑攻擊者來攻擊合約,也可以設置為public屬性,因為就算告訴攻擊者?secretNumber?的值他也不能猜對這個數字。
使用地址0x5B3點擊「Deploy」部署合約,調用?secretNumber?查看初始隨機數為1,由于這里還沒有初始化結構體也就不會覆蓋隨機數所以是正確的。
之后攻擊者發現了該蜜罐合約,查看?secretNumber?為1并認為該合約可以進行攻擊獲利,所以在符合?play()?函數中的第一個判斷條件情況下傳入數字1和攜帶1個以太幣進行函數調用,函數調用成功后查看賬戶余額發現賬戶余額不僅沒有得到合約中的所有代幣反而將剛才函數調用時攜帶的1個以太幣也損失掉了。
為了探究具體原因我們對剛才的函數調用進行Debug。
調試點擊下一步直到第一個條件判斷,此時?secretNumber?仍然為1。
繼續點擊按鈕進行下一步的調試,當進行到?game.player=msg.sender?時由于結構體game的初始化對存儲數據?secretNumber?進行了覆蓋,導致?secretNumber?變成了msg.sender的uint256內容,這樣一來就使得后面的if判斷條件不能成立,從而使得攻擊者不能轉走合約中的所有代幣余額。
2.2開放地址彩票:OpenAddressLottery
2.2.1蜜罐分析
第二個要介紹的是OpenAddressLottery,它譯為「開發地址彩票」。
GutHub地址:Solidlity-Vulnerable/OpenAddressLottery.sol
聲音 | Jameson Lopp:比特幣是一個非常有趣的實驗:據cointelegraph報道,比特幣開發者Jameson Lopp稱,我相信比特幣是一個非常有趣的實驗,如果從長遠來看,如果成功,不僅可以徹底改變金錢,還可以徹底改變我們對治理的看法,可以實現向無政府資本主義社會的過渡。[2019/1/1]
Etherscan地址:OpenAddressLottery|0xd1915A2bCC4B77794d64c4e483E43444193373Fa
蜜罐的完整代碼如下:
蜜罐合約OpenAddressLottery的游戲邏輯很簡單,合約中有一個初始值為1的狀態變量?LuckyNumber,競猜者每次競猜時都會根據其地址隨即生成0或者1,如果生成的值和?LuckyNumber?一樣,那么競猜者就可以獲得1.9倍的獎金,且每個地址只能贏得一次游戲勝利,之后將無法繼續參加競猜。該蜜罐合約的重點就在于?participate()、luckyNumberOfAddress()?和?forceReseed()?函數,下面來對這3個函數進行依次講解。
首先是?participate()?函數,這是用戶參與競猜的函數:
接著是?luckyNumberOfAddress()?函數,將競猜者的地址作為參數傳入,通過?n=uint(keccak256(uint(addr),secretSeed))%2;?來計算競彩時競猜者對應的數字,由于是對2取余,所以得到的結果只能為0或者1。在計算這個數字時使用了變量?secretSeed,而該變量總是通過?reseed()?函數得到的。
最后我們來講下上面說到的?reseed()?函數,通過keccak256算法將傳入的4個參數來生成?secretSeed。
通過上面對合約的分析,看起來合約沒有什么問題,中獎率也是50%,但其實是有陷阱的,這就要說到Solidity0.4.x結構體局部變量引起的變量覆蓋漏洞,也就是給未初始化的結構體局部變量賦值時會直接覆蓋掉智能合約中定義的前幾個變量,這樣就使得合約中?forceReseed()?函數被調用后,第四個定義的參數?LuckyNumber?會被?s.component4=tx.gasprice*7?給覆蓋并將其設置為7,該蜜罐合約原理和上一個蜜罐合約類似。
查看該合約的交易內容,可以發現OpenAddressLottery的交易數量很多,這也說明了蜜罐合約OpenAddressLottery的欺騙性。
2.2.2代碼復現
將蜜罐合約的代碼復制到RemixIDE中,為了方便我們查看?LuckyNumber?的值,我們將?LuckyNumber?的類型設置為public,這樣就可以在RemixIDE中就有獲取其值的?getter()?函數了。同樣的,蜜罐部署者也可以將該變量設置為public屬性讓攻擊者誤以為有利可圖,因為?LuckyNumber?的值會被覆蓋永遠為7。
使用地址0x5B3點擊「Deploy」部署合約,調用?LuckyNumber?查看其值為1,由于這里還沒有初始化?SeedComponent?結構體也就不會覆蓋掉?LuckyNumber?的值,所以它還是1。
聲音 | Joseph Young: 加密貨幣對千禧一代來說是最有趣的金融標的:福布斯財經評論員 Joseph Young在社交媒體中表示,對于千禧一代來說,加密貨幣是最有趣的金融標的。在加密貨幣領域,千禧一代愿意積累有關期貨市場、ETF、市場增長、基礎設施和創業生態系統的知識。這比任何大學課程都要好。[2018/11/14]
使用合約所有者0x5B3調用?forceReseed()?函數來初始化?SeedComponent?中的四個變量,可以看到?LuckyNumber?的值由于初始化已經變成了7。
攻擊者0x4B2看到該合約后認為其存在漏洞,攜帶10eth調用?participate()?函數,調用后查看余額發現并沒有增加。查看自己的地址對應的?luckyNumberOfAddress?的值為1,但是卻沒有得到獎勵,再查看?LuckyNumber?的值發現一直為7。
其原因就是在部署者調用?forceReseed()?函數初始化后?LuckyNumber?的值就被覆蓋為了7,而攻擊者地址生成的隨機數只能是0或1,這就意味著永遠不會有人獲得勝利。這就是利用了編譯器的漏洞,該問題已經在Solidity0.5.0中修復,所以這種蜜罐合約只有在Solidity0.4.x中才會生效。
2.3山丘之王:KingOfTheHill
2.3.1蜜罐分析
第三個要介紹的是KingOfTheHill,它譯為「山丘之王」。
GutHub地址:Solidlity-Vulnerable/KingOfTheHill.sol
Etherscan地址:KingOfTheHill|0x4dc76cfc65b14b3fd83c8bc8b895482f3cbc150a
蜜罐的完整代碼如下:
蜜罐合約KingOfTheHill只有38行代碼,邏輯很簡單,有回退函數和?takeAll()?函數,其中?jackpot?變量是傳入合約的所有代幣之和,每次有用戶調用回退函數后如果傳入的?mag.value比?jackpot?大,就將?owner?的值賦值為?msg.sender。
當用戶獲得了合約所有者權限后,就可以調用?takeAll()?函數在延期時間到后將合約中所有余額轉走。接下來重點分析下這兩個函數。
首先是回退函數,這是用戶參與合約「漏洞」的函數,其代碼如下:
接著是?takeAll()?函數,這是能轉走合約中所有余額的函數,其代碼如下:
通過對上面兩個函數的分析,感覺該合約并沒有什么問題,但是我們說了這是個蜜罐,那么它的陷阱到底在哪兒呢?回看下「有趣的智能合約蜜罐」中的TestBank蜜罐合約就能知道原因了,它們的原理類似,都是「誰是合約主人」的問題。
KingOfTheHill中存在著Owned和KingOfTheHill兩個合約,KingOfTheHill繼承了Owned,為了方便理解,我們將KingOfTheHill改寫成一個單合約,代碼如下:
聲音 | DRW控股創始人:對我來說,更有趣的是比特幣的應用方法:DRW控股公司的創始人兼交易商大亨Don Wilson稱,有很多關于比特幣存儲價值的爭論。但對我來說更有趣的是比特幣的應用方法,在一個系統中沒有信任的傳輸能力是具有巨大破壞性的。[2018/9/19]
在改寫了合約代碼后很容易就可以看出問題所在,用于權限判斷的修飾器函數onlyOwner中判斷的變量是?owner1,而回退函數中修改的是原來子類新定義的owner,也就是?owner2,這就說明了合約所有者是不會被更改的,調用?takeAll()?函數的人只能是合約創建者。接下來我們通過代碼來復現一下。
2.3.2代碼復現
將蜜罐合約的代碼復制到RemixIDE中,為了方便我們復現,將回退函數中?withdrawDelay=block.timestamp5days;?修改為?withdrawDelay=block.timestamp0days;,這樣我們在測試的時候就不用等待5天后再去嘗試取款操作了。
使用地址0x5B3點擊「Deploy」部署KingOfTheHill合約,點擊?owner?查看當前值為0。
再使用0x5B3攜帶10eth調用回退函數,向合約中存入10個以太幣,此時?jackpot?為10eth,查看owned為0x5B3。
攻擊者0xAb8設置msg.value為20eth調用回退函數,查看?owner?為0xAb8。
攻擊者發現此時?owner?為自己的地址,符合了?takeAll()?函數的要求,所以去調用?takeAll()?函數,結果發現交易失敗,并且自己的余額仍然為80eth。
蜜罐部署者0x5B3發現有人上鉤了,合約中已經有了30eth,此時雖然?owner?為攻擊者地址0xAb8,但是0x5B3調用?takeAll()?函數仍然將合約中的所有余額全部轉走,查看賬戶余額,的確增加了30eth。
與之類似的智能合約還有RichestTakeAll:
GitHub地址:Solidlity-Vulnerable/RichestTakeAll.sol
智能合約地址:RichestTakeAll|0xe65c53087e1a40b7c53b9a0ea3c2562ae2dfeb24
2.4以太幣競爭游戲:RACEFORETH
2.4.1蜜罐分析
第四個要介紹的是RACEFORETH,它譯為「以太坊競爭游戲」。
GutHub地址:Solidlity-Vulnerable/RACEFORETH.sol
蜜罐的完整代碼如下:
蜜罐合約RACEFORETH中有一個?SCORE_TO_WIN?參數,其值為100finney,字面意思我們也可以知道該參數的作用是勝利的分數,然后合約還有兩個映射,其中?racerScore?是競爭者當前得分數,racerSpeedLimit?是每步的限制。競爭者通過每次的轉賬金額來積累自己的分數?racerScore,當自己的得分?racerScore?大于等于?SCORE_TO_WIN?時就能獲得勝利,取走合約創建者一開始存入的獎勵?PRIZE。蜜罐合約的核心內容就是?race()?函數和?endRace()?函數,接下來我們分析下這兩個函數。
投資管理公司的首席宏觀投資策略師表示 數字貨幣是一件新鮮有趣的事物:全球最大投資管理公司BlackRock的首席宏觀投資策略師Isabelle Mateos Y Lago 表示,數字貨幣是一件非常新鮮、非常有趣的事物,該公司正在密切關注。Lago還表示,投資于數字貨幣并不難,但重要的是資產的安全性,并認為比特幣并非“可投資資產”。(彭博)[2018/1/30]
首先是?race()?函數,其代碼如下:
用戶每次調用?race()?函數都會帶入?msg.value,且?msg.value?需要大于1wei和小于步長限制,通過判斷后加到自己的總得分數?racerScore?上,接著將新的步長限制設置為當前步長限制的一半,只要總得分數大于等于了獲勝目標值就可以取走獎勵,初看合約會覺得每次增加的步數在減少,但總有一天會追上,但事實是這樣嗎?
接著是?endRace()?函數,其代碼如下:
合約所有者在上一次競賽的3天后就可以轉走合約中所有的余額了。
2.4.2代碼復現
將蜜罐合約的代碼復制到RemixIDE中,為了方便我們復現,增加了一個?publicnowScore,這樣我們在測試的時候就可以看到每次競賽后的分數了。
使用地址0x5B3點擊「Deploy」部署RACEFORETH合約。
使用0xAb8作為攻擊者,根據代碼的要求,第一次最大只能為50Finney,所以將msg.value也設置為50Finney,之后查看當前分數為50Finney。
攻擊者0xAb8第二次嘗試將msg.value設置為大于上一次競賽的50Finney一半的26Finney,調用?race()?函數后發現調用失敗,原因則是因為我們的26Finney不滿足require中小于等于上一次競賽一半的條件。
每次我們都傳入上一次最大值的一半,執行多次后發現仍然未到100Finney。因為如下的公式只能無限趨于100卻用于不能等于100。
其中:
永遠是小于2的,那么50乘上這個式子就永遠不可能等于100了,也就永遠無法到達終點,所以對于該蜜罐合約,即使我們多次調用?race()?函數,每次都轉入最大限制值,也不可能達到目標分數,那么我們就不能取出合約中的獎勵了。
3.黑客的漏洞利用
3.1僅僅是測試?(整數溢出):For_Test
3.1.1蜜罐分析
第五個要介紹的是For_Test,它譯為「僅僅是測試?」。
GutHub地址:Solidlity-Vulnerable/For_Test.sol
Etherscan地址:For_Test|0x2eCF8D1F46DD3C2098de9352683444A0B69Eb229
蜜罐的完整代碼如下:
蜜罐合約For_Test的邏輯很簡單,核心函數只有?Test()?一個,在該函數中當傳入的?msg.value?大于0.1eth時,根據for循環的內容,最終會得到?amountToTransfer?的值,也就是說函數調用者會獲得4倍轉入金額的獎勵。接下來我們分析函數的主要內容。
仔細分析代碼邏輯可以發現for循環中if判斷中有個條件,當條件為真時會跳出循環,但是這個判斷條件很詭異,因為?amountToTransfer?初始為0,在跳出之前?amountToTransfer=multi,而在下一次循環時?multi?變為2倍的?i,這就意味著?multi是永遠大于?amountToTransfer?的值,相應的這個判斷條件不是會永遠也不成立了嗎?在最終揭秘這個蜜罐合約前我們還需要了解下幾個知識。
msg.value?的單位是wei,而1eth=1018wei。
當一個參數變量被定義為?var?時,其數據類型為?uint8,其取值范圍為。
再次看到?Test()?函數中的循環,msg.value?的最小值為0.1eth,而?msg.value*2?的值就會超過?uint8?的取值范圍,也就是說此處會存在整形溢出,在?i=255?時再執行?i?就會導致?i?上溢變為0,此時的?multi?為0從而小于?amountToTransfer?的值,這樣就滿足了if的判斷條件,循環也會提前結束。根據代碼內容,最終轉給調用者的金額為?amountToTransfer=255*2=510wei?,無論調用者傳入了大于0.1eth的任何金額,最后都只會得到510wei。
3.1.2代碼復現
將蜜罐合約的代碼復制到RemixIDE中,使用地址0x5B3點擊「Deploy」部署For_Test合約,此時0x5B3的賬戶余額為100eth。
選擇0xAb8作為攻擊者,將?msg.value?設置為10eth,調用?Test()?函數,調用成功后發現賬戶余額不但沒有增加反而減少了剛才傳入的10eth。
當攻擊者將代幣轉入合約后,合約所有者調用?withdraw()?函數進行取款,將剛才攻擊者調用?Test()?函數傳入的10eth轉走,賬戶余額增加到110eth。
與之類似的智能合約還有Test1:
Github地址:smart-contract-honeypots/Test1.sol
3.2股息分配:DividendDistributor
3.2.1蜜罐分析
最后一個要介紹的是DividendDistributor,它譯為「股息分配」。
GutHub地址:Solidlity-Vulnerable/DividendDistributor.sol
Etherscan地址:DividendDistributorv3|0x858c9eaf3ace37d2bedb4a1eb6b8805ffe801bba
蜜罐的完整代碼如下:
蜜罐合約DividendDistributor的邏輯不算太難,主要有投資、取錢、計算股息等功能,合約中有一個結構體類型的investor,其作用為存儲投資人的投資信息包括投資額度和股息,并且該結構體通過mapping實現賬戶地址到investor的映射。
通篇看來下合約并沒有任何的問題,并且如果編譯器版本設置正確的話合約也不會出現任何問題。看一下合約關鍵的函數,invest()、divest()、loggedTransfer()?和?payDividend(),接下來我們就對這4個函數進行詳細分析。
先是?invest()?函數,其函數功能為用戶調用該函數進行投資,每次的投資數量不能小于要求的最低數量0.4eth,投資后更新相關的變量。
完整代碼如下:
divest()?函數作為和上面的函數剛好相反,是取出自己投資的金額,函數中一開始就要檢查調用者投資的數量或者調用函數傳入的參數不為0,接著減去該次取錢操作的金額數量,最后從合約所有者賬戶中轉走amount金額給調用者。完整代碼如下:
loggedTransfer()函數的功能非常簡單,就是轉賬和記錄轉賬操作。完整代碼如下:
payDividend()?函數為獲得由合約所有者設置的股息。完整代碼如下:
通過分析上面的4個函數,我們發現該蜜罐合約的誘惑點在于投資者不僅能夠隨時存取投資,還可以通過?payDividend()?函數獲取股息,這樣的合約好像是有利可圖的,然而事實是這是一個陷阱,它利用的就是舊版本編譯器中的漏洞,在Solidity0.4.12之前存在一個漏洞,如果將空字符串作為函數調用時的參數那么編譯器就會跳過該參數。
而在上面的幾個核心函數中,divest()?函數就是存在這樣的問題,根據漏洞說明,調用?this.loggedTransfer(amount,"",msg.sender,owner);?后會變成?loggedTransfer(uintamount,bytes32msg.sender,addressowner,address空)?最終給?owner?用戶轉賬?owner.call.value(amount)()。下面我們就通過代碼來復現這個蜜罐合約,揭開它的真面目。
3.2.2代碼復現
將蜜罐合約的代碼復制到RemixIDE中,將編譯器Solidity的版本設置為0.4.11。
選擇0x5B3作為合約部署者和所有者,點擊「Deploy」進行部署,隨后將VALUE設置為10eth并調用?distributeDividends?函數設置股息。
將0xAb8作為攻擊者,設置VALUE為10eth并調用?invest()?函數進行投資。
使用0xAb8調用下圖中的函數獲取該蜜罐合約的相關信息,包括計算股息,自己的投資數額,最小投資數額,合約所有者?owner,總的股息和總的投資數額。
繼續使用0xAb8調用?divest()?函數并設置其傳入參數為?5000000000000000000?想要取出剛才投資的10eth的一半,發現該交易被確認,查看該交易的logs可以發現和上面我們分析的一樣,target?參數變成了?owner?的地址,第二個參數也被?msg.sender?所取代,返回查看賬戶當前余額,發現剛才調用?divest()?函數取出的5eth被轉到了?owner?賬戶0x5B3中。
4.總結
通過對以太坊蜜罐智能合約的分析,我們可以發現在智能合約中這些有趣的蜜罐合約更像是釣魚,通過各種欺騙手法誘使他人將代幣轉入合約中從而進一步獲取這些代幣。當然蜜罐合約也不是完全沒有學習價值的,我們從蜜罐合約中可以看到合約的攻擊思路以及Solidity的很多新舊特性。
在平時的合約審計中也需要考慮這些問題,否則這些合約就可能被黑客攻擊導致合約代幣被盜取。即使是現在,同樣有人編寫蜜罐合約進行誘騙,只是他們的思路不再僅限于那些想要靠天上掉餡餅獲取利益的人,各種機器人也成為了他們的誘騙目標。
所以我們一定要重視合約的功能邏輯,防止合約因為功能邏輯被攻擊的同時還要防止合約所有者跑路等各種因素。
5.文獻參考
蜜罐技術_百度百科(baidu.com)
以太坊蜜罐智能合約分析(seebug.org)
Solidity中文手冊
“波卡知識圖譜”是我們針對波卡從零到一的入門級文章,我們嘗試從波卡最基礎的部分講起,為大家提供全方位了解波卡的內容,當然這是一項巨大的工程,也充滿了挑戰.
1900/1/1 0:00:00Kava網絡融合了以太坊和Cosmos生態。我們很高興地宣布,Kava將在2022年邁向下一個階段,在通過確立作為一個強大的、可擴展的、為頂級DeFi產品提供支持的Layer-1網絡和生態后,K.
1900/1/1 0:00:00尊敬的用戶:?????????BKEXGlobal即將上線LITWTF,詳情如下:上線交易對:LITWTF/USDT??幣種類型:ERC20充值功能開放時間:已開放交易功能開放時間:2022年2.
1900/1/1 0:00:00尊敬的用戶: BKEXGlobalETP專區將于2022年2月9日15:00上線BTTC3L、BTTC3S,具體詳情如下:? 交易標的 PTB繼9月上線BKEX后.
1900/1/1 0:00:00本文來自老雅痞,Odaily星球日報經授權轉載發布。在過去的一年里,NFTs已經突破了壯觀和投機的高度,也出現了許多質疑.
1900/1/1 0:00:00WEEX全球站用戶: 第一條 本網站的宗旨是在不違反新加坡相關法律法規的前提下,盡可能地為全球廣大數字資產愛好者及投資者提供專業的國際化水準的交易平臺和金融產品.
1900/1/1 0:00:00