零時科技丨CTF技能寶典之智能合約 整數溢出漏洞

2021-01-11 零時科技new

前言

近年來,各個大型CTF(Capture The Flag,中文一般譯作奪旗賽,在網絡安全領域中指的是網絡安全技術人員之間進行技術競技的一種比賽形式)比賽中都有了區塊鏈攻防的身影,而且出現的題目絕大多數都是區塊鏈智能合約攻防。此系列文章我們主要以智能合約攻防為中心,來剖析智能合約攻防的要點,前兩篇我們分享了合約反編譯,反彙編的基礎內容。後續的文章中,我們會繼續分享CTF比賽中智能合約常見題型(重入,整數溢出,空投,隨機數可控等)及解題思路,相信會給讀者帶來不一樣的收穫。

上篇文章中我們分享了CTF比賽中常考的重入漏洞題型,本篇繼續來分享CTF比賽中的整數溢出題型,也是比較常見的一類題型,當然多數CTF智能合約題目並不僅僅考察單個漏洞的攻防,可能涉及多個漏洞的組合。

本篇我們以2018年WCTF上BelluminarBank題目為例,給大家分享智能合約整數溢出的題型。解出這道題不僅需要整數溢出攻擊,也需用到變量覆蓋,權限設置等多個攻擊技巧。

題目地址:

由於WCTF智能合約比賽沒有在以太坊測試網(ropsten)進行,沒有在線的攻防場景,合約具體題目介紹及合約源碼已在GitHub給出:https://github.com/beched/ctf/tree/master/2018/wctf-belluminar

題目分析

題目提示

團隊需要對字節碼進行反向工程,並使用以下攻擊:

整數溢出繞過存款期限限制;存儲溢出以覆蓋銀行所有者;存儲訪問權限以洩露私有屬性;部署自殺合同以強制將eth發送到目標合同(以解決餘額差異)不一定需要意外的以太攻擊,如果使用withdraw()和invest()調用,則可以適當平衡。可能是由於導致錯誤解決方案的巨大錯誤所致:withdraw()函數不會更改balances數組。但是仍然需要事先利用整數溢出。

合約說明

Belluminar Bank非常小而特別。其工作方式如下:

任何人都可以投資任何金額,並應指定存款期限(在此之前存款將被鎖定);存款期限必須比先前客戶的存款期限至少長1年;每個存款分配一個帳號;帳戶0包含31337 wei,由銀行所有者(合同創建者)鎖定多年;存款期限滿一年(如果您不提款),銀行所有者可以沒收您的存款。目標是破解這家銀行並清空其餘額。如果成功,該機器人將向您發送交易數據中的標誌。

合約源碼

