實現CTF智能合約題目的環境部署


本文章是參考一位大佬博客學來的。
智能合約題的環境主要包括兩部分:一個是智能合約的部署,另一個就是監聽合約事件進而發送flag的腳本。

智能合約部署

這里寫的合約是指solidity合約,使用Remix IDE。
合約主要部署到以太坊測試鏈而非主鏈上(沒錢😑),幾個主要的測試鏈:Ropsten,Rinkeby,Kovan。
這里需要一個瀏覽器錢包插件MetaMask(可以在FireFox和Chrom上下載),注冊並申請賬戶后,選擇測試網絡(筆者選擇的是Rospten):

新創建的賬戶是沒有以太幣的,需要到測試水管(在首頁點擊存入)申請:

有了以太幣之后就可以利用Remix IDE將合約代碼部署到測試網絡。
這里先准備一個簡單的發送flag的合約:

pragma solidity ^0.4.24; //選擇solidity編譯器版本

contract TestFlag {
    event victory(string b64email,string slogan); //定義事件
    
    function getFlag(string b64email) public {
        emit victory(b64email, "666!"); //觸發此事件,發送flag到郵箱
    }
}

整個編譯器界面是這樣的:

右側選擇編譯器版本,然后點擊Start to compile進行編譯,編譯成功的話右側就會顯示一個寫着合約名稱的綠色框框。
點擊右上角的Run,Envir選擇Injected Web3,賬戶就會自動變為你MetaMask錢包里的賬戶,如果之前沒有部署過這個合約就點擊下方紅框Deploy,此時會跳出支付gas的彈窗,點擊確定即可,等待幾秒合約就會部署完成,最下方就會顯示已部署的合約(及其地址);如果之前部署過相同合約,那么可以將合約地址復制到At Address並點擊藍色按鈕加載合約,效果相同。

紅框getFlag就是合約里的函數,輸入一個郵箱base64字符串(雙引號括上)並點擊紅色按鈕就可以調用此函數了,通過ropsten.etherscan.io可以查到此合約的交易和事件。

智能合約的部署就這樣了,但是現在調用函數還不能收到郵件,現在還缺少自動發送郵件的腳本,往下看。
注意下面的腳本需要用到合約地址和事件日志中的topic0。

郵件發送腳本的編寫

先注冊Infura https://infura.io 獲取遠程節點rpc:

點擊黑色按鈕創建project,然后在KEYS欄中找到ENDPOINT,Ropsten網絡的URL,就是后面腳本中加載的RPC了(注意,API key不要暴露,具體什么安全規則這里咱也不知道😂)。

這里使用python3編寫腳本,需要用到web3的包,提前下一個(不過安裝這個包有一點坑,百度一下如何下載web3.py包)。
附上python腳本:

# -*- coding:UTF=8 -*-
from web3 import Web3,HTTPProvider
import os
import time
import binascii
import base64
import smtplib
from email.mime.text import MIMEText
from email.header import Header

contract_address = "0x128..." # 你的合約地址
contract_topic0 = "0x90c...1e8a11" # 事件日志中的topic0,針對同意合約的所有事件日志的topic0都是相同的
rpc = "https://ropsten.infura.io/v3/1b8...64b0" # 你注冊的Infura中的ENDPOINT


flag = "flag{a_smart_contract_test}"
email = {
    "host":"smtp.163.com",
    "port":25,
    "user":"sender@163.com", # 用來發送flag的郵箱
    "code":"******"  # 郵箱的客戶端授權碼
}
# initial
w3 = Web3(Web3.HTTPProvider(rpc))
sender = smtplib.SMTP(host=email["host"],port=email["port"])
sender.ehlo()
sender.starttls()
sender.login(email["user"],email["code"])

# email content
message = MIMEText("收下你的flag:"+flag, 'plain', 'utf-8')
message["From"] = email["user"]
message["Subject"] = Header("ctf flag","utf-8")

# 發送flag的函數
def sendflag(toEmail):
    message["To"] = toEmail
    sender.sendmail(email["user"],toEmail,message.as_string())
    # log
    os.system("echo "+time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) +": Get flag -- "+toEmail+" >> /tmp/variant_of_cat.log")
    print("send success")

# 監聽合約事件的函數
def event():
    # 從網絡中的事件日志中抓取符合這一合約的日志信息
    flag_logs = w3.eth.getLogs({
            "address":contract_address,
            "topic0":contract_topic0
        })
    if flag_logs is not []:
        for flag_log in flag_logs:
            data = flag_log["data"][2:]
            length = int(data[64*2:64*3].replace('00', ''),16)
            data = data[64*3:][:length*2]
            b64email = binascii.unhexlify(data).decode('utf-8')
            try:
                email = base64.b64decode(b64email).decode('utf-8')
                sendflag(email)
            except:
                errmsg = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())+":decode or send to b64 - {} fail".format(b64email)
                os.system("echo " + errmsg + ">> /tmp/variant_of_cat_error.log")
                print(errmsg)
# 循環運行
while(True):
    event()
time.sleep(30)

運行上述腳本就可以實現一旦調用合約的getFlag函數就能執行發送flag郵件的操作了。
不過這里還有一點小毛病,就是sleep(30)可能短於新區塊的產生時間,導致會連續發送多個郵件過來(我猜測是這個原因,具體后面再推斷)。

參考資料

智能合約攻擊面及ctf出題指南


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM