Un fichier audio (.wav) contenant une séquence de tons DTMF (Dual-Tone Multi-Frequency), le type de signaux utilisés par les téléphones à touches, est mis à disposition.
Phase d’étude
L’observation du fichier dans Audacity a révélé une série claire de signaux DTMF, caractérisés par :
- Des impulsions uniformes et distinctes (environ 50ms chacune)
- Des séparations nettes entre les signaux (entre 30ms et 40ms)
- Une amplitude constante pendant chaque ton
- Une absence de chevauchement entre les signaux
Une rapide analyse de spectre montre des piques de fréquences qui se détachent nettement et qui nous permettront rapidement de retrouver les touches pour chaque partie du signal.
Étape 1 : Extraction de la séquence DTMF
Je commence par développer un script Python utilisant scipy
pour décoder les tons DTMF avec les caractéristiques suivantes :
- Utilisation de la transformée de Fourier (spectrogramme) pour détecter les fréquences
- Matrices de fréquences DTMF standard (697-1633 Hz)
- Paramètres optimisés :
- Fenêtre d’analyse de 30ms
- Tolérance de fréquence de 30 Hz (même si les signaux semblent de bonne qualité)
- Seuil de puissance adaptatif
- Vérification de la durée minimale des tons
Script de détection DTMF_detect.py
J’exporte ensuite depuis Audacity un nouveau fichier wav, débarrassé de la conversation du début, en 8000 Hz Mono et je lance la détection.
La séquence extraite commence par 1#8B0800000000000003*C5B
, ce qui est un premier indice crucial.
Étape 2 : Étude de la séquence extraite
Le DTMF compte, en standard, 16 touches, nous pouvons donc aisément le rapprocher de l’hexadécimal en remplaçant les touches “*” et “#”. L’observation de la séquence a révélé un pattern intéressant :
- Début par
1#8B08
- Utilisation exclusive des caractères 0-9, A-D, * et # En remplaçant :
- ‘*’ par ‘E’
- ‘#’ par ‘F’
On obtient 1F8B08...
qui est le “magic number” caractéristique du format GZIP !
Etape 3 : Décodage
Je développe alors un second script pour :
- Remplacer * et # par leurs équivalents hexadécimaux (E et F)
- Convertir la chaîne hexadécimale en bytes
- Décompresser le GZIP résultant
import gzip
from pathlib import Path
def read_input_file(filename):
try:
with open(filename, 'r') as f:
return f.read().strip()
except Exception as e:
print(f"Error reading file: {e}")
return None
def decode_gzip_data(encoded_data):
# Remplacement de caractères pour obtenir de l'hexadécimal
hex_data = encoded_data.replace('*', 'E').replace('#', 'F')
# Conversion de l'hexadécimal en bytes
try:
binary_data = bytes.fromhex(hex_data)
except ValueError as e:
print(f"Error converting hex: {e}")
return None
# Sauvegarde en GZip
with open('output.gz', 'wb') as f:
f.write(binary_data)
print("Saved compressed data to 'output.gz'")
# Tentative de décompression
try:
decompressed = gzip.decompress(binary_data)
return decompressed
except Exception as e:
print(f"Error decompressing: {e}")
return None
# Lecture du fichier de DTMF extraits
encoded = read_input_file('dtmf_output.txt')
if encoded:
# Décoder ...
result = decode_gzip_data(encoded)
if result:
print("Ooooooh yeah !")
else:
print("Failed to read input file")
Le GZIP contient un fichier binaire linux et un README_PLEASE.TXT qui nous invite à lancer le programme afin d’obtenir le flag … Fin de l’épreuve :) !