(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.
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 :
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}