TamuCTF 2017 - Stéganographie - Musical Bits

Le Tamu CTF est challenge CTF Jeopardy. Cet article explique la résolution du challenge de stéganographie “Musical Bits”. Quelques heures de prise de tête ont été nécessaires, accompagné de @Iptior, afin de résoudre ce challenge. C’est parti !

Découverte

Le challenge commence avec un texte disant qu’un hacker dispose d’un son étrange… Rien à tirer de ceci. On télécharge donc le fichier donné et on utilise la commande file afin de récupérer des informations sur ce dernier.

$ file magical_bits
magical_bits: RIFF (little-endian) data, WAVE audio, Microsoft PCM, 16 bit, mono 44100 Hz

On se retrouve donc avec un fichier audio. On l’ouvre tout d’abord avec un lecteur audio classique, afin de voir si des choses sont audibles. Manque de chance, rien de bien intéressant d’un point de vue élocution, on entend de petits sons pendant environ 20 minutes. Ces sons sont de petits “bip” de deux sortes. Aidés du titre du challenge, on pense à une représentation binaire de quelquechose.

On cherche ensuite à savoir de quoi est composé le son, donc on ouvre le fichier avec Audacity. Après avoir pas mal zoomé et passé la partie “Introduction” du son, on remarque que des séquences se répètent de nombreuses fois. En voici un extrait.

musical_bits_1

On remarque la présence de paquets de 8 signaux, et on pense directement à des octets. En effet, on voit 3 signaux différents. 2 quasi-identiques, et un troisième, plus long. On suppose la signification suivante :

  • Le plus gros signal correspond à un bit "1"
  • Le petit signal correspond à un "0"
  • Le signal plus long correspond à la séparation de chaque octet
Suivant cette logique, on peut tester notre raisonnement en commençant à la main... Si on prend les 4 premiers paquets de signaux, on obtient la chaine binaire suivante :

11111111 11011000 11111111 1110000

Puis on la convertit au format hexadécimal : FF 4D FF E0

Hmmmm… Après quelques recherches, on se rend compte qu’il s’agit en réalité d’un début d’en-tête JPEG ! On peut maintenant être sûrs d’avoir la bonne méthode et qu’il faut extraire ces données pour former une image JPEG.

Extraction - Première tentative

J’ai tout d’abord cherché sur Internet des outils existant et permettant d’extraire automatiquement ce type de données ou un moyen de l’extraire avec Python. Mais à court d’idées, j’ai finalement décidé d’exporter le son complet en format “raw”.

Après avoir supprimé l’introduction, nous disposons d’un fichier brut exploitable. À l’aide de hexedit, on peut afficher le contenu du fichier.

musical_bits_2

Si on défile un peu, on peut remarquer que les données ressemblent souvent à celles de l’image ci dessus. De plus, on distingue deux formes différentes.

musical_bits_3

Cela pourrait être nos “0” et “1” trouvés précédemment. Encore une fois, on défile manuellement afin de voir si les formes correspondent. Et… Oui ! On retrouve nos données. On dispose maintenant d’un moyen d’extraire nos bits.

Il faut cependant trouver une séquence modèle (un patron) afin de savoir s’il s’agit d’un 1 ou d’un 0.

Nous avons essayé un bon nombre de patrons et extrait beaucoup de bits, mais nous avions toujours un problème… Plusieurs mauvais bits. Étant donné que ce n’était pas la bonne solution, les scripts ne seront pas montrés dans cette partie.

Extraction - Seconde tentative

Et c’est le moment où Iptior rentre en jeu… :)

Je suis arrivé à court d’idées et après avoir envoyé mes scripts Python aux membres de l’équipe, il est revenu avec une idée. L’idée d’une séquence répétitive semblait bonne, mais abordée de la mauvaise manière.

Nous avons alors fait quelquechose que nous avions oublié, mais qui aurait pû nous faire gagner beaucoup de temps. Nous nous sommes d’abord servi de la commande strings. Voici un échantillon du résultat.

$ strings magical_bits
RIFF
WAVEfmt
data

...

':*p,..v/L0
,}++*
'a&L%Y$
!u!>!
%2%v$
#=$r$b$
"2!? H
< B \ \ b - ':*p,..v/L0 ,}++* 'a&L%Y$ !u!>!
%2%v$
#=$r$b$
"2!? H
< B \ \ b - ':*p,..v/L0 ,}++* 'a&L%Y$ !u!>!
%2%v$
#=$r$b$
"2!? H
< B \ \ b - ':*p,..v/L0 ,}++* 'a&L%Y$ !u!>!
%2%v$
#=$r$b$
"2!? H
< B \ \ b - ':*p,..v/L0 ,}++* 'a&L%Y$ !u!>!
%2%v$
#=$r$b$
"2!? H
< B \ \ b - ':*p,..v/L0 ,}++* 'a&L%Y$ !u!>!
%2%v$
#=$r$b$
"2!? H
< B \ \ b - ':*p,..v/L0 ,}++* 'a&L%Y$ !u!>!
%2%v$
#=$r$b$
"2!? H
< B \ \ b - ':*p,..v/L0 ,}++* 'a&L%Y$ !u!>!
%2%v$
#=$r$b$
"2!? H
< B \ \ b - 1 A A c ## ` . ^ . W ( O s D b q c , 5 H C ... ':*p,..v/L0 ,}++* 'a&L%Y$ !u!>!
%2%v$
#=$r$b$
"2!? H
< B
\ \
b -
* V b J
x t
s 0
!F s
6
Y !
* V b J
x t
s 0
!F s
6
Y !
* V b J
x t
s 0
!F s
6
Y !
* V b J
x t
s 0
!F s
6
Y !
1 A
A c

...

Et là encore… On retrouve souvent les mêmes caractères… Peut être peut on en sortir une correspondance ?

Après quelques recherches et analyses, une correspondance semble avoir été trouvée !

  • "1" peut être extrait lorsque l'on trouve les caractères "<" puis "B"
  • "0" peut être extrait avec la séquence "!", "F" puis "s"
À partir de là, il est possible d'écrire un petit script python afin d'extraire toutes les données.
#!/usr/bin/env python

with open("essai.txt", "r") as fichier:
musi=fichier.read()

musi = musi.split('\n')
outBin=""
for textM in musi:
if("!" in textM and "s" in textM and "F" in textM):
outBin+="0"
elif("B" in textM and "<" in textM):
outBin+="1"

print(outBin[1:])

Convertion

À l’heure actuelle, on dispose d’une chaine binaire que nous a donné le script précédent. Mais, ayant vu un en-tête JPEG, on pense qu’il faut trouver une image. Il faut donc convertir le tout !

Pour faire cela, nous utilisons un autre script :

#!/usr/bin/env python

with open("binary.txt", "r") as fichier:
binaire=fichier.read()
outFile=""

i=1
outHex=""

while i <= (len(binaire)-8):
number = (int(binaire[i]+binaire[i+1]+binaire[i+2]+binaire[i+3]+binaire[i+4]+binaire[i+5]+binaire[i+6]+binaire[i+7],2)) #on peut je pense faire number = int(binaire[i:i+8],2)
outFile += chr((number))
i = i+8
outHex += str(hex(number)[2:])

print(outHex)

Récupération du flag

On essaie ensuite d’ouvrir l’image générée avec le script.

musical_bits_4 class=

Voilà le flag !! :)

ECW 2017 - Web - Hall of Fame