CTF区块链 出题指南 solidity篇

渗透技巧 1年前 (2023) admin
792 0 0

前言:

区块链方向已然成为ctf一大主流的竞赛内容,故写此文章来分享如何出eth相关的题目,主要面向solidity,move语言暂时不在本文章讨论范围内,后续看心情编写,本文只介绍如何在公链和私链部署智能合约题目。

在测试链上部署智能合约:

首先谈一下测试链的问题,以及为什么逐渐被淘汰了,首先的问题就是测试链不防上车,一个选手解出来后通过eth的浏览器可以轻松的看见该选手的exp内容,可以快速实现上车的目的。

接下来浅谈一下如何在测试链上部署题目:

关于测试币如何领取可以参考之前的文章:Goerli与Sepolia,mumbai测试币领取-魔法少女雪殇 (snowywar.top)  (http://www.snowywar.top/?p=3887)

关于测试链上合约编写特点

由于测试链上环境共享,每个选手在做题时独立来部署合约不现实(现在可以实现了,后续介绍),所以通用办法是写一个mapping来记录每个选手所交互的地址是否满足条件,如果满足条件则触发emit事件(emit可以由后台脚本进行监听)来决定是否进行发送相关的flag内容等。

案例演示:

编写智能合约部署在测试网:

pragma solidity ^0.8.9;

contract Testcontract {
mapping(address=>uint) _flag;

event sendflag(string email,string tokens);

function test() public {
_flag[msg.sender] = 1;
}
function payforflag(string memory _base64Email,string memory _token) public {
require(_flag[msg.sender] == 1);
emit sendflag(_base64Email , _token);
}
}


CTF区块链 出题指南 solidity篇

然后基本上所有的eth浏览器都给了可以查询的api接口,比如我用的mumbai测试网的api网址:

Mumbai PolygonScan – PolygonScan

接下来就是参考文档写轮询脚本即可。

花了五分钟写的,大概逻辑就是反复请求给的api就行了,然后把event进行解密,之后发送邮箱,具体思路这样:

from web3 import Web3, HTTPProvider
import requests
import smtplib
from email.mime.text import MIMEText
from email.header import Header
import base64
import time


sender = "[email protected]"
mail_host = "smtp.qq.com"
smtpport = 465
password = "xxxx"


def send_mail(mail, token):
    try:
        smtp=smtplib.SMTP_SSL(mail_host, smtpport)
        smtp.login(sender, password)
        message = MIMEText("flag is a flag{123}""plain""utf-8")
        message["From"] = Header("Sender""utf-8")
        message["To"] = Header("Receiver""utf-8")
        subject = "here is your flag"
        message["Subject"] = Header(subject, "utf-8")
        smtp.sendmail(sender, mail, message.as_string())
    except smtplib.SMTPException as e:
        print(e)
    finally:
        smtp.quit()

topics = []

def event():
    url = "https://api-testnet.polygonscan.com/api?module=logs&action=getLogs&topic1&address=xxxxx"
    response = requests.get(url)
    data = response.json()
    print(data)
    if data['status'] == '1':
        for i in data['result']:
            if i["topics"][0in topics:
                continue
            else:
                #发送邮件
                logs = i['data']
                logs = logs[2:]
                logs = [logs[i:i+64for i in range(0, len(logs), 64)]
                logs[3] = Web3.toText(Web3.toBytes(hexstr=logs[3]))
                logs[5] = Web3.toText(Web3.toBytes(hexstr=logs[5]))
                mail = logs[3]
                token = logs[5]
                mail = base64.b64decode(mail).decode()
                send_mail(mail, token)
                topics.append(i["topics"][0])
                
while(True):
    event()
    time.sleep(20)

具体内容log要做具体解析,看区块链浏览器的event就好。

CTF区块链 出题指南 solidity篇

效果演示:

CTF区块链 出题指南 solidity篇


测试链结合自动部署出题方案

该方案部署起来也比较方便,不需要单独运行水龙头等内容,可以运行在测试网上

使用工具:

eth-challenge-base/example at main · chainflag/eth-challenge-base (github.com)

(https://github.com/chainflag/eth-challenge-base/tree/main/example)

后续也会使用这套工具来进行,还是非常感谢这套系统的作者的。

项目克隆下来后,进入example路径,编写.env文件:

CTF区块链 出题指南 solidity篇


其余几个不用管,常规题目基本上用不傲,只需要注意第一个即可。

前往app.infura.io网站注册,获取api:

CTF区块链 出题指南 solidity篇


选择想要部署的网络,复制即可。

将你编写好的合约文件放入contract文件夹中,来编写一个测试合约:

pragma solidity 0.8.7;

contract Checkin {
string greeting;

constructor(string memory _greeting) public {
greeting = _greeting;
}

function greet() public view returns (string memory) {
return greeting;
}

function setGreeting(string memory _greeting) public {
greeting = _greeting;
}

function isSolved() public view returns (bool) {
string memory key = "HelloCTF";
return keccak256(abi.encodePacked(key)) == keccak256(abi.encodePacked(greeting));
}
}

然后编写challenge.yml :

CTF区块链 出题指南 solidity篇


其实注释也写得比较完整,比较需要注意的是contract和args,contract上要写的内容是合约的名称,比如上述合约是Checkin,那么这个是checkin,包括后续去编写工厂合约还是ERC20等相关内容,只需要写上主要的主合约名称即可。

args决定了部署合约时传输的内容,以数组模式表示,此处对应constructor,如果传输数量和 constructor所需内容不符就会报错,这里要对应上,如果不需要就注释。

value值决定了该合约在创建后有多少个eth,对应的在创建时需要转给相应的eth,单位是eth。

其他的就没什么好说了,后面都有注释。

(注:合约要有isSolved函数,如果没有就去修改solved_event)

编写好后就可以docker-compose up -d进行部署测试,编译需要一段时间,等几分钟再nc查看,如果报错可能是网络问题,自行解决。

nc即可,如有需要也可以修改docker-compose.yml来修改端口。

效果演示:

CTF区块链 出题指南 solidity篇

CTF区块链 出题指南 solidity篇


还是比较方便的,也不需要去维护geth,如果没有特殊要求我个人还是比较推荐该方案的。

当然所有数据在测试网均可查,转账也是用metamask进行转账操作。

私链水龙头+自动部署题目出题方案

这个方案应该是如今ctf环境用得比较多的,当然部署起来还是有点小坑,这里也都编写出来分享一下。

整体的项目还是参考chainflag/eth-challenge-base: xinetd docker for building ethereum contract challenges (github.com) (https://github.com/chainflag/eth-challenge-base)

这个项目,具体修改内容如下:

主目录的entrypoint.sh可能存在编码问题,我建议跑一次uinx2doc。

CTF区块链 出题指南 solidity篇

然后其余不用动了,进入geth文件目录,主要修改内容是.env.example

CTF区块链 出题指南 solidity篇

这些内容其实在 readme里面有详解,那么也还是说一下:chain_id其实无所谓,就是指定一个id,任意即可,第二个是geth所指定的一个用户地址,这个需要用来当水龙头的分发地址和miner的地址,必须要有的,第三行是对应上面地址的一个私钥,可以从metamask导出,也可以自主生成,总之要和地址对应起来,第四行也任意,配置好后保存重命名为.env文件,修改项目自带的njs路径下的eth-jsonrpc-access.js 。

项目自带的该文件无法让用户自行部署合约,这很不好,不符合我使用remix进行解题的幻想,重写为如下内容:

function access(r{
  var whitelist = [
      "eth_blockNumber",
      "eth_call",
      "eth_chainId",
      "eth_estimateGas",
      "eth_gasPrice",
      "eth_getBalance",
      "eth_getCode",
      "eth_getStorageAt",
      "eth_getTransactionByHash",
      "eth_getTransactionCount",
      "eth_getTransactionReceipt",
      "eth_sendRawTransaction",
      "net_version",
      "rpc_modules",
      "web3_clientVersion"
  ];

  try {
      var payload = JSON.parse(r.requestBody);
      if (payload.jsonrpc !== "2.0") {
          r.return(401"jsonrpc version not supportedn");
          return;
      }
      // if (!whitelist.includes(payload.method)) {
      //     r.return(401, "jsonrpc method is not allowedn");
      //     return;
      // }
      if (Object.keys(payload).filter(key => key.toLowerCase() === 'method').length > 1) {
          r.return(401"jsonrpc method is not allowedn");
          return;
      }
  } catch (error) {
      r.return(415"Cannot parse payload into JSONn");
      return;
  }

  r.internalRedirect('@jsonrpc');
}

export default { access }

(不排除其他问题,但却是使用默认的无法部署合约。欢迎补充)

修改后然后编写docker-compose.yml ,

把上个方案的那些内容拿过来,然后把uri改为geth:8545 

CTF区块链 出题指南 solidity篇


文件也复制在同一目录下,在docker-compose.yml中追加,当然也可以部署多个,直接后面继续修改端口和新建几个合约文件夹即可,这样题目和水龙头就都在一个网络里了:

CTF区块链 出题指南 solidity篇


docker-compose up -d进行部署:

CTF区块链 出题指南 solidity篇


可以访问8080查看水龙头:

CTF区块链 出题指南 solidity篇


可以在metamask进行连接rpc网络:

CTF区块链 出题指南 solidity篇


剩下的与上一方案基本没差别,只是把整体网络搬到了私链,极大程度加大了上车的难度。整体响应速度也比在公链上快,也不会出现搞不到水龙头的情况。

补充:该项目nc后的第四个选项只会显示主要编译的合约,如果出现了多合约的题目,个人建议再challenges.yml的show_source:False这段取消注释,然后以附件的形式提供题目源码等内容。当然有能力的也可以去修改app.py源码,可以自行发挥。

CTF区块链 出题指南 solidity篇

总结

以上是常见的三种eth出题的一个方案,也算是在自己近期出题的时候踩的坑和相关内容进行一个记录,有些地方可能存在不全或者不正确的问题,也希望大家能够指出,大家伙也给chainflag这个项目去点点star,真的非常牛逼。

因为本文是关于出题方面的文章,当然也离不开CTF比赛,相信大家对于春秋GAME实验室都有所耳闻,其题目质量值得称赞,真的狠狠的期待住了未来春秋GAME的比赛。

参考文章

https://app.infura.io/

智能合约攻击面及ctf出题指南 – DoubleMice – 我以晦朔春秋为聘,不知你愿否共我度完蜉蝣小年(https://doublemice.github.io/智能合约攻击面及ctf出题指南/)。

chainflag/eth-challenge-base: xinetd docker for building ethereum contract challenges (github.com) (https://github.com/chainflag/eth-challenge-base)

CTF区块链题目环境的防抄袭方案 | iczc (https://www.iczc.me/post/solution-for-a-fair-ctf-blockchain-env/)


CTF区块链 出题指南 solidity篇

原文始发于微信公众号(i春秋):CTF区块链 出题指南 solidity篇

版权声明:admin 发表于 2023年2月8日 下午5:01。
转载请注明:CTF区块链 出题指南 solidity篇 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...