Skip to main content

(Moyen) SOS Raid 1/2

Description

Nous avon ici affaire à un challenge de reconstruction de RAID.

Nous possédons 2 disques ainsi qu'un manuel qui nous donne des informations sur la technologie:

Type de RAID : 5
Taille des blocs : 1 octet

https://fr.wikipedia.org/wiki/RAID_(informatique)#RAID_5_:_volume_agr%C3%A9g%C3%A9_par_bandes_%C3%A0_parit%C3%A9_r%C3%A9partie

Analyse

Commencons par découvrir le RAID 5.

Le RAID 5 est une technologie de stockage des données qui permet de perdre un disque sans avoir de perte de données.

Le principe est simple, on divise les disques en blocs de taille égales puis on remplies chaque ligne de blocs avec les donnés et on garde un bloc vide pour y stocker une parité. Cette parité correspond à un XOR entre les autres blocs de la même ligne.


image-1655619911119.png

A chaque ligne on décalle le bit de parité d'un cran sur la gauche pour éviter que tout les blocs de partiés soit sur le même disque.

Lors de la perte d'un disque, il suffit de le reconstruire en réalisant un XOR entre tout autres disques ligne après ligne.

Solution

Le but ici est donc de retrouver les données qui étaient stockées sur 3 disques.

Pour cela on va commencer par stocker les deux premiers disques en mémoire:

from functools import partial

d1 = []
with open("./disk0.img", 'rb') as f:
    for block in iter(partial(f.read, blocksize), ''):
        if block == b'':
            break
        d1.append(block)
d2 = []
with open("./disk1.img", 'rb') as f:
    for block in iter(partial(f.read, blocksize), ''):
        if block == b'':
            break
        d2.append(block)

Maintenant le but n'est pas de reconstruire le 3ème disque, mais de récupérer les informations qui sont stockés sur ces 3 disques.

Pour cela il faut parcourir chaque bloc et stocké l'information si le bloc n'est pas un bloc de parité :

data = b""
for j in range(len(d1) * 3):
    block1, block2 = d1[j // 3], d2[j // 3]
    if (2 - ((j // 3) % 3)) == j % 3:
        continue
    if (j % 3) == 0:
        data += block1
    elif (j % 3) == 1:
        data += block2
    else:
        data += bytes([_a ^ _b for _a, _b in zip(block1, block2)])

On stock ensuite les informations dans un nouveau disque :

file = open("./disk2.img", 'wb')
file.write(data)

Le code complet :

from functools import partial
blocksize = 1
d1 = []
with open("./disk0.img", 'rb') as f:
    for block in iter(partial(f.read, blocksize), ''):
        if block == b'':
            break
        d1.append(block)
d2 = []
with open("./disk1.img", 'rb') as f:
    for block in iter(partial(f.read, blocksize), ''):
        if block == b'':
            break
        d2.append(block)
data = b""
for j in range(len(d1) * 3):
    block1, block2 = d1[j // 3], d2[j // 3]
    if (2 - ((j // 3) % 3)) == j % 3:
        continue
    if (j % 3) == 0:
        data += block1
    elif (j % 3) == 1:
        data += block2
    else:
        data += bytes([_a ^ _b for _a, _b in zip(block1, block2)])
file = open("./disk2.img", 'wb')
file.write(data)

On obtient ainsi notre nouveau disque qui semble être une archive ZIP :

image-1655625365809.png

On dézippe l'archive jar -xvf disk2.img et on obtient deux nouveaux fichier, notre flag ainsi qu'une image pour la seconde partie du chall.

flag : 404CTF{RAID_5_3st_p4s_tr3s_c0mpl1qu3_1abe46685ecf}