Étape 0 - prologue
Description
The year is 2022. Gunshots shattered the night. A witness caught a glimpse of a vehicle at the scene — a Tesla, they claim? The investigators on the case firmly believe this incident is affiliated with a clandestine criminal group that has eluded them for years... You are given the police reports and a backup of the car's logs. Your technical expertise is needed to move the investigation forward!
Download the challenge files (sha256: e95deeb7202cc10786f767f83644291bd76958d86e2d311c4e93efade85b75cd)
Your are mandated to conduct an investigation on this group and ultimately retrieve a validation e-mail ([0-9a-f]{16}@sstic.org).
Indice
Un indice a été donné quelques jours après la parution de l’épreuve :
Important advice regarding the prologue: don't overthink it! Just carefully read the elements you are given — these clearly suggest what you should look for in the context of the investigation. As mentioned right above, an intermediate flag can be found after you solved the prologue; you are not necessarily looking for a SSTIC-formatted flag right away. Immerse yourself and try to answer what you're asked!
Résolution
Aussi appelée pas loin d’abandonner dès le début. Le lecteur qui ne désire pas s’encombrer des échecs successifs peut directement sauter à la section Finalement.
Setup
Le lien de téléchargement fourni contient les données suivantes :
- Un fichier pdf
Police Report interview.pdf
; - Un autre fichier pdf
Police Report requisition.pdf
; - Une extraction de sauvegarde Tesla au format TeslaMate backup
teslamate.bck
.
Les deux fichiers PDF sont des éléments récoltés par la police en Belgique pour tenter d’élucider un crime survenu en août 2022. Le premier retranscrit un entretien de témoin ayant assisté à la scène et déclarant avoir vu une Tesla blanche au moment de l’événement. Le second fournit la justification du fichier de backup obtenu, en montrant l’ordre de communiquer le fichier de backup aux autorités mandatées (de la “seule Tesla blanche” à proximité au moment du crime).
TeslaMate est OpenSource et facilement installable en suivant les instructions indiquées.
Le docker-compose fourni expose 4 services incluant une base de données postgreSQL, un Grafana pour visualiser les données, une interface Web en Elixir qui fait à peu près la même chose (et redirige sur le Grafana pour la plupart des requêtes et dashboards) mais permet des actions sur les paramètres ou sur la Tesla elle-même (a priori). Le dernier service est un broker MQTT pour supporter le transport des messages, de peu d’intérêt ici.
On peut facilement importer les données de la backup qui sont un export SQL classique, la procédure est d’ailleurs documentée sur le site officiel :
docker-compose exec -T database psql -U teslamate -d teslamate < teslamate.bck
Exploration initiale
Une fois la DB en place, on peut constater qu’il y a peu de tables accessibles, et se dire que ça ne devrait pas être trop difficile de trouver les données qui nous intéressent (erreur) :

L’oeil avisé aura reconnu un étrange “http_” dans le nom de la première geofence. L’oeil non avisé n’aura pas prêté attention à ce détail, ce qui va lui coûter cher en heures par la suite
Avant d’entrer dans le détail du contenu de la DB, on peut également aller fouiller sur le Grafana. Étant donné qu’on semble nous indiquer d’aller chercher les habitudes de conduite de notre suspect principal, on peut directement aller au dashboard associé :

Parmi tous les dashboards proposés, on peut en trouver un qui récapitule les trajets du suspect, notamment vers la date du crime :


Sur le parcours affiché, on peut constater une anomalie suspecte qui correspond en plus au jour du drame :

Finalement, ça ne donne rien de probant (en même temps, qu’est-ce qu’on s’attendait à trouver en zoomant sur l’anomalie …). Aucun des autres dashboards ne semble aboutir à l’affichage de données pertinentes.
Retour à la donnée brute
Puisque les différents dashboards Grafana ne révèlent rien au premier regard, il est temps de regarder les tables qui sont potentiellement en lien avec des localisations. Parmi celles-ci, on retrouve :
- La table adresses qui contient normalement toutes les adresses enregistrées par le conducteur (table très intéressante puisque contenant un nombre limité mais notable d’entrées (83)) ;
- La table drives qui contient les informations sur tous les trajets effectués (départ, arrivée, paramètre à ces moments, …) ;
- La table geofences qui contient toutes les positions enregistrées de balises de repérages comme celles pour les chargeurs ;
- La table positions (la plus volumineuse) qui contient un très grand nombre d’enregistrements des positions de la voiture et de paramètres physiques au cours du temps.
Intéressons nous à la table adresses. Celle-ci contient 83 enregistrements, chacun étant composé d’au moins un nom, le résultat d’une requête OpenStreetMap (OSM) et d’un OSM_ID :

