前言

有幸也是拿到了other下三道题目的一血,来写下wp。



文字频率分析

下载附件得到题目源码

import os
import random
import base64
import time
from PIL import Image, ImageDraw, ImageFont, ImageFilter
import io
from secret import flag
import hashlib


def pow(hard_level = 6):
    if hard_level >= 30:
        hard_level = 30


    m = hex(random.getrandbits(16 * 8))[2:]
    c = hashlib.md5(m.encode()).hexdigest()
    part = m[:32 - hard_level]
    print(f'plaintext: {part}' + '?' * hard_level)
    print(f'md5_hex -> {c}')
    return part, c


def crack(part, c, level = 6):
    s = int(part, 16) << (level * 4)
    _limit = s + (1 << level * 4)
    while s <= _limit:
        s += 1
        seed = hex(s)[2:].encode()
        if hashlib.md5(seed).hexdigest() == c:
            return seed


def getData(nums = 400):
    data = []
    result = [0 for _ in range(26)]
    for i in range(nums):
        c = random.randint(0, 25)
        data.append(c)
        result[c] += 1
    result = [str(i) for i in result]
    return data, result


def makeImage(data = [], size = 20):
    img = Image.new('RGB', (size * 50, size * 50))
    for y in range(size):
        for x in range(size):
            img.paste(pngs[data[x + y * size]], (x * 50, y * 50))
    content = io.BytesIO()
    img.save(content, 'png')
    img.save('flag.png')
    img.close()
    return content.getvalue()


def game():
    print("Easy pow:")
    pm, c = pow()
    m = input("What's is plaintext?\n> ")
    if hashlib.md5(m.encode()).hexdigest() != c:
        print('Failed')
        return


    data, result = getData()
    png = makeImage(data)
    print('now, you get a png: ')
    print(base64.b64encode(png).decode())
    print("##The end, please tell me your list:")
    _start = time.time()
    _input = input('> ').split(',')
    _end = time.time()


    if _end - _start > 10:
        print('timeouted')
        return


    if _input == result:
        print("Got it!")
        print(flag)
    else:
        print('Sorry, try again.')


if __name__ == '__main__':
    print('loading pngs, please waiting...')
    pngs = []
    seed = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
    for i in range(26):
        pngs.append(Image.open(f'/app/src/{seed[i]}.png').resize((50, 50), Image.ANTIALIAS))
    game()


以及图片


分析源码,能得知做了以下几件事:

  1.  随机获取字符串并截取,并给出md5,要求我们还原字符串

  2. 还原后,脚本以26字母表的顺序对应题目给的图片,进行随机的选取400次图片,并拼成大图,以base64的形式展现给我们

  3. 要求我们通过大图来还原每张图片出现的频率,以字母表的顺序排序。最后要求我们输入频率的序列进行对比,正确则得到flag

刚开始爆破md5后,得到base64字符串还原得到这张图片:


每张小图50*50大小,共1000*1000的大小。

所以我的思路,先是分辨:通过PIL模块分割小图,先取26张不重样的图片,对照字母表进行一个分辨最后保存到文件夹里。

之后进行批量识别:创建一个26位的列表,分割大图为小图,与先前保存好的图片进行比对(用getdata),将相同的图片根据辨别好的字母表找到对应的索引,添加到列表的下标。最后打印列表就是题目的答案了。

一把梭脚本:

from PIL import Image
from pwn import *
import base64
from itertools import product


p = remote('47.97.127.1',22398)


def getm(c,hash):
    table = string.hexdigits[:-6]
    for i in product(table,repeat=6):
        a = c + ''.join(i)
        if hashlib.md5(a.encode()).hexdigest() == hash:
            return a


def step1():
    p.recvuntil('plaintext:')
    m = p.recv().decode().split('\n')
    m1 = m[0].strip().split('?')[0]
    m2 = m[1].split('->')[1].strip()
    a = getm(m1,m2)
    print(a)
    #p.recvuntil("What's is plaintext")
    p.sendline(a)
    p.recvuntil('now, you get a png:')
    content = ''
    c = p.recv().decode()
    while "##The end" not in c:
        content += c
        c = p.recv().decode()
    content += c
    content = content.split('##')[0].strip()
    return content


def getdata():
    content = step1()
    print(content)
    open('new.png','wb').write(base64.b64decode(content))
    img = Image.open('new.png')
    w,h = img.size
    n = 0
    for y in range(0,1000,50):
        for x in range(0,1000,50):
            n += 1
            a = img.crop((x,y,50+x,50+y))
            a.save(f"img/{n}.png")
