Aller au contenu

Stéganographie APNG : données cachées dans les frames d'une image animée

Le format APNG (Animated PNG) étend PNG pour stocker des animations. Sa particularité : l’image affichée par une visionneuse PNG classique (non animée) peut être entièrement différente du contenu animé. Ce mécanisme permet d’embarquer des données dans les frames d’animation sans qu’elles soient visibles à l’ouverture ordinaire du fichier.

Un fichier APNG est un PNG valide avec des chunks supplémentaires. Les visionneuses qui ne comprennent pas l’APNG ignorent ces chunks et affichent uniquement l’image principale stockée dans les chunks IDAT standard.

PNG Signature (8 octets)
IHDR — dimensions du canvas, profondeur de couleur
acTL — Animation Control : nombre de frames, nombre de boucles
fcTL — Frame Control : dimensions, position, timing, mode de fusion (par frame)
IDAT — image par défaut (affichée par les visionneuses PNG classiques)
fdAT — Frame Data : données de chaque frame animée (hors première si IDAT la couvre)
...
IEND — fin du fichier

Le chunk acTL signale qu’un PNG est en réalité un APNG. Il contient deux champs :

Champ Taille Description
num_frames 4 octets Nombre total de frames de l’animation
num_plays 4 octets Nombre de boucles (0 = boucle infinie)

Sa présence est le marqueur distinguant un APNG d’un PNG ordinaire.

Chaque frame possède un chunk fcTL qui précède ses données. Il définit :

Champ Taille Description
sequence_number 4 octets Numéro de séquence (ordre des chunks fdAT et fcTL)
width / height 4 octets chacun Dimensions de la frame
x_offset / y_offset 4 octets chacun Position dans le canvas
delay_num / delay_den 2 octets chacun Durée d’affichage (fraction)
dispose_op 1 octet Comportement après affichage (effacer, conserver, restaurer)
blend_op 1 octet Mode de fusion avec le canvas existant

L’image par défaut et la dissociation des contenus

Section intitulée « L’image par défaut et la dissociation des contenus »

La spec APNG autorise deux cas :

Cas 1 — le fcTL de la première frame précède les IDAT : l’image principale fait partie de l’animation. PNG et APNG affichent le même contenu.

Cas 2 — les IDAT précèdent le premier fcTL : l’image principale est indépendante de l’animation. Une visionneuse PNG classique affiche le contenu des IDAT. Une visionneuse APNG affiche l’animation stockée dans les fdAT, potentiellement complètement différente.

C’est ce second cas qui rend le format intéressant en stéganographie.

Fenêtre de terminal
exiftool fichier.apng

Un APNG produit des champs supplémentaires absents d’un PNG ordinaire :

File Type : APNG
Animation Iterations : 0
Frame Count : 5

L’absence de Frame Count dans la sortie d’un fichier .apng indique soit un PNG mal nommé, soit une visionneuse qui ne parse pas les chunks animés.

Les chunks APNG ont des signatures reconnaissables en hexadécimal :

Fenêtre de terminal
hexdump -C fichier.apng | grep -E "acTL|fcTL|fdAT"

Signatures en hexadécimal :

61 63 54 4C → acTL (Animation Control)
66 63 54 4C → fcTL (Frame Control)
66 64 41 54 → fdAT (Frame Data)

La présence de acTL confirme un fichier APNG. L’absence de fcTL avant les IDAT indique que l’image par défaut est distincte des frames animées.

apngdis décompose un APNG en frames individuelles et produit un fichier texte avec les paramètres de chaque frame.

Fenêtre de terminal
sudo apt install apngdis
Fenêtre de terminal
apngdis fichier.apng

apngdis génère :

  • fichier_N.png pour chaque frame (numérotées à partir de 1)
  • fichier.txt contenant le timing et les paramètres de chaque frame

Exemple de sortie du fichier texte généré :

num_frames=5
num_plays=0
frame1=fichier_1.png delay=100/1000 x=0 y=0 w=800 h=600 blend=None dispose=None
frame2=fichier_2.png delay=100/1000 x=0 y=0 w=800 h=600 blend=None dispose=None
...

Une fois les frames extraites, les inspecter comme n’importe quelle image PNG :

Fenêtre de terminal
# Vérifier les métadonnées de chaque frame
exiftool fichier_*.png
# Chercher des données textuelles dans les frames
strings fichier_*.png | grep -v "^.\{1\}$"
# Comparer les tailles des frames (une frame anormalement lourde mérite attention)
ls -lh fichier_*.png
# Vérifier si une frame contient des données après son IEND
exiftool fichier_2.png | grep "Warning"

Chercher du texte encodé dans les noms de chunks personnalisés

Section intitulée « Chercher du texte encodé dans les noms de chunks personnalisés »

Le format PNG autorise des chunks privés (dont le nom commence par une lettre minuscule). Un APNG peut embarquer des chunks non standard portant des données arbitraires :

Fenêtre de terminal
python3 - <<'EOF'
import struct
with open("fichier.apng", "rb") as f:
data = f.read()
i = 8
while i < len(data):
length = struct.unpack(">I", data[i:i+4])[0]
chunk_type = data[i+4:i+8].decode('ascii', errors='replace')
chunk_data = data[i+8:i+8+length]
# Les chunks dont le nom commence en minuscule sont privés/non standard
if chunk_type[0].islower():
print(f"Chunk privé : {chunk_type} ({length} octets)")
print(f" Contenu : {chunk_data[:64]}")
i += 12 + length
EOF

Face à un fichier APNG suspect :

1. Vérifier le type de fichier
exiftool fichier.apng
→ Frame Count présent ? Nombre de frames > ce que l'on attend ?
2. Inspecter les chunks bruts
hexdump -C fichier.apng | grep -E "acTL|fcTL|fdAT"
→ Les IDAT précèdent-ils le premier fcTL ?
3. Décomposer les frames
apngdis fichier.apng
→ Combien de frames ? Contenu différent de l'image affichée ?
4. Comparer l'image par défaut et la première frame
→ Une différence indique un contenu intentionnellement dissocié
5. Inspecter chaque frame individuellement
exiftool fichier_*.png
strings fichier_*.png
→ Données cachées dans les métadonnées ou en texte brut ?
6. Chercher les chunks non standard
→ Chunks dont le nom commence en minuscule
→ Données binaires inhabituelles
7. Analyser les paramètres de timing
→ Valeurs de delay anormales encodant potentiellement des données

Un APNG affiché dans une visionneuse classique ne montre que l’image principale des chunks IDAT. Les frames animées stockées dans les fdAT restent invisibles sans un outil dédié. apngdis extrait l’ensemble des frames en une commande. Comparer l’image par défaut et les frames animées est la première vérification à effectuer sur tout APNG suspect.