mercredi 25 mai 2011

Solution partielle du challenge SSTIC 2011 -- Si loin, si proche.

Le SSTIC propose tous les ans un challenge à résoudre. Celui de cette année était particulièrement intéressant. Commençons déjà par féliciter les gagnants, bravo à eux. Cette année, le challenge consistait à analyser une vidéo afin de trouver une adresse mail. Le challenge finit le 25 mai, je propose ma solution incomplète, blogger devrait le publier à 23h59 ce 25 mai.

0/ Slow start
Avant de démarrer, un petit brainstorming et des recherches. Qui dit vidéo dit immédiatement format de vidéo (conteneur/contenu), DRM, stéganographie et métadonnées. Une recherche google avec en mot clés les noms des auteurs et ces technologies (et VLC/ video etc..) ne montre pas de papiers antérieurs pouvant servir. Sous linux, la commande file indique que le fichier en question est une video type mp4 (ce qui semble confirmer le DRM):
ISO Media, MPEG v4 system, version 2
Une lecture via VLC de ce fichier ne donne pas la solution, un strings challenge | grep @sstic.org non plus :-)

1/ Etude préliminaire 
Le programme pour étudier les mp4 sous linux est gpac, ou MP4Box. Vérifions les métadonnées et le contenu
$ MP4Box -info challenge
* Movie Info *
        Timescale 90000 - Duration 00:00:17.298
        Fragmented File no - 3 track(s)
        File Brand mp42 - version 0
        Created: GMT Sun Mar 20 18:10:09 2011

File has no MPEG4 IOD/OD

Track # 1 Info - TrackID 1 - TimeScale 90000 - Duration 00:00:17.298
Media Info: Language "Undetermined" - Type "vide:mp4v" - 518 samples
MPEG-4 Config: Visual Stream - ObjectTypeIndication 0x20
MPEG-4 Visual Size 640 x 480 - Simple Profile @ Level 1
Pixel Aspect Ratio 1:1 - Indicated track size 640 x 480
Self-synchronized

Track # 2 Info - TrackID 2 - TimeScale 44100 - Duration 00:00:17.182
Media Info: Language "Undetermined" - Type "soun:mp4a" - 740 samples
MPEG-4 Config: Audio Stream - ObjectTypeIndication 0x40
MPEG-4 Audio AAC LC - 2 Channel(s) - SampleRate 44100
Synchronized on stream 1

Track # 3 Info - TrackID 3 - TimeScale 90000 - Duration 00:00:06.995
Media Info: Language "Undetermined" - Type "data:elf " - 128 samples
Unknown media type
        Vendor code "...." - Version 0 - revision 0

on a donc un container avec trois pistes: une video, une audio et une ... elf! (format de fichier exécutable linux).

2/ Etude de la piste trois
L'extraction du elf se fait très bien:
$ MP4Box -raw 3 challenge
Extracting '' Track (type 'data') - Compressor
$ ls
challenge  challenge_track3.elf\
(curieusement, le nom du track3 à un espace à la fin (?))
Etude rapide du fichier à l'aide de la commande file:
challenge_track3.elf : ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, not stripped
Shared Object: Il s'agit donc d'une bibliothèque partagée, reste à en trouver l'usage.

L'outil strings permet de connaître quelques infos intéressantes:
/home/jb/vlc-1.1.7/src/.libs
%s/sstic2011/secret1.dat
%s/sstic2011/secret2.dat
(...)

Le téléchargement, la compilation et l'installation de vlc-1.1.7 se déroule sans heurts. Après quelques recherches on se rend compte que la lib:
/usr/local/lib/vlc/plugins/demux/libmp4_plugin.so
ressemble grandement à la lib fournie par le challenge. On intervertit les deux libs, et on constate à l'aide d'un strace que:
$ mv challenge_track3.elf\  /usr/local/lib/vlc/plugins/demux/libmp4_plugin.so

$ strace -ff -o track cvlc --noaudio --novideo mp4.m4a
VLC media player 1.1.7 The Luggage (revision exported)
 (...)
