Stéganographie Whitespace : programme caché dans les espaces d'un fichier texte
Le langage Whitespace, créé par Edwin Brady et Chris Morris en 2003, ignore tous les caractères imprimables. Seuls trois caractères ont une signification : l’espace (0x20), la tabulation (0x09) et le saut de ligne (0x0A). Tout le reste est traité comme un commentaire.
Cette propriété permet d’embarquer un programme exécutable dans n’importe quel fichier texte. La prose visible masque un programme entièrement codé dans les espaces et tabulations intercalés entre les caractères lisibles.
Structure du langage
Section intitulée « Structure du langage »Les trois caractères
Section intitulée « Les trois caractères »| Caractère | Notation | Valeur binaire |
|---|---|---|
| Espace | [S] |
0 |
| Tabulation | [T] |
1 |
| Saut de ligne | [L] |
séparateur / terminateur |
Instructions
Section intitulée « Instructions »Les instructions sont regroupées par préfixe (IMP — Instruction Modification Parameter) :
| Préfixe | Catégorie |
|---|---|
[S] |
Manipulation de pile |
[T][S] |
Arithmétique |
[T][T] |
Accès au tas |
[L] |
Contrôle de flux |
[T][L] |
Entrées / Sorties |
Exemple, empiler la valeur 65 (A en ASCII) et l’afficher :
[S][S] → push[S] → signe positif[T][S][S][S][S][S][T] → 1000001 = 65 en binaire[L] → fin du nombre[T][L][S][S] → output char[L][L][L] → exitEncodage des nombres
Section intitulée « Encodage des nombres »Les nombres sont encodés en binaire avec un bit de signe en tête :
[S]= positif[T]= négatif
Les bits suivants : [S] = 0, [T] = 1, terminés par [L].
Détecter un programme Whitespace
Section intitulée « Détecter un programme Whitespace »Repérer des patterns inhabituels
Section intitulée « Repérer des patterns inhabituels »Un fichier texte ordinaire contient des tabulations et des espaces en quantités prévisibles. Un fichier portant un programme Whitespace présente souvent :
- Des suites de tabulations là où aucune indentation n’est attendue
- Des espaces en fin de ligne en nombre inhabituel
- Un ratio espaces/tabulations qui ne correspond pas au contenu visible
# Afficher les caractères spéciaux (^ = tab, $ = fin de ligne)cat -A fichier.txt
# Compter les tabulationscat fichier.txt | tr -cd '\t' | wc -c
# Compter les espacescat fichier.txt | tr -cd ' ' | wc -c
# Afficher les octets brutshexdump -C fichier.txt | head -30Extraire les seuls caractères Whitespace
Section intitulée « Extraire les seuls caractères Whitespace »Pour isoler le programme de son enveloppe textuelle :
# Ne garder que les espaces, tabulations et sauts de lignecat fichier.txt | tr -cd ' \t\n' > programme.wsLe fichier programme.ws ne contient plus que les caractères significatifs pour l’interpréteur.
Visualiser les caractères cachés
Section intitulée « Visualiser les caractères cachés »with open("fichier.txt", "r") as f: contenu = f.read()
for i, char in enumerate(contenu): if char == ' ': print(f"pos {i:4d} : ESPACE") elif char == '\t': print(f"pos {i:4d} : TAB") elif char == '\n': print(f"pos {i:4d} : NEWLINE")Exécuter un programme Whitespace
Section intitulée « Exécuter un programme Whitespace »En ligne
Section intitulée « En ligne »Plusieurs interpréteurs en ligne acceptent les fichiers .ws :
https://www.tutorialspoint.com/execute_whitespace_online.phpColler le contenu extrait (ou uploader le fichier .ws) et lancer l’exécution.
En local avec Python
Section intitulée « En local avec Python »pip install whitespace-pythonwhitespace fichier.wsOu utiliser un interpréteur Python minimal :
import sys
def run(source): # Filtrer uniquement les caractères Whitespace prog = [c for c in source if c in (' ', '\t', '\n')] stack = [] heap = {} output = [] i = 0
def read_num(): nonlocal i sign = 1 if prog[i] == ' ' else -1 i += 1 bits = [] while prog[i] != '\n': bits.append('0' if prog[i] == ' ' else '1') i += 1 i += 1 return sign * int(''.join(bits), 2) if bits else 0
while i < len(prog): c = prog[i]; i += 1 if c == ' ': nc = prog[i]; i += 1 if nc == ' ': stack.append(read_num()) elif nc == '\n': nnc = prog[i]; i += 1 if nnc == ' ': stack.append(stack[-1]) elif nnc == '\t': stack[-1], stack[-2] = stack[-2], stack[-1] elif nnc == '\n': stack.pop() elif c == '\t': nc = prog[i]; i += 1 if nc == '\n': nnc = prog[i]; i += 1 if nnc == ' ': nnnnc = prog[i]; i += 1 if nnnnc == ' ': output.append(chr(stack.pop())) elif nnnnc == '\t': output.append(str(stack.pop())) elif c == '\n': nc = prog[i]; i += 1 if nc == '\n': nnc = prog[i]; i += 1 if nnc == '\n': break
return ''.join(output)
with open(sys.argv[1], 'r') as f: print(run(f.read()))python3 whitespace_interp.py fichier.txtWorkflow en pratique
Section intitulée « Workflow en pratique »1. Inspecter le fichier cat -A fichier.txt → tabulations ou espaces en fin de ligne inhabituels ?
2. Quantifier les caractères Whitespace cat fichier.txt | tr -cd '\t' | wc -c cat fichier.txt | tr -cd ' ' | wc -c → un nombre élevé par rapport à la longueur du texte visible évoque un programme embarqué
3. Extraire le programme cat fichier.txt | tr -cd ' \t\n' > programme.ws
4. Exécuter → interpréteur en ligne ou whitespace-python
5. Analyser la sortie → texte en clair → flag ou message → texte chiffré → identifier le chiffrementÀ retenir
Section intitulée « À retenir »Un programme Whitespace est invisible dans un éditeur de texte standard et dans tout affichage terminal normal. cat -A révèle les tabulations et les fins de ligne inhabituelles. Extraire les seuls caractères [S], [T] et [L] suffit à isoler le programme, que n’importe quel interpréteur Whitespace peut ensuite exécuter.