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

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

智能合約部署

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

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

有了以太幣以後就能夠利用Remix IDE將合約代碼部署到測試網絡。
這裏先準備一個簡單的發送flag的合約:web

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。ide

郵件發送腳本的編寫

先註冊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出題指南

相關文章
相關標籤/搜索