$ grep secret track.121*
track.12118:open("/home/kevin/sstic2011/secret1.dat", O_RDONLY|O_LARGEFILE|O_CLOEXEC)                               = 4
track.12120:open("/home/kevin/sstic2011/secret1.dat", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 6

donc la lib tente d'ouvrir le fichier sstic2011/secret1.dat ce qui semble confirmer le bon emploi de la bibliothèque. Ceci dit, le secret2.dat n'apparait jamais dans le strace, cela doit dépendre du succès d'ouverture de secret1.dat.

En regardant un peu les différences entre les deux libs mp4 (l'officielle et celle du challenge) on découvre les fonctions suivantes:
encryption_keys
sstic_lame_derive_key
expected_plaintext
sstic_check_secret2
sstic_check_secret1
sstic_read_secret1
sstic_read_secret2

Le mot-clé sstic_lame_derive_key permettait de tomber sur une page de pastebin.com donnant le code source de cette fonction, mais apparemment, cette page a disparu de pastebin et du cache google (??).

Enfin, une lecture du code de VLC (le nom de certaines structures et commentaires sont priceless) et de différentes docs confirme bien que secret1 et secret2 sont effectivement des clés servant à déchiffrer les DRM. Ces secrets sont en dur dans le code de VLC, ayant été cassés (Quelques infos sur la mailing list VLC indique que dans le temps, VLC allait chercher ces secrets dans des fichiers dédiés):
/usr/src/vlc-1.1.7/modules/demux/mp4$ grep secret *
drms.c:                static const char p_secret[] = "tr1-th3n.y00_by3";
drms.c:                memcpy( p_drms->p_key, p_secret, 16 );
drms.c:    char p_secret1[] = "Tv!*";
drms.c:    static const char p_secret2[] = "____v8rhvsaAvOKM____FfUH%798=[;."
 (etc...)
on retrouve d 'ailleurs dans la lib SSTIC le tr1-th3n.y00_by3 et d'autres choses.

Nous nous retrouvons donc face à un problème de reverse de code et de crypto. Mais il est mention de deux fichiers, secret1 et secret2. Il semble évident que la résolution crypto donnera un des deux secrets. Il faut donc découvrir le second.

3/ If at first you don't succeed, try again
Il est temps de reprendre l'étude du fichier original après avoir bien travaillé la librairie.
un lecture hexa du fichier challenge dévoile une info intéressante en tout début de fichier:
Le mot "introduction.txt" n'est clairement présent par hasard :-)
Et en sautant le nombre d'octets appropriés, on tombe sur:
kevin@slackware:~$ dd if=challenge of=intro bs=1 count=64 skip=32
64+0 enregistrements lus
64+0 enregistrements écrits
64 octets (64 B) copiés, 0,00461791 s, 13,9 kB/s
kevin@slackware:~$ file intro
intro: gzip compressed data, was "introduction.txt", from FAT filesystem (MS-DOS, OS/2, NT), last modified: Thu Mar 17 14:01:52 2011, max compression
kevin@slackware:~$
Ce qui est plutôt prometteur. Néanmoins, son dégzippage pose problème:
kevin@slackware:~$ dd if=challenge of=intro.gz bs=1 count=128000 skip=32
128000+0 enregistrements lus
128000+0 enregistrements écrits
128000 octets (128 kB) copiés, 7,50659 s, 17,1 kB/s
kevin@slackware:~$ gunzip intro.gz
gzip: intro.gz: invalid compressed data--format violated
kevin@slackware:~$
Son extraction n'est pas immédiate et il devient nécessaire de se plonger dans de la doc sur le format de fichier mp4.

Un fichier mp4 est constitué d'atomes, suivant une structure simple:
4 octets indiquant une taille suivis de 4 octets ASCII donnant le nom de l'atom, suivi des données. Par exemple, pour le fichier challenge:
00 00 00 18 66 74 79 70  6d 70 34 32 00 00 00 00
6d 70 34 32 69 73 6f 6d  00 3f a1 9a 6d 64 61 74
taille: 18Hex donc 24 octets. type "ftyp". Données: mp42 00 00 00 00  mp42isom. Ce qui permet d'ailleurs à la commande file de reconnaitre le fichier.
L'atom suivant démarre à l'octet 24, fait une taille de  4170138 est du mdat, etc, etc..
On obtient donc une structure:
Atom ftyp @ 0 of size: 24, ends @ 24
Atom mdat @ 24 of size: 4170138, ends @ 4170162
Atom mdat @ 4170162 of size: 277450, ends @ 4447612
Atom mdat @ 4447612 of size: 178748, ends @ 4626360
Atom moov @ 4626360 of size: 12507, ends @ 4638867
   Atom mvhd @ 4626368 of size: 108, ends @ 4626476
   Atom trak @ 4626476 of size: 5982, ends @ 4632458
     Atom tkhd @ 4626484 of size: 92, ends @ 4626576
     Atom ssti @ 4626576 of size: 12, ends @ 4626588
     Atom mdia @ 4626588 of size: 5822, ends @ 4632410
       Atom mdhd @ 4626596 of size: 32, ends @ 4626628
       Atom hdlr @ 4626628 of size: 33, ends @ 4626661
       Atom minf @ 4626661 of size: 5749, ends @ 4632410
         Atom vmhd @ 4626669 of size: 20, ends @ 4626689
         Atom dinf @ 4626689 of size: 36, ends @ 4626725
           Atom dref @ 4626697 of size: 28, ends @ 4626725
         Atom stbl @ 4626725 of size: 5685, ends @ 4632410
           Atom stsd @ 4626733 of size: 181, ends @ 4626914
             Atom mp4v @ 4626749 of size: 165, ends @ 4626914
               Atom esds @ 4626835 of size: 79, ends @ 4626914
                 Atom stts @ 4626914 of size: 3248, ends @ 4630162
           Atom stsz @ 4630162 of size: 2092, ends @ 4632254
           Atom stsc @ 4632254 of size: 40, ends @ 4632294
           Atom stco @ 4632294 of size: 88, ends @ 4632382
           Atom stss @ 4632382 of size: 28, ends @ 4632410
     Atom edts @ 4632410 of size: 48, ends @ 4632458
       Atom elst @ 4632418 of size: 40, ends @ 4632458
   Atom trak @ 4632458 of size: 3492, ends @ 4635950
     Atom tkhd @ 4632466 of size: 92, ends @ 4632558
     Atom mdia @ 4632558 of size: 3372, ends @ 4635930
        Atom mdhd @ 4632566 of size: 32, ends @ 4632598
        Atom hdlr @ 4632598 of size: 33, ends @ 4632631
        Atom minf @ 4632631 of size: 3299, ends @ 4635930
          Atom smhd @ 4632639 of size: 16, ends @ 4632655
          Atom dinf @ 4632655 of size: 36, ends @ 4632691
            Atom dref @ 4632663 of size: 28, ends @ 4632691
          Atom stbl @ 4632691 of size: 3239, ends @ 4635930
             Atom stsd @ 4632699 of size: 103, ends @ 4632802
               Atom mp4a @ 4632715 of size: 87, ends @ 4632802
               Atom esds @ 4632751 of size: 51, ends @ 4632802
             Atom stts @ 4632802 of size: 24, ends @ 4632826
             Atom stsz @ 4632826 of size: 2980, ends @ 4635806
             Atom stsc @ 4635806 of size: 40, ends @ 4635846
             Atom stco @ 4635846 of size: 84, ends @ 4635930
     Atom udta @ 4635930 of size: 20, ends @ 4635950
       Atom name @ 4635938 of size: 12, ends @ 4635950
   Atom trak @ 4635950 of size: 2917, ends @ 4638867
     Atom tkhd @ 4635958 of size: 92, ends @ 4636050
     Atom mdia @ 4636050 of size: 2817, ends @ 4638867
       Atom mdhd @ 4636058 of size: 32, ends @ 4636090
       Atom hdlr @ 4636090 of size: 45, ends @ 4636135
       Atom minf @ 4636135 of size: 2732, ends @ 4638867
         Atom nmhd @ 4636143 of size: 12, ends @ 4636155
         Atom dinf @ 4636155 of size: 36, ends @ 4636191
           Atom dref @ 4636163 of size: 28, ends @ 4636191
         Atom stbl @ 4636191 of size: 2676, ends @ 4638867
           Atom stsd @ 4636199 of size: 32, ends @ 4636231
             Atom elf  @ 4636215 of size: 16, ends @ 4636231
               Atom stts @ 4636231 of size: 24, ends @ 4636255
           Atom stsc @ 4636255 of size: 1552, ends @ 4637807
           Atom stsz @ 4637807 of size: 532, ends @ 4638339
           Atom stco @ 4638339 of size: 528, ends @ 4638867

A propos: deux atomes ne font pas partie de la norme, le premier appelé "ssti" et le second nommé "elf ". On retrouve l'espace qui suit l'extension pour le .elf et le nom ssti pour un atome ressemble trop à SSTIC pour n'être qu'une coïncidence :-) [Néanmoins, la valeur de l'atom ssti vaut 00 00 00 00 ce qui n'avance à pas grand chose... secret1.dat fait lui aussi 4 octets dans le code source de VLC, mais quatre octets à 0 hexa n'est apparemment pas une bonne valeur pour secret1.dat.]
L'extraction de la piste 1(video) indique qu'elle est plus petite que le premier atom mdat. Nous avons donc dans le premier atom mdat (media data) la piste video, et des octets "en plus". Ce qui signifie que de manière dispersée dans dans la partie mdat du fichier mp4, il y a des octets superfétatoires. J'ai donc écrit un outil me permettant d'en extraire le diff, et sans surprise le diff est un fichier gz:
kevin@slackware:~$ file zip.gz
zip.gz: gzip compressed data, was "introduction.txt", from FAT filesystem (MS-DOS, OS/2, NT), last modified: Thu Mar 17 14:01:52 2011, max compression
kevin@slackware:~$ zcat zip.gz
Cher participant,

Le développeur étourdi d'un nouveau systÚme de gestion de base de données
révolutionnaire a malencontreusement oublié quelques fichiers sur son serveur
web. Une partie des sources et des objets de ce SGBD pourraient se révéler
utile afin d'exploiter une éventuelle vulnérabilité.

Sauras-tu en tirer profit pour lire la clé présente dans le fichier
secret1.dat ?

  url      : http://snip-snip-snip-snip   -- jetons un voile pudique
  login    : sstic2011                    -- sur une partie de ces
  password : snip-snip-snip-snip          -- informations :-)


--------------------------------------------------------------------------------
Toute attaque par déni de service est formellement interdite. Les organisateurs
du challenge se réservent le droit de bannir l'adresse IP de toute machine
effectuant un déni de service sur le serveur.
--------------------------------------------------------------------------------

gzip: zip.gz: unexpected end of file
kevin@slackware:~$
On avance donc dans le challenge. introduction.txt permettra de connaître secret1.dat, et la crypto étudiée auparavant donnera vraisemblablement secret2.dat.

(Un mot pour wezak qui se reconnaîtra sans doute. Les fichiers ont été indexés par google sur votre site web alors qu'aucun lien pointant vers eux n'est référencé. Ce qui signifie que soit les liens ont été envoyés via gmail, soit chrome a été utilisé pour les consulter. Dans les deux cas, google FAIL)

4/ Attaque en ligne 
Nous sommes partis d'un fichier video, nous nous retrouvons à attaquer du service en ligne :-) Ce challenge comporte bien des rebondissements! Lançons un navigateur web:
L'image lobster_dog n'a, semble t'il, rien d'exceptionnel, bien que je ne retrouve pas précisément la même sur internet (pas de metadata particulier dans cette image, pas de données utiles apparemment).

Une observation de la page web montre un message d'erreur en bas de page. Et l'erreur #2002 avec une référence à mySQL plus le contenu d'introduction.txt pousse à lancer un client mysql:
kevin@slackware:~$ mysql -u sstic2011 -h snip-snip-snip-snip -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 1

Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This software comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to modify and redistribute it under the GPL v2 license

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> \q
Bye
kevin@slackware:~$



Cette base de donnée ne semble pas être un mySQL "authentique" (pas de version, un select 3+2 ne fonctionne pas, les commandes basiques ne fonctionnent pas, etc etc..). Ceci dit, rien n'empêche de creuser un peu:
kevin@slackware:~$ mysql -u sstic2011 -h snip-snip-snip-snip -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 1

Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This software comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to modify and redistribute it under the GPL v2 license

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;
+----------+
| Database |
+----------+
|   system |
|    sstic |
+----------+
2 rows in set (0.05 sec)

mysql> use system;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> show tables;
+-------------+
| Tables      |
+-------------+
| information |
+-------------+
1 row in set (0.05 sec)

mysql> select * from information;
+------------------+----------+
| version          | security |
+------------------+----------+
| 1.3.337sstic2011 | SECCOMP  |
+------------------+----------+
1 row in set (0.05 sec)

mysql> use sstic;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> show tables;
+--------+
| Tables |
+--------+
|  users |
+--------+
1 row in set (0.05 sec)

mysql> select * from users;
+------+--------+----------------------------------+
| id   | login  | password                         |
+------+--------+----------------------------------+
| 0    | root   | 3e47b75000b0924b6c9ba5759a7cf15d |
| 1    | guest  | a76637b62ea99acda12f5859313f539a |
| 2    | nobody | 6c92285fa6d3e827b198d120ea3ac674 |
| 3    | *      | 5058f1af8388633f609cadb75a75dc9d |
+------+--------+----------------------------------+
4 rows in set (0.05 sec)

mysql> quit;
Bye
kevin@slackware:~$

Nous apprenons donc que seccomp est employé (1.3.337 en version est il une erreur? En l33Tsp34K, ce serait plutôt 3.1.337).  Le cassage MD5 des password est immédiat et donne:
"nothing" "interesting" "here" "."
:-) Faisons confiance aux auteurs du challenge. Je pense qu'il n'y a pas de SQL skills particulières à avoir pour ce challenge.