pragma solidity ^0.4.23;contract BelluminarBank { struct Investment { uint256 amount; uint256 deposit_term; address owner; } //全局變量 Investment[] balances; uint256 head; address private owner; bytes16 private secret; //secret可讀取 function BelluminarBank(bytes16 _secret, uint256 deposit_term) public { secret = _secret; owner = msg.sender; if(msg.value > 0) { balances.push(Investment(msg.value, deposit_term, msg.sender)); } } function bankBalance() public view returns (uint256) { return address(this).balance; } //局部變量覆蓋全局變量 function invest(uint256 account, uint256 deposit_term) public payable { if (account >= head && account < balances.length) { Investment storage investment = balances[account]; investment.amount += msg.value; } else { if(balances.length > 0) { //存在整數溢出 require(deposit_term >= balances[balances.length - 1].deposit_term + 1 years); } //局部變量 investment.amount = msg.value; investment.deposit_term = deposit_term; investment.owner = msg.sender; balances.push(investment); } } function withdraw(uint256 account) public { require(now >= balances[account].deposit_term); require(msg.sender == balances[account].owner); msg.sender.transfer(balances[account].amount); } function confiscate(uint256 account, bytes16 _secret) public { require(msg.sender == owner); require(secret == _secret); require(now >= balances[account].deposit_term + 1 years); uint256 total = 0; for (uint256 i = head; i <= account; i++) { total += balances[i].amount; delete balances[i]; } head = account + 1; msg.sender.transfer(total); }}

合約分析

從題目提示可以得出,本次攻擊的目的是拿到合約中的所有餘額。並且需要多個漏洞攻擊手法。

先來分析合約中的存在轉帳功能函數withdraw()和confiscate():

function withdraw(uint256 account) public { require(now >= balances[account].deposit_term); require(msg.sender == balances[account].owner); msg.sender.transfer(balances[account].amount);}

withdraw()函數中,會判斷現在的時間是否大於存款的期限,第二句判斷調用者地址是否是存款者地址,如果條件滿足,就會轉出當前合約調用者的存款餘額,可以得出該函數並不能轉出合約所有餘額。

繼續來看第二個函數confiscate():

function confiscate(uint256 account, bytes16 _secret) public { require(msg.sender == owner); require(secret == _secret); require(now >= balances[account].deposit_term + 1 years); uint256 total = 0; for (uint256 i = head; i <= account; i++) { total += balances[i].amount; delete balances[i]; } head = account + 1; msg.sender.transfer(total);}

confiscate()函數中會依次判斷所有者地址,secret,存款期限,如果條件滿足,之後會對存款數組進行遍歷,將得到的資金amount全都賦予total,最終通過transfer()將所有餘額轉出,也就是說合約所有者可以將之前存款記錄的餘額全部取出。很明顯,該confiscate()函數就是我們最終需要利用的轉帳函數。

如果要使用confiscate()函數進行轉帳,我們需要解決該函數中的前三行代碼判斷條件,由於題目給出了提示-存儲溢出以覆蓋銀行所有者。我們繼續分析該合約變量覆蓋問題。

從合約源碼可看到,該合約中有四個全局變量(balances,head,owner,secret),在solidity中,全局變量存儲在storage當中,對於複雜的數據類型,比如array(數組)和struct(結構體),在函數中作為局部變量時,也會默認儲存在storage當中。並且solidity的狀態變量存儲時,都是按照狀態在合約中的先後順序進行依次存儲。

也就是說目前合約的四個全局變量存儲如下:

storage[0] : balancesstorage[1] : headstorage[2] : ownerstorage[3] : secret

在invest()函數中,通過Investment storage investment = balances[account];可得到一個結構體變量investment,該變量有三個成員均存在賦值操作,如下:

investment.amount = msg.value; investment.deposit_term = deposit_term;investment.owner = msg.sender;

在函數中作為局部變量時,也會默認儲存在storage當中,由於結構體變量investment並未對三個成員進行初始化,所以當變量存儲時依然會按照順序存儲在storage[0,1,2]中,那麼目前storage中的存儲為數據為:

這裡局部變量覆蓋全局變量時要特別注意一點:局部變量amount覆蓋全局變量balances時,由於balances變量是數組的長度(目前數組中有合約部署者傳入的一組數據,故balances為1),當其他調用者也傳入的一組數據,比如傳入的msg.value值為1(也就是amount的值為1時),之後變量覆蓋後的balances值也為1,但是由於傳入了一組數據後,數組的長度balances變為2,由於變量覆蓋相互影響的關係,balances的值為2後,amount的值也變為2,也就是說雖然傳入的msg.value值為1,但最終amount的值為2。

我們繼續來分析合約漏洞,由於上圖invest()函數中賦值的三個變量(msg.value,deposit_term,msg.sender)都可控:

第一個變量msg.value,是調用者傳入的資金;

第二個變量deposit_term本身的含義是存款期限,調用者可根據自己情況輸入需要存款的時間,在合約中發生變量覆蓋後則代表head值(存款數據的索引)。並且在invest()函數中存在和deposit_term變量相關聯的判斷條件require(deposit_term >= balances[balances.length - 1].deposit_term + 1 years);我們將它和confiscate()函數中的判斷條件require(now >= balances[account].deposit_term + 1 years);進行對比。

由於我們最終需要利用confiscate()函數中的transfer函數進行轉帳,如果按照正常邏輯運算,我們存錢後至少需要一年時間才能取出,所以該判斷條件(require(now >= balances[account].deposit_term + 1 years);)必須設法繞過。同時我們還需要invest()函數中的判斷條件(require(deposit_term >= balances[balances.length - 1].deposit_term + 1 years);)也正常執行。

可以看到這兩行代碼的條件判斷中加減操作並沒有做安全防護,這裡假如我們使balances[balances.length - 1].deposit_term + 1 years值等於2256,由於solidity的存儲關係,這裡會發生整數上溢出,最終結果為0,就可以繞過該判斷條件。還需要注意的一點為:confiscate()函數中的for循環需要head(由於變量覆蓋的關係,head值為deposit_term傳入的值)從0開始才能將所有的資金取出,所以需要我們對deposit_term進行兩次賦值:第一次賦值為2256 - 1 years(solidity中默認時間單位為秒,故這裡的賦值為:2^256 - 3153600 = 115792089237316195423570985008687907853269984665640564039457584007913098103936 ),第二次賦值為0(賦值為0,判斷條件也恆成立)。

第三個變量msg.sender,從上圖可以看出,該變量傳入後覆蓋全局變量owner,當前調用者地址就會變為合約所有者,從而就可繞過confiscate()函數中msg.sender == owner判斷條件。

由於secret存儲在storage(storage變量是指永久存儲在區塊鏈中的變量),所以我們可以調用storage索引獲取裡面的值。

至此confiscate()函數中的前三句判斷條件均已滿足。

解題思路

通過分析BelluminarBank合約漏洞,我們可以利用整數溢出,變量覆蓋,訪問權限等漏洞攻擊轉出合約所有餘額。具體解題思路如下:

通過調用invest()函數傳入account為1,deposit_term為115792089237316195423570985008687907853269984665640564039457584007913098103936,攜帶的msg.value為1wei。account始終根據第一句的判斷條件進行賦值。msg.value賦值amount,再進行balances變量覆蓋(由於變量循環賦值的關係),最終結果balances=amount=2;deposit_term值變量覆蓋也成為head值;調用者地址msg.sender最終變量覆蓋後會成為合約所有者owner。調用之後balances[balances.length - 1].deposit_term + 1 years發生整數溢出,繞過判斷條件。繼續調用invest()函數傳入account為2,deposit_term為0,攜帶的msg.value為2wei,msg.value賦值amount,再進行balances變量覆蓋(由於變量循環賦值的關係),最終結果balances=amount=3;deposit_term值變量覆蓋也成為head值為0,相當於還原head原始的值。由於balances變量的循環覆蓋的關係,最終的合約餘額會有差別,可通過合約自毀或者withdraw()函數調整合約餘額。調用confiscate()函數傳入兩個參數:account為1,secret值為我們之後通過storage獲取的密碼值,最終取走合約所有的餘額。攻擊演示

本次攻擊演示在ropsten測試網進行,使用工具為Remix+Matemask+myetherwallet

Remix在線編輯器:http://remix.ethereum.org/

MetaMask錢包插件:https://metamask.io/

MyEtherWallet在線錢包:https://www.myetherwallet.com/

1.首先部署BelluminarBank漏洞合約

使用在線編輯器Remix通過Meta Mask在線錢包A地址部署BelluminarBank合約,部署時給合約傳入參數為:

value:31337 wei,deposit_term:0x00000000000000000000000000000001,_secret:1000

(為了方便查看數據,我們將合約源碼中的一部分內容進行了可見性修改)

部署完成後目前合約中變量值為以下

2.使用myetherwallet在線錢包調用BelluminarBank合約

在remix中獲取api並複製部署的合約地址,填入myetherwallet錢包中。

連接成功

3.調用invest()函數修改合約所有者owner,存款期限數值deposit_term,變量重複覆蓋值amount

傳入參數為:

value:0.000000000000000001 ETH,account:1,deposit_term:115792089237316195423570985008687907853269984665640564039457584007913098103936。

完成後目前合約中變量值為以下

4.調用invest()函數修改存款期限數值deposit_term(修改head為0),變量重複覆蓋值amount

傳入參數為:

value:0.000000000000000002 ETH,account:2,deposit_term:0。

完成後目前合約中變量值為以下

雖然上圖中顯示的合約全部餘額為31340 wei,但調用過程中出現循環變量覆蓋,導致數組中的餘額為31337+2+3 =31342 wei,如下圖所示:

為了使合約本身餘額與數組中的amount匹配,這裡我們選擇強制給該轉幣。

5.通過c地址部署合約並調用taijie()函數自毀合約給BelluminarBank合約轉2 wei,平衡合約數組中的餘額。

自毀成功後,BelluminarBank合約餘額變為31342 wei。

6.調用confiscate()函數最終取走合約所有的餘額

傳入兩個參數:account:2,secret:0x00000000000000000000000000000001

調用完成後,BelluminarBank合約餘額變為0。

至此完成攻擊演示

總結

本篇文章中,我們通過2018WCTF比賽中的BelluminarBank智能合約題目,詳細分析了合約存在的漏洞問題,提供了解題思路並進行了攻擊演示,其中使用的相關工具已在文中給出連結,希望對智能合約初學者及愛好者有所幫助,下一篇我們會繼續分享CTF智能合約經典題目,請大家持續關注。

相關焦點

  • 零時科技丨CTF技能寶典之智能合約 薅羊毛漏洞
    前言近年來,各個大型CTF(Capture The Flag,中文一般譯作奪旗賽,在網絡安全領域中指的是網絡安全技術人員之間進行技術競技的一種比賽形式)比賽中都有了區塊鏈攻防的身影,而且出現的題目絕大多數都是區塊鏈智能合約攻防。此系列文章我們主要以智能合約攻防為中心,來剖析智能合約攻防的要點,前兩篇我們分享了合約反編譯,反彙編的基礎內容。
  • 商務白話「智能合約」
    然而,智能合約卻天然的具備一項特殊的技能,可以從頭杜絕、消滅交割風險。  原子性(Atomic)操作,是資料庫編程的概念,講的是對資料庫進行操作時,一個事務(Transaction)所包含的所有步驟應當為不可分割的一個整體,要麼所有步驟都執行完畢,要麼就將資料庫回滾到沒有任何一個步驟被執行的初始狀態,不能只執行一部分步驟而放棄其餘,否則將破壞資料庫業務邏輯的完整性。
  • 波長鏈智能合約靠譜嗎?
    波長鏈智能合約 波長鏈智能合約靠譜嗎? 波場鏈是搭建在波場鏈上的一個項目,自從8月全球市場啟動以來,目前全球已經有40多個國家的玩家參與,美國市場首當其衝。波場鏈tronchain 9月才開始進入大陸。波長鏈智能合約靠譜嗎?
  • 聯盟分析-政策法規 | DeFi閃電貸遭攻擊背後——DeFi智能合約安全...
    DeFi安全事件主要是由於其智能合約安全漏洞的問題,在此大背景下,本文對DeFi智能合約安全漏洞的相關法律問題進行研究。DeFi 智能合約在自動化執行、效率等方面具備了其他執行方式所不可比擬的優越性。但是,基於其計算機代碼的本質,DeFi 智能合約也存在一定的局限性,主要有代碼漏洞與執行的問題。(一)代碼漏洞問題由於計算機代碼人類編寫,就不可避免存在漏洞。
  • 希臘神話-卡俄斯智能合約的安全性如何考證,技術大神解讀
    用戶和智能合約交互那麼用戶怎麼和智能合約進行交互呢?看下圖:首先智能合約就是一段預先編寫好的程序,部署在區塊鏈上。雖然我們沒有私鑰去直接操作合約地址裡面的資產,但是可以通過合約程序定義的方法和合約進行互動,以【卡俄斯Chaos】智能合約為例,最核心的有三個方法:兌換母幣:張三通過往智能合約地址轉入TRX,智能合約通過預先制定的兌換比例返還一定數量的母幣給她,此時智能合約地址裡面就有了TRX資產,但是沒有任何人能直接將TRX資產通過私鑰轉移走,因為壓根兒就沒有私鑰
  • 《明日方舟》危機合約技能怎麼選 危機合約技能選擇推薦
    導 讀 今天小編為大家帶來的是明日方舟遊戲中危機合約的推薦技能匯總介紹,下面小編偉告訴大家為何選擇這些技能,一起來看一下吧。
  • 流量獵手,一機多用 | 蠻牛智能合約一體機正式發售
    2020年12月2日,東莞市蠻牛計算機科技有限公司在重慶舉行了戰略合作籤約儀式並正式發布智能合約一體機全國開售活動。本次會議活動由企校中國組委會與東莞市蠻牛計算機有限公司(下文簡稱蠻牛科技)聯合組辦。
  • 獵豹區塊鏈研究中心楊文玉:智能合約自動化審計存在誤報率高、自動...
    在數量穩定增長的同時,智能合約還面臨著安全問題。從 2017 年到 2018 年 6 月,智能合約漏洞頻繁爆發,帶來大量資金損失,也使得區塊鏈甚至智能合約的一些開發者或者用戶對智能合約的安全性產生質疑,也阻礙了以太坊之後的發展。 除此之外,Fomo3D 在興起的時候,僅僅在第二天就出現了大量的山寨合約。
  • 隱私2.0——Oasis Network如何保障智能合約隱私
    在加密貨幣發展的過程中,門羅幣、大零幣和達世幣背後所用的技術主要是零知識證明、環籤名和混幣技術,這幾種方式使得在交易的過程中可以隱匿自己的交易信息,這幾種技術雖然效果很不錯,技術成熟,但是僅僅處於一種低階應用的場景中,而且還隨著監管的加強,應用場景將會進一步減小,我們將之稱為區塊鏈隱私1.0階段。
  • 人民創投 | 區塊鏈之後火起來的智能合約到底是什麼?【新知】
    文丨myacqkl達沃斯之父與區塊鏈作為人類文明每次躍升轉折點的蒸汽、電力和計算機具有不可替代的根本性革命意義。區塊鏈技術不僅可以支持可編程合約,而且具有去中心化、不可篡改、過程透明可追蹤等優點,天然適合於智能合約。因此,也可以說,智能合約是區塊鏈技術的特性之一。什麼是智能合約智能合約,簡而言之就是一套以數字形式定義的承諾,包括合約參與方可以在上面執行這些承諾的協議。其中,一套承諾指的是合約參與方同意的權利和義務。
  • 通用圖靈機和智能合約之間的關係是什麼
    作為團隊的非技術成員,我想了解圖靈完備的真正含義,為什麼區塊鏈社區在這個問題上存在分歧,為什麼我們的工程師在努力教育其他人安全智能合約編程的缺點。所以,如果你像我一樣,被區塊鏈系統的開發所吸引,尤其是被智能合約語言所吸引,那麼希望這個故事能夠提高你對圖靈完備如何影響智能合約安全的理解。 圖靈機促進圖靈完備 20世紀30年代,艾倫·圖靈提出了通用圖靈機的概念。請注意,圖靈機不同於二戰期間用來解決Enigma代碼的設備。這就是炸彈機。
  • 技術極客:周鴻禕誇大了360發布的EOS安全漏洞的危害
    網易科技訊 5月30日消息 近日,360發布EOS高危安全漏洞消息。EOS創始人BM回應稱,360在故意製造恐慌。周鴻禕回答王峰邀十問時對此表示否認。網易科技在「3點霞客行&EOS踢館護館群」中邀請嘉賓,就EOS此次發現安全漏洞問題展開討論。
  • 擁有智能合約的比特幣網絡—RSK
    打開APP 擁有智能合約的比特幣網絡—RSK 發表於 2019-07-04 10:32:32 因此RSK作為BTC的側鏈加入了新的轉帳速度認證和BTC智能合約系統。 1.RSK整合了圖靈智能合約,為比特幣加入了智能合約,並且為整個網絡提供了更快的交易速度。比特幣將跨入一個新的轉帳紀元。 2.RSK每秒處理300筆交易,20秒內幾乎可以確認所有的交易。並且RSK使用和BTC一樣的挖礦機制。
  • STD-Chain——以太坊智能合約Solidity以及IPFS星際文件協議
    文章中說:「好的企業,要用商業手法解決社會問題、推動社會發展,而不是尋找社會漏洞去發展。解決的社會問題越大,企業就越偉大。如果能為國家解決問題,那就是一個國家企業。」馬雲創建的阿里巴巴集團,將電商、物流、金融等各項業務做得十分出色,那麼盛大公鏈是不是一個好項目?
  • IMU PLUS引領行業新風向 全方位保障智能合約安全
    區塊鏈2.0時代被定義為數字貨幣與智能合約的疊加,這時候區塊鏈技術在金融行業的應用達到廣泛場景化及流程化升級,其中以智能合約為代表。IMUPLUS發現,在區塊鏈2.0時代可以輕鬆實現智能合約的上傳和執行,合約執行過程中的安全和隱私有了保證。除此之外,數字資產利用區塊鏈技術的不可更改、可編程性,在區塊鏈上進行登記的股票和債券,可以靠智能合約進行點對點的自主交易,自我結算。
  • RSK實驗室收購比特幣智能合約協議Rootstock
    RSK實驗室收購比特幣智能合約協議Rootstock 圖片版權所屬:站長之家>今日塊訊(ChinaZ.com) 11 月 19 日消息   據cointelegraph消息,基於比特幣的智能合約協議Rootstock( RSK )正在與分拆項目RIF OS合併,以擴大其運營範圍。
  • MMMDeFi(MDF)智能合約到底是什麼
    DeFi是區塊鏈智能合約領域中誕生的新詞彙Decentralized Financial的簡寫。 去中心化是區塊鏈智能合約思想和精神的全面整合,這是人類渴望的最高生命形式。 MMMDefi(MDF)智能合約是原MMM運營團隊整合多國技術人才及業內精英,按照馬夫羅季先生思想和積累多年的互助理論基礎+數學流動運算邏輯實際推演+去中心化智能合約技術支持+世界廣泛認可的文化思想等集成的。經過13個月的精心籌備從2019年7月開始設計與不斷地完善、反覆精算推演內測與修改。
  • 火龍果財經:區塊鏈智能合約與傳統合同有什麼區別
    事實上,因為維權成本高,真正拿起法律武器去維護權益的人少之又少。許多人只是把「籤合同」當成走過場,而不是把其當作具備法律效力的承諾。其次,我們來了解一下智能合約。智能合約是什麼?智能合約是一種能以信息化的形式傳播且具有驗證或者執行合同功能的計算機協議。換句話說,智能合約是一種數位化的計算機合作協議,而且這個合作協議可以在第三方缺席的情況下執行。