零時科技丨CTF技能寶典之智慧型合約 薅羊毛漏洞

2021-10-25 13:18:17 字數 3290 閱讀 6144

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

上篇文章中我們分享了ctf比賽中常考的整數溢位漏洞題型,其中用到了變數覆蓋等多種攻擊技巧,需要讀者仔細推敲。本篇文章我們繼續分享ctf比賽中的空投題型,也就是薅羊毛。在系列文章整數溢位題型中,也用到了空投,但只是呼叫一次空投達到觸發其他漏洞的判斷條件,並沒有進行批量獲取空投。

本篇我們以2023年nssc ctf上skybank題目為例,分享智慧型合約薅羊毛的題型,該題型也是多次出現在ctf的賽場。相對於之前的系列文章內容,本篇薅羊毛題型更容易理解。

題目提示

合約原始碼

檢視合約題目,合約存在0.62ether,沒有給出合約原始碼,如下圖:

以下為逆向後的合約**:

pragma solidity ^0.4.24;

contract skybank

function gether() public

function transfer(address to, uint bur) public

}

合約分析

先來看題目最終的判斷函式obtainflag():

function obtainflag(string base64email,string md5namectf)
從該函式可以看出,obtainflag()函式傳入兩個引數(base64email,md5namectf),函式第一行**require(balances[msg.sender] >= 1000000000);會判斷呼叫者位址餘額是否大於等於1000000000 wei,如果滿足該條件,則執行emit sendflag(base64email,md5namectf);**,從題目可以得出,只要參賽者觸發sendflag事件並將引數輸出表示獲取flag成功。

function gether() public
gether()函式中,第一句**require(balances[msg.sender] == 0);判斷當前呼叫者的位址是否為0,如果滿足條件,則給該呼叫者加10000000 wei的資金,我們最終觸發sendflag事件的obtainflag()函式中,需要1000000000 wei,所以只要呼叫gether超過100次就可以觸發sendflag事件。

繼續分析合約的轉賬函式transfer():

function transfer(address to, uint bur) public
transfer()函式中,首先第一行**require(bur == balances[msg.sender]);判斷傳入的引數bur和目前呼叫者位址的餘額是否相等,如果條件滿足,將該餘額轉至傳入的位址to中,之後將呼叫者位址的餘額減掉。這裡非常重要的一點是:轉賬之後的呼叫者位址餘額再次變為0,也就是說我們可以重複該函式進行轉賬。

通過以上skybank題目合約分析,可以總結出兩種解題思路:

第一種:

第二種:

我們進行第一種解題思路的攻擊演示,使用remix+metamask對攻擊合約進行部署呼叫

1. 自毀給題目合約轉幣

由於題目合約的初始狀態沒有ether,故我們通過自毀函式,強行將ether轉入題目合約位址,雖然當前題目合約有一定資金。為了攻擊完整性,也演示一次自毀。

構造自毀合約:

pragma solidity ^0.4.24;

contract burn

}

部署burn合約,並利用kill()函式帶入0.02ether進行自毀,將ether傳送到題目合約位址。

2. 使用a位址部署最終呼叫者合約attacker2(合約位址d)

呼叫**

pragma solidity ^0.4.24;

inte***ce skybankinte***ce

contract attacker2

}

部署成功

3.使用b位址部署獲取空投的合約attacker(合約位址e)

pragma solidity ^0.4.24;

inte***ce skybankinte***ce

contract attacker {

skybankinte***ce constant private target = skybankinte***ce(0xe6bebc078bf01c06d80b39e0bb654f70c7b0c273);

function exploit(uint256 len) public payable {

for(uint256 i=0; i部署成功

呼叫exploit()函式並傳入引數101,獲取101次空投

獲取空投成功

4.使用a位址呼叫d合約的exploit()函式

通過獲取到的ether呼叫exploit()函式觸發題目合約的sendflag事件

成功觸發事件

至此,攻擊完成

mysql建立零時表

在使用mysql中有時需要使用零時表,現在就把幾種建立零時表的方案總結一下 我喜歡先檢查一下是否有這個表,有就刪除 drop temporary table if exists temp1 這是字首,用於檢查是否存在,存在就刪除 1 只建立不寫資料進去!create temporary table ...

IOS從零時開始 序言

每一次努力都是一道坎兒,跨過去海闊天空 過不去被坎兒絆死。這句話自己勉勵的同時送給後來者,說了個題外話。具體的學習痛苦就不說了,方式無非是圖書館借書,網上各種找資料,尤其iteye上面找到很多有用的資料,絕對是低成本的學習路線。現在又因為工作的需要又轉做ios開發了,同樣悲劇的是以前根本就沒接觸過i...

IOS從零時開始 序言

每一次努力都是一道坎兒,跨過去海闊天空 過不去被坎兒絆死。這句話自己勉勵的同時送給後來者,說了個題外話。具體的學習痛苦就不說了,方式無非是圖書館借書,網上各種找資料,尤其iteye上面找到很多有用的資料,絕對是低成本的學習路線。現在又因為工作的需要又轉做ios開發了,同樣悲劇的是以前根本就沒接觸過i...