5/ Reverse!
Il est donc désormais temps de lire les deux fichiers udf fournis sur le site web. udf semble signifier selon moi "User Defined Function" ce que confirme le fichier udf.c:
kevin@slackware:~$ head udf.c
/*
 * CREATE FUNCTION max INTEGER, INTEGER RETURNS INTEGER SONAME "udf_max@udf.so";
 * CREATE FUNCTION min INTEGER, INTEGER RETURNS INTEGER SONAME "udf_min@udf.so";
 * CREATE FUNCTION abs INTEGER RETURNS INTEGER SONAME "udf_abs@udf.so";
 * CREATE FUNCTION concat STRING, STRING RETURNS STRING SONAME "udf_concat@udf.so";
 * CREATE FUNCTION substr STRING, INTEGER, INTEGER RETURNS STRING SONAME "udf_substr@udf.so";
 */

#define _BSD_SOURCE
#include
kevin@slackware:~$
Le code fait appel à une structure nommée val, que l'on retrouver dans chacune des fonctions, par exemple:
void udf_max(int a, int b, val *result) {
        result->value.i = (a > b) ? a : b;
}

et la lecture du code .c associé à un peu de désassemblage du .so permet de la reconstituer de cette manière:

typedef struct val {
 int unknown;      /*tout se passe à +4, unknown semble inutilisé (?)*/
 union {
    /*i et p sont tous les deux à +4*/
  int i;
  char *p;
 } value;
 size_t size;
 int (*expand)(struct val *);


Il faut désormais trouver un moyen d'exploiter la BdD on line. Je parviens déjà à la faire crasher:
mysql> select concat(3,'b');
ERROR 2013 (HY000): Lost connection to MySQL server during query
mysql>
mais n'arrive pas au delà. SECCOMP complique l'exploitation, les seuls appels systèmes acceptés sont exit(), sigreturn(), read() et write() aux file descriptors déjà ouverts.

Pour exploiter tout ça, on a quelques pistes: le code C utilise plusieurs fois des memcpy() qui est une fonction dangereuse, le code de retour de realloc() n'est pas vérifié, et la structure val fait un appel à la fonction expand via un pointeur (mais est il modifiable?). Enfin, le problème n'est peut-être pas lié à cette bibliothèque .so, mais au serveur SQL lui-même (après tout, l'auteur du nouveau système de gestion de base de données révolutionnaire est étourdi :-) ).
Un nmap sur l'adresse IP n'affiche que deux ports ouverts, le 80 et le 3306.

6/ This is the end, my friend
Je ne suis pas allé plus loin, bloqué par le reverse crypto et l'attaque du service en ligne. Je vais donc attendre la publication des résultats pour voir où j'ai pêché, si je suis passé à côté d'autres indices, et si j'avais raison dans la progression du challenge. J'aurai loisir de renommer ce post de blog en "plus ou moins proche de la solution". Je suis curieux de savoir si les organisateurs du concours vont donner les logs du serveur pour connaître le nombre de connexions, les tentatives réalisées, et s'ils publieront le code de celui-ci pour que l'on s'entraîne hors ligne.

Pour résumer:
-le service en ligne permet de connaître secret1.dat
-Il y a de grandes chances que la fonction publiée sur pastebin permette de reverser plus facilement la lib et donc de trouver secret2.dat, dommage qu'elle ne soit plus disponible.
-Avec secret1.dat et secret2.dat je pense que la video se lira correctement et dévoilera l'adresse mail tant recherchée :-) A moins qu'il ne s'agisse que d'une première étape et que la video indique seulement ou continuer la quête pour aller encore un peu plus loin.


Comme dit en introduction, ce challenge est vraiment riche en contenu et en rebondissements.. Aller attaquer un service en ligne, penser à retrouver de l'info sur pastebin, écrire un outil d'extraction de données, reverser des libs, creuser dans la doc mp4 pour lire les atom, etc.. tout ça à partir d'un simple fichier video, en fait un challenge très plaisant.

Thx 2 NR pour les échanges par mail.

2 commentaires: