Запись по задаче CTF на основе машинного обучения.

Команда S1lySUUz

Нам предоставляют адрес и порт. После запуска netcat мы получаем blob с заданной инструкцией

Нам предоставляется большой блок данных (256 КБ), закодированный в base64. После декодирования мы получаем файл с заголовком 78 9c (Zlib Compressed data). После распаковки мне предоставляется файл заголовка JPEG размером 560x560 пикселей.

Похоже, они сделали таблицу изображений 20x20 из набора данных Digits MNIST. MNIST («Модифицированный национальный институт стандартов и технологий») - это де-факто «привет мир» набор данных компьютерного зрения. Теперь вопрос состоит в том, чтобы решить эту капчу, используя обученную модель Digits MNIST и отправить координаты нечетного. 1 изображение неправильное из 400 изображений размером 28x28 пикселей.

Для решения этой проблемы я использовал модель, которую я обучил с использованием 42000 изображений в Keras, давно еще для Digits MNIST.

Модель CNN:

model = Sequential([
    
    
    Conv2D(filters = 64, kernel_size = (3,3),padding = 'Same', activation ='relu', input_shape = (28,28,1)),
    BatchNormalization(),
Conv2D(filters = 64, kernel_size = (5,5),padding = 'Same', activation ='relu'),
    BatchNormalization(),
MaxPooling2D(pool_size=(2,2)),
    Dropout(0.25),
    
    Conv2D(filters = 64, kernel_size = (3,3),padding = 'Same', activation ='relu'),
    BatchNormalization(),
Conv2D(filters = 64, kernel_size = (3,3),padding = 'Same', activation ='relu'),
    BatchNormalization(),
    MaxPooling2D(pool_size=(2,2), strides=(2,2)),
    Dropout(0.25),
Conv2D(filters = 64, kernel_size = (3,3), padding = 'Same',  activation ='relu'),
    BatchNormalization(),
    Dropout(0.25),
Flatten(),
    Dense(256, activation = "relu"),
    BatchNormalization(),
    Dropout(0.25),
Dense(10, activation = "softmax")
])
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
hst = model.fit(train_images,train_labels,epochs=50,validation_data = (test_images,test_labels),batch_size=32)

Модель CNN, которую я обучил, имела точность 99,86. Поскольку они дают нам 3 попытки координат, я подумал об использовании функции softmax, которую я получаю после каждого предсказания изображения. Итак, я создал 2D-массив 20x20, который содержит значения softmax по индексу фактической цифры. Затем отправьте координаты тех, у кого меньше 3 значений.

Я использовал библиотеку pwntools в Python для трансляции данных netcat.

Окончательный код:

from pwn import *
import base64
from PIL import Image
import zlib
import numpy as np
import tensorflow as tf
from tensorflow import keras
import matplotlib.pyplot as plt
from statistics import mode
model = keras.models.load_model('cnn_mnist_model.h5')#loading the pretrained model
conn = remote('34.x.x.x',7777)
data = conn.recv()

Затем решите вопрос о Captcha в цикле, пока не появится флаг.

while True:  
    conn.recvuntil('b\'', drop=True)
    image_data = conn.recvline()
    image_data = image_data[:-2]
    image_data = image_data.decode('utf-8')
    image_file = zlib.decompress(base64.b64decode(image_data))
    f = open('image.jpeg','wb')
    f.write(image_file)
    f.close()
    image = Image.open('image.jpeg')
    imarray = np.asarray(image.convert('L')).reshape(560,560)
    imarray = imarray/255.0
    digit = mode([int(np.argmax(model.predict(imarray[:28,:28].reshape(1,28,28,1)),axis=1)),
                 int(np.argmax(model.predict(imarray[28:2*28,:28].reshape(1,28,28,1)),axis=1)),
                 int(np.argmax(model.predict(imarray[2*28:3*28,:28].reshape(1,28,28,1)),axis=1))])
    print(digit)
    x1=0
    y1=0
    x2=0
    y2=0
    x3=0
    y3=0
    ans = np.ones(shape=(20,20,1))
    for i in range(20):
        for j in range(20):
            imagdat = imarray[i*28:(i+1)*28,j*28:(j+1)*28]
            imagdat = imagdat.reshape(1,28,28,1)
            #preds = int(np.argmax(model.predict(imagdat),axis=1))
            ans[i,j,0]=model.predict(imagdat)[0,digit]
    y1 = np.argmin(ans)%20
    x1 = np.argmin(ans)//20
    ans[x1,y1,0]=1
    y2 = np.argmin(ans)%20
    x2 = np.argmin(ans)//20
    ans[x2,y2,0]=1
    y3 = np.argmin(ans)%20
    x3 = np.argmin(ans)//20
    ans[x3,y3,0]=1
    sendmsg = str((x1,y1,x2,y2,x3,y3))
    print(sendmsg)
    conn.sendline(sendmsg)
    flag = conn.recvline()
    if flag != b'Correct! onto the next one then\n':
        print(flag)
        break

После 100 капч и вуаля! флаг.

inctf{1_D4Y_R0b0t5_w1ll_rul3_th3_w0rld_4_5UR3!!}