def getflag():
    getdata()
    result = [0]*26
    datas = [0]*26
    for i in range(26):
        name = str(i).zfill(2)
        data = list(Image.open(f'zimubiao/{name}.png').getdata())
        datas[i] = data


    for i in range(1,401):
        data = list(Image.open(f'img/{i}.png').getdata())
        print(datas.index(data),f'{i}.png')
        result[datas.index(data)] += 1
    flag = ''
    for i in result:
        flag += str(i)+","
    flag = flag[:-1]
    print(flag)
    p.sendline(flag)
    while 1:
        print(p.recv())


getflag()


def getimage():
    ls = []
    a = {}
    for i in range(1,401):
        img = Image.open(f"img/{i}.png")
        data = list(img.getdata())
        a[i] = data


    for i in a.values():
        if i not in ls:
            ls.append(i)
    print(len(ls))


    for k in ls:
        for i in a.keys():
            if a[i] == k:
                print(f'img/{i}.png')
                image = Image.open(f'img/{i}.png')
                image.save(f'zimubiao/{i}.png')
                break

脚本的getimage()是将大图中不重样的小图保存到本地,图片的名称是我自己根据字母表改名的:


运行得到flag

图片识别

下载附件得到题目源码

import os
import random
import base64
import time
from PIL import Image, ImageDraw, ImageFont, ImageFilter
import io
from secret import flag
import hashlib


animals = ['cat', 'pig', 'tigger', 'dog', 'cattle', 'mouse', 'bird', 'chicken', 'rabbit', 'koala', 'horse', 'deer', 'lion', 'elephant']
names = ['1.jpg','10.jpg','2.jpg','3.jpg','4.jpg','5.jpg','6.jpg','7.jpg','8.jpg','9.jpg']


def pow(hard_level = 6):
    if hard_level >= 30:
        hard_level = 30


    m = hex(random.getrandbits(16 * 8))[2:]
    c = hashlib.md5(m.encode()).hexdigest()
    part = m[:32 - hard_level]
    print(f'plaintext: {part}' + '?' * hard_level)
    print(f'md5_hex -> {c}')
    return part, c


def crack(part, c, level = 6):
    s = int(part, 16) << (level * 4)
    _limit = s + (1 << level * 4)
    while s <= _limit:
        s += 1
        seed = hex(s)[2:].encode()
        if hashlib.md5(seed).hexdigest() == c:
            return seed


def game():
    print("Easy pow:")
    pm, c = pow()
    m = input("What's is plaintext?\n> ")
    if hashlib.md5(m.encode()).hexdigest() != c:
        print('Failed')
        return


    _score = 0
    for _ in range(10):
        animal = random.choice(animals)
        name = random.choice(names)
        img = Image.open(f'/app/animal/{animal}/{name}')
        img = img.rotate(random.randint(30, 170), expand = 1)
        x, y = img.size
        _rate = random.randint(-50, 10)
        x, y = int(x + x * _rate / 100), int(y + y * _rate / 100)
        img = img.resize((x, y), Image.ANTIALIAS)
        content = io.BytesIO()
        img.save(content, format='jpeg')
        print(base64.b64encode(content.getvalue()).decode())
        img.close()
        content.close()
        time.sleep(1)
        _start = time.time()
        _input = input("What's animal in this picture?\n> ")
        _end = time.time()


        if _end - _start > 3:
            print('timeouted')
            return


        if _input != animal:
            print(f"Oh, This is {animal}")
        else:
            print("Yes, it's")
            _score += 1


    if _score >= 8:
        print("Great, you are good.")
        print(flag)
    else:
        print('Failed')


if __name__ == '__main__':
    game()

与上题类似,同样是爆破MD5,不同的是这里将图片进行了一个旋转和改变大小的操作,要求我们通过变换后的图片识别出这是哪类动物,可以在题目附件文件夹中找到同样的图片。

这里刚开始的思路是想像上一道题一样通过还原找到原图,但是发现这次只给了我们3秒钟的时间


但是我们如果要还原大小和旋转则需要爆破,这个过程是会超过3秒的。所以这一个思路pass掉了。

想起前几天rctf做的一道catspy,也是一道识别题,里面用到了模型识别的模块比较深刻。

def divide(img):
  # Step 1: Initialize model with the best available weights
  weights = ResNet50_Weights.DEFAULT
  model = resnet50(weights=weights)
  model.eval()


  # Step 2: Initialize the inference transforms
  preprocess = weights.transforms()


  # Step 3: Apply inference preprocessing transforms
  batch = preprocess(img).unsqueeze(0)


  # Step 4: Use the model and print the predicted category
  prediction = model(batch).squeeze(0).softmax(0)
  class_id = prediction.argmax().item()
  category_name = weights.meta["categories"][class_id]
  return category_name


