Sigma Technology
4 minutos de lectura
Se nos proporciona una página web en la que podemos modificar el color de cinco píxeles de una imagen de un perro. Podemos elegir la posición de los píxeles (x
, y
) y el color (valor RGB) y la imagen tiene 32x32 píxeles:
El robot clasifica la imagen como uno de estos objetos:
airplane
automobile
bird
cat
deer
dog
frog
horse
ship
truck
La clasificación se realiza mediante un algoritmo de Machine Learning (usando tensorflow
). También se nos proporciona el modelo (sigmanet.h5
) y el código en Python para cargar el modelo y clasificar imagenes (model.py
).
Entendiendo el ataque
El objetivo es confundir a la Inteligencia Artificial del robot de manera que clasifique la imagen del perro como otra cosa después de alterar los píxeles. Esto es una especie de Adversarial Machine Learning, que es solo añadir ruido a la imagen para causar un error en la clasificación del modelo:
En primer lugar, podríamos comenzar por abrir un Jupyter Notebook para trabajar con el modelo. Esta vez estaré usando Google Colab. Importé el código de model.py
como una celda y cargué el modelo y la imagen del perro:
import imageio
import numpy as np
from matplotlib.pyplot import imshow
img = imageio.imread('dog.png', pilmode='RGB')
dog = np.array(img)
Prueba de concepto
Jugando un poco con los píxeles, dibujando cruces en rojo y blanco, conseguimos que el modelo clasifique las imágenes como horse
y cat
:
Sin embargo, si probamos estos valores en la página web, no vemos la flag (aunque la IA clasifica mal la imagen):
Luego empecé a pensar que tendríamos que confundir a la IA para que se equivoque con todas las clases disponibles, y me puse a trabajar en frog
de manera manual.
Método manual
El proceso fue probar posiciones y colores aleatorios. Luego, fui cambiando un valor arriba y abajo y me quedaba con aquel que aumentaba la confianza (que es el valor en la posición correspondiente de la lista). Cuando ese valor es el máximo para todas las confianzas, entonces el modelo dirá que la imagen es frog
. Finalmente lo logré, después de muchas pruebas:
Pero la página web tampoco mostró la flag. Por tanto, pensé que lo que teníamos que hacer era que el robot clasificara la imagen como un objeto que no fuera un animal. Es decir, uno de estos:
airplane
automobile
ship
truck
La estrategia manual requiere mucho tiempo y no es eficiente. Por tanto, podemos diseñar algún tipo de algoritmo que haga algo similar, pero automáticamente.
Método automático
La idea es coger una lista de pruebas, y generar nuevas pruebas basándonos en las previas (modificando aleatoriamente un único valor para posición o color). Luego, las clasificamos con el modelo y las ordenamos por confianza en los cuatro objetos que necesitamos. Finalmente, cogemos solamente los mejores y seguimos a la siguiente iteración hasta que consigamos que la confianza sea la máxima de la lista.
Este es el código en Python que implementa el algoritmo:
from random import randint as ri
NUM_WINNERS = 4
MAX_DISTANCE = 10
MAX_COLOR = 4
NUM_FIXES = 5
def generate(x, y, r, g, b):
n = ri(0, 99)
if 0 <= n < 10: x = max( 0, x - ri(1, MAX_DISTANCE))
if 10 <= n < 20: x = min( 31, x + ri(1, MAX_DISTANCE))
if 20 <= n < 30: y = max( 0, y - ri(1, MAX_DISTANCE))
if 30 <= n < 40: y = min( 31, y + ri(1, MAX_DISTANCE))
if 40 <= n < 50: r = max( 0, r - ri(1, MAX_COLOR))
if 50 <= n < 60: r = min(255, r + ri(1, MAX_COLOR))
if 60 <= n < 70: g = max( 0, g - ri(1, MAX_COLOR))
if 70 <= n < 80: g = min(255, g + ri(1, MAX_COLOR))
if 80 <= n < 90: b = max( 0, b - ri(1, MAX_COLOR))
if 90 <= n < 100: b = min(255, b + ri(1, MAX_COLOR))
return x, y, r, g, b
def rand_pixel():
return ri(0, 31), ri(0, 31), ri(0, 255), ri(0, 255), ri(0, 255)
attempts = [[rand_pixel() for __ in range(5)] for _ in range(NUM_WINNERS)]
done = False
while not done:
new_attempts = attempts.copy()
for i, attempt in enumerate(attempts):
for _ in range(50):
new_attempts.append([generate(*attempt[i]) for i in range(NUM_FIXES)])
max_confs = []
for i, attempt in enumerate(new_attempts):
new_img = dog.copy()
for x, y, r, g, b in attempt:
new_img[x, y] = [r, g, b]
pred, conf = sigmanet.predict_one(new_img)
max_conf = max(conf[0], conf[1], conf[8], conf[9])
max_confs.append((i, max_conf))
if pred in {'airplane', 'automobile', 'ship', 'truck'}:
print(pred, attempt)
done = True
break
max_confs.sort(key=lambda x: x[1], reverse=True)
indices = map(lambda x: x[0], max_confs[:NUM_WINNERS])
attempts = [new_attempts[i] for i in indices]
print(max_confs[:NUM_WINNERS])
El algoritmo siempre aumentará la confianza para los valores deseados (o al menos, la mantendrá). Después de un tiempo, obtenemos este resultado:
Hay unos valore que clasifican la imagen como airplane
. Podemos probarlo localmente:
Flag
Estupendo. Finalmente, si probamos estos valores en la página web, conseguiremos la flag:
HTB{0ne_tw0_thr33_f0ur_f1v3_p1xel_attack}