Les données de longitude et latitude indiquées sont cohérentes avec celles de la colonne raw :

Cependant, certaines anomalies viennent perturber la cohérence des données, et notamment des noms de lieux inexistants:

Est-ce que notre auteur sur OpenStreetMap est de confiance ?

De plus, les OSM_ID indiqués dans la dernière colonne ne sont pas cohérents par rapport à ceux du JSON. On peut faire la corrélation avec OpenStreetMap.
Finalement, la piste se révèle infructueuse.
Le moment de se perdre
On revient aux dahsboards, et comme on commence à manquer d’idées, on va entamer une phase d’hallucination individuelle consistant à voir du binaire partout où il y en a, mais pas pour former un flag :


Finalement, au bout de plus de 130 cases d’un notebook Jupyter sans sens :

NDLR : Généralement, sortir numpy pour calculer des LSB sur des données obscures, alors que l’épreuve a été déjà flag en moins d’une heure, est une (très) mauvaise idée.
Plus à ça près
Après un retour à la base de données, on se rend compte qu’il y a une table tokens
qui contient une unique ligne, avec ce qui semble être un access et un refresh token chiffrés. BINGO ! D’après la documentation, ces éléments sont chiffrés avec le mot de passe qui est demandé lors de l’accès à l’API Tesla depuis teslamate.
Qu’à cela ne tienne, on va tenter cette piste prometteuse. Encore faut-il savoir comment est chiffré le jeton et de quel jeton il s’agit. Pour cela, il faut créer un compte sur le site de Tesla pour obtenir un access token valide :

On peut ensuite s’authentifier en local à l’aide du binaire tesla_auth
qui ouvre un mire d’authentification. Une fois connecté, on obtient les jetons espérés, qu’on peut transmettre à l’application teslamate :

Au final, ces jetons sont chiffrés avec le mot de passe choisi initialement dans la configuration du docker-compose ( variable d’environnement ENCRYPTION_KEY
). Le mécanisme de chiffrement choisi peut être retrouvé dans le code source Elixir du fichier vault

Quelques lignes de python plus tard et on constate qu’on récupère bien le JWT pour la clé valant le secret par défaut. On tente donc un script de bruteforce du secret sur le jeton initial, le mot de passe sera dans rockyou non ?

Encore raté …
Finalement
Rien de tout ça n’était nécessaire au final. L’indication du pdf suivante couplée à la visualisation via le Grafana étaient les seuls éléments utiles à la résolution du prologue :
WOULD YOU HAPPEN TO KNOW WHERE THIS CAR USUALLY WENT? FOR INSTANCE, ITS MOST VISITED PLACES. SUCH INFORMATION WOULD HELP IN OUR INVESTIGATION
Le grafana présente en effet une vue Drive Stats
qui montre ce qu’on cherche, à savoir les lieux les plus visités par le suspect / coupable :

On se rend sur http://163.172.99.233:8080 et c’est gagné pour cette step 0, pfiou :
curl http://163.172.99.233:8080 2>&1|grep SSTIC\{
...SSTIC{0a3ef4d4bb265ca2f27dd557be0...}
Analyse post-mortem
Bien que certains spécialistes en stéganographie et en analyses des petits détails aient immédiatement su comment exploiter les données atypiques contenues dans la base TeslaMate, le problème est qu’ici il y a beaucoup d’éléments de contexte et de données qui peuvent être interprétés comme autant de pistes potentielles infructueuses.
Certaines incohérences ou anomalies (comme la singularité du trajet au moment de l’accident affichée sur Grafana, les différences entre OSM ID entre raw json et colonne de la table adresses, ou bien encore le manque de certains identifiants auto-incrémentés dans la table des drives) poussent naturellement à creuser de manière futile des culs-de-sac.
Même s’il s’agit d’un reproche qui est facile à faire vis-à-vis de la stéganographie de manière générale, il n’en demeure pas moins une certaine frustration engendrée à passer plus de 6 heures sur un tel début. Malgré tout, le cheminement proposé reste “logique”, seulement le résultat est caché de manière complètement irréaliste dans un flot de données qui le sont beaucoup plus.
Bref, la partie du challenge que j’ai le moins appréciée (il en faut bien une), merci à la personne qui m’a donné le coup de pouce / remis sur les bons rails pour franchir cette étape.