模型识别能识别图片进行分析是什么东西,也是需要训练。

刚开始用这个识别难免会与答案有些不符,但由于每次错误后题目会把正确答案提示出来,所以可以让识别的结果与答案做一个字典处理,然后每次识别后先看字典中是否存在这一识别结果 若有则发送答案。

所以我这道题就是识别+爆破做出来的,当时凑巧要出去做核酸就挂机运行这个脚本,回来的时候发现已经有flag了。

脚本:

from torchvision.models import resnet50, ResNet50_Weights
from PIL import Image
from pwn import *
import base64
from itertools import product


animals = ['cat', 'pig', 'tigger', 'dog', 'cattle', 'mouse', 'bird', 'chicken', 'rabbit', 'koala', 'horse', 'deer', 'lion', 'elephant']
dc = {'tiger':'tigger','brambling':'bird','hamster':'mouse',"Angora":"rabbit","rhinoceros beetle":"horse","koala":"koala","Ibizan hound":"cattle","tabby":"cat","dhole":"deer",'kuvasz':'dog','jacamar':"bird",
'redbone':'horse','Border collie':'dog','Norfolk terrier':'mouse',"muzzle":"elephant","hog":"pig","fox squirrel":'cat','Sussex spaniel':'dog','clumber':'chicken','patas':"deer"}


def getm():
    p.recvuntil('plaintext:')
    m = p.recv().decode().split('\n')
    c = m[0].strip().split('?')[0]
    hash = m[1].split('->')[1].strip()
    table = string.hexdigits[:-6]
    for i in product(table,repeat=6):
        a = c + ''.join(i)
        if hashlib.md5(a.encode()).hexdigest() == hash:
            return a


def decode(name):
    t1 = time.time()
    content = ''
    c = p.recvline().decode()
    print(c)
    n = 0
    while "What's animal in this picture" not in c:
        content += c
        c = p.recvline().decode()
        n += 1
        print(n,c[:60])
        if "flag" in c:
            open('flag.txt','a').write(c)
    content = content.strip().split('\n')[-1]


    f = open(name,'wb')
    f.write(base64.b64decode(content))
    t2 = time.time()
    print(f'{name} 图片构写完毕 耗时 {t2-t1}s')


def divide(img):
  # Step 1: Initialize model with the best available weights
  weights = ResNet50_Weights.DEFAULT
  model = resnet50(weights=weights)
  model.eval()


  # Step 2: Initialize the inference transforms
  preprocess = weights.transforms()


  # Step 3: Apply inference preprocessing transforms
  batch = preprocess(img).unsqueeze(0)


  # Step 4: Use the model and print the predicted category
  prediction = model(batch).squeeze(0).softmax(0)
  class_id = prediction.argmax().item()
  category_name = weights.meta["categories"][class_id]
  return category_name

def sickimg():
    res = getm()
    p.sendline(res)
    for i in range(11):
        t1 = time.time()
        decode(f"{i}.jpeg")
        img = Image.open(f"{i}.jpeg")
        category_name = divide(img)
        print('模型结果:%s'%category_name)
        a = 0
        if category_name in dc.keys():
            for i in dc.keys():
                if category_name == i:
                    a = 1
                    print(f'1 发送{dc[i]}')
                    p.sendline(dc[i])
                    break
        else:
            for i in animals:
                if i in category_name:
                    print(f'2 发送{i}')
                    a = 1
                    p.sendline(i)
                    break
        if a != 1:
            print(f'3 发送mouse')
            p.sendline('mouse')
        t2 = time.time()
        print("time",t2-t1)


for i in range(1000):  
    p = remote('47.97.127.1',29335)
    try:
        sickimg()
        p.close()
    except:
        pass
    sleep(3)

垃圾邮件分析

这道题没有附件,直接远程连接题目。


爆破sha256,脚本:

import string
from itertools import product
import hashlib


m1 = 'u8tkfHbguwjvbuf5pdYVbhknh9Y9'
m2 = 'ca7a86b1d4d914dd78c5f434b173b8ec8d51cfc71ede5ae1be5b47e54e121cf8'
table = string.ascii_letters + string.digits
print(table)
for i in product(table,repeat=4):
    a = m1 + ''.join(i)
    if hashlib.sha256(a.encode()).hexdigest() == m2:
        print(a,''.join(i))
        break


发送后触发游戏规则

题目大致意思就是会给你50个样本模型,让你的AI去训练,最后会给你10个样品让你的AI去验证是否为垃圾邮件

由于这题没设置答题时间,让人来识别也是能识别出来的。

我这边是用chatGPT来识别,每道题基本上能分析出正确答案,YYDS。


最后答对10题即可获得flag(补一张题目bug的图片)