龙珠NFT

有源码(又是Flask)

# !/usr/bin/env python

# -*-coding:utf-8 -*-

"""

# File       : app.py

# Time       :2022/10/20 15:16

# Author     :g4_simon

# version    :python 3.9.7

# Description:DragonBall Radar (BlockChain)

"""

import hashlib

from flask import *

import os

import json

import hashlib

from Crypto.Cipher import AES

import random

import time

import base64

#网上找的AES加密代码,加密我又不懂,加就完事儿了

class AESCipher():

    def __init__(self,key):

        self.key = self.add_16(hashlib.md5(key.encode()).hexdigest()[:16])

        self.model = AES.MODE_ECB

        self.aes = AES.new(self.key,self.model)

    def add_16(self,par):

        if type(par) == str:

            par = par.encode()

        while len(par) % 16 != 0:

            par += b'\x00'

        return par

    def aesencrypt(self,text):

        text = self.add_16(text)

        self.encrypt_text = self.aes.encrypt(text)

        return self.encrypt_text

    def aesdecrypt(self,text):

        self.decrypt_text = self.aes.decrypt(text)

        self.decrypt_text = self.decrypt_text.strip(b"\x00")

        return self.decrypt_text

#初始化全局变量

app = Flask(__name__)

flag=os.getenv('FLAG')

AES_ECB=AESCipher(flag)

app.config['JSON_AS_ASCII'] = False

#懒得弄数据库或者类,直接弄字典就完事儿了

players={}

@app.route('/', methods=['GET'])

def index():

    """

    提供登录功能

    """

@app.route('/radar',methods=['GET','POST'])

def radar():

   """

   提供雷达界面

   """

@app.route('/find_dragonball',methods=['GET','POST'])

def  find_dragonball():

    """

    找龙珠,返回龙珠地址

    """

    xxxxxxxxxxx#无用代码可以忽略

    if search_count==10:#第一次搜寻,给一个一星龙珠

        dragonball="1"

    elif search_count<=0:

        data={"code":1,"msg":"搜寻次数已用完"}

        return jsonify(data)

    else:

        random_num=random.randint(1,1000)

        if random_num<=6:

            dragonball=一个没拿过的球,比如'6'

        else:

            dragonball='0'#0就代表没有发现龙珠

    players[player_id]['search_count']=search_count-1

    data={'player_id':player_id,'dragonball':dragonball,'round_no':str(11-search_count),'time':time.strftime('%Y-%m-%d %H:%M:%S')}

    #json.dumps(data)='{"player_id": "572d4e421e5e6b9bc11d815e8a027112", "dragonball": "1", "round_no": "9", "time":"2022-10-19 15:06:45"}'

    data['address']= base64.b64encode(AES_ECB.aesencrypt(json.dumps(data))).decode()

    return jsonify(data)

@app.route('/get_dragonball',methods=['GET','POST'])

def get_dragonball():

    """

    根据龙珠地址解密后添加到用户信息

    """

    xxxxxxxxx#无用代码可以忽略

    try:

        player_id=request.cookies.get("player_id")

        address=request.args.get('address')

        data=AES_ECB.aesdecrypt(base64.b64decode(address))

        data=json.loads(data.decode())

        if data['dragonball'] !="0":

            players[data['player_id']]['dragonballs'].append(data['dragonball'])

            return jsonify({'get_ball':data['dragonball']})

        else:

            return jsonify({'code':1,'msg':"这个地址没有发现龙珠"})

    except:

        return jsonify({'code':1,'msg':"你干啥???????"})

@app.route('/flag',methods=['GET','POST'])

def get_flag():

    """

    查看龙珠库存

    """

    #如果有7颗龙珠就拿到flag~

@app.route('/source',methods=['GET','POST'])

def get_source():

    """

    查看源代码

    """

if __name__ == '__main__':

    app.run(host='0.0.0.0',port=80,debug=False)

没发现有什么可以利用的点

反正我是没有什么思路,看了wp之后才略有所悟

首先这题利用点在 address

我们应该要先了解aes.ecb加密

AES(Advanced Encryption Standard)是一种对称加密算法,用于在网络上安全传输数据。AES的加密过程包括以下几个步骤:



密钥扩展:将密钥转换为多个子密钥,以便在后续的轮次中使用。



初始轮:将明文按块进行分组,并与第一个子密钥进行XOR运算。



轮函数:将分组进行一系列的变换操作,包括替换、置换和线性变换等,以增强加密强度。



轮密钥加:将轮函数的输出结果与下一个子密钥进行XOR运算,以进一步混淆数据。



重复执行轮函数和轮密钥加操作,直至达到设定的轮数。



最终轮:在最后一轮中,不执行轮函数操作,只进行轮密钥加和末尾的置换操作。



加密完成后,将加密后的密文发送给接收方,在接收方使用相同密钥进行解密操作。



总体而言,AES算法采用了一系列复杂的技术手段来保证加密数据的安全性,因此在现代计算机上被广泛应用于各种场景下的加密需求,如网络通信、文件存储等。
ECB(Electronic Codebook)模式是一种对称加密算法中的分组密码模式,它将明文分成固定长度的块,然后每个块单独进行加密处理,最终得到密文。具体过程如下:



将明文划分为n个等长的块,每个块的长度与密钥长度相同。



对每个块执行相同的加密算法操作,使用相同的密钥。



最终得到的n个密文块拼接在一起,构成完整的密文。



ECB模式的特点是简单、快速,并且可以并行加解密。但由于相同的明文块会被加密成相同的密文块,因此ECB模式容易受到攻击。例如,如果攻击者知道某个数据块的明文内容,那么他只需要在密文中找到相应的块,就可以轻松地识别出原始明文。



因此,虽然ECB模式在一些特定场景下依然具有一定的应用价值,但在大多数情况下,建议使用更加安全的分组密码模式,如CBC、CFB和OFB等。 	

而在这道题中,ecb分段中每段大小为16字节,即将

{"player_id": "572d4e421e5e6b9bc11d815e8a027112", "dragonball": "1", "round_no": "9", "time":"2022-10-19 15:06:45"}

分段成

1 | {"player_id": "5

2 | 72d4e421e5e6b9bc

3 | 11d815e8a027112"

4 | , "dragonball": 

5 | "1", "round_no":

6 | "9", "time":"20

7 | 22-10-19 15:06:

8 | 45"}

然后我们理一理题目的思路

题目会先获取一个随机值,当随机值在random_num<=6范围时就会让dragonball等于这个值,超过这个范围就让其等于0,接着会将类似于这样的json对象{"player_id": "572d4e421e5e6b9bc11d815e8a027112", "dragonball": "1", "round_no": "9", "time":"2022-10-19 15:06:45"}通过分段,然后进行aes加密成address,也就是说address中包含了这个json对象中的所有数据,而dragonball就是拿到龙珠的关键,所以也就是说我们可以通过改变address来获取龙珠,但是因为我们不知道密钥是多少,所以我们不能直接去解密修改加密,但因为这是分段加密的,所以我们可以去尝试删掉某一段,恰巧,当我们把第五段删掉后,dragonball就等于round_no,那很好,因为我们可以控制round_no(即轮数),那么我们就利用这个脚本去删掉第五段,让dragonball等于round_no

import base64

while True:

    data=input('print input:')

    data=base64.b64decode(data.encode()).hex()

    data=data[:128]+data[160:]

    print(base64.b64encode(bytes.fromhex(data)).decode())

经过七轮后,我们就成功拿到七颗龙珠了,我们也就可以拿到flag了