22h, Les portes de la salle s’ouvrent.

Les participants de la Nuit du Hack jettent un regard curieux dans la pièce. Au milieu d’une grande salle lumineuse, les membres de Sysdream, organisateurs du CTF (Capture The Flag), rappellent les règles de la compétition qui vient d’ouvrir au public. Le principe est simple : 10 équipes de 5 joueurs venus des quatre coins du globe s’affrontent avec un seul et même but : gérer et protéger une compagnie aérienne tout en attaquant celles des adversaires. Site de réservation de billets, ERP de gestion des vols, API à destination des comparateurs de prix, appareils de lecture des tickets d’embarquement… Tous les services doivent rester joignables sous peine de pénalité, rappelle le responsable du CTF.

L’ensemble des machines des challengers est hébergé par la plateforme Malice, solution produite par Sysdream, utilisée pour la mise en place d’événements comme celui-ci.

Les prétendants au titre fixent leurs écrans d’ordinateur sans ciller. Le silence est pesant, tous veulent remporter ce challenge. Les doigts glissent sur les claviers. Depuis une demi-heure, les équipes ont accès à leurs infrastructures respectives. Identifiants, comptes clients, données sensibles, ils ont eu une trentaine de minutes pour changer leurs données par défaut (mots de passe…) avant l’ouverture complète du réseau aux équipes et de la salle au public.

La tension est palpable. Les dix équipes présentes ce soir sont sorties victorieuses du CTF de qualification qui avait vu plus de 1000 participants tenter leur chance en ligne le 1er Avril dernier. La récompense est de taille : le vainqueur remportera un chèque de 5 000 €. Malgré l’enjeu, les équipes restent cordiales. Les membres de EEE, retardés par des soucis logistiques, entrent dans la salle en trombe et s’installent pendant que la team Pwnies fête l’ouverture complète du réseau une bière en main. « CHEERS ! ». L’atmosphère se détend.

22h15, 0ops ouvre le score. La compétition démarre vite. L’équipe a réussi à exploiter une SQLi sur le service d’authentification centrale, mais elle ne l’utilise que sur 2 équipes. En parallèle, le Wargame public bat son plein dans une autre salle du centre de convention. Le Dashboard s’affiche sur un écran géant pour que les participants puissent suivre l’avancement du CTF privé.

23h20, p4 marque à son tour de nombreux points et score de manière spectaculaire pendant une demi-heure. Ils volent les billets d’avion enregistrés sur les bases de données centrales des 9 autres équipes. La compétition s’intensifie, les scores se resserrent. 0h20, L’équipe française Hexpresso, qui jusqu’à présent était restée discrète est bien décidée à se reprendre. Elle inverse la tendance en scorant à maintes reprises.

1h10, COCORICO ! Hexpresso comptabilise plus de 15 000 points. Cette avancée spectaculaire est remarquée jusque dans la salle du Wargame public d’où l’on entend retentir une salve d’applaudissements. Chauvinisme diront certains, fierté nationale diront les autres !

2h30, les polonais p4 se distinguent après une heure relativement calme, et se rapprochent des français en passant à 13 000 points. La compétition est ponctuée de flash infos animés qui annoncent les avancées marquantes des différentes équipes.

Pendant ce temps, les membres de Sysdream en charge de l’organisation du CTF veillent au comptage de points et assurent l’accessibilité du réseau et des infrastructures. Alors qu’ils s’occupaient des derniers préparatifs logistiques quelques heures plus tôt, les organisateurs avaient reçu la visite du Secrétaire d’Etat au numérique, Mr Mounir Mahjoubi, curieux de comprendre comment ce type de concours se déroulait.

3h30, Pwnies tente de redémarrer ses services de sorte à perdre moins de points via les pénalités. Mais ils se font immédiatement exploiter par plusieurs équipes et décident de faire marche arrière.

3h45, Le réveil des Bushwhackers se fait sentir. L’équipe marque 20 000 points en très peu de temps et prend la tête du classement provisoire devant Hexpresso et p4. Malgré l’heure tardive, l’ambiance reste studieuse et contraste avec la Crash Party qui se déroule à l’autre bout du couloir.

5h00, les écarts se creusent et le trio de tête se distingue des autres compétiteurs. P4 en profite pour dépasser doucement Hexpresso et prend la deuxième place du concours. Bushwackers fait cavalier seul et conserve une avance confortable sur ses deux plus proches concurrents. Le classement restera le même jusqu’à la clôture du challenge.

5h30, l’équipe 0ops trouve une faille de serialization dans le service AirManager permettant d’exploiter une RCE. Mais elle n’arrive pas à en tirer profit en essayant d’exploiter la mauvaise classe PHP.

6h00, sonne la fin du CTF privé. L’équipe Bushwackers qui termine en tête du classement remporte un chèque de 5 000 €.

Derrière elle, les équipes p4 et Hexpresso se voient remettre respectivement des cadeaux d’une valeur de 1000 et 500€.


L’équipe Pwnies s’est intéressée aux scanners de tickets à partir de 2h-3h mais n’a pas réussi à l’exploiter. Ci-dessous le Write Up du scanner de Tickets (QR) :

Writeup du scanner de tickets

Nous avions installé un RaspberryPi avec un écran et une webcam pour chaque équipe. Ils représentaient une porte d'embarquement avec un affichage et une fonctionnalité pour scanner les billets des passagers. Bien entendu, des failles étaient présentes pour prendre le contrôle du système.

Les joueurs pouvaient générer des billets valides via le site web public des autres équipes pour ensuite le scanner.

Identification des points d'entrée

La première étape est d'identifier la vulnérabilité du service. En décodant le QRCode d'un ticket, on s'aperçoit qu'il contient un identifiant et un cipher qui est encodé avec un pseudo-base64, séparés par le caractère ":".

En regardant de plus près le serveur de ticketing, on peut trouver les fonctions utilisées pour encoder/décoder le pseudo-base64 :

Python

import base64
import string

my_base64chars  = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+/="
std_base64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="


def translate(cipher, table_src,table_dst):
    chaine = ""
    for x in cipher:
        index = table_src.find(x)
        if index == -1:
            return ""
        chaine += table_dst[index]
    return chaine


def decode(cipher):
    cipher = translate(cipher, my_base64chars, std_base64chars)
    return base64.b64decode(cipher)


def encode(plain):
    cipher = base64.b64encode(plain)
    cipher = translate(cipher, std_base64chars, my_base64chars)
    return cipher

Déclencher la vulnérabilité

Nous n'avons que deux points d'entrée : l'identifiant et le base64. Nous testons le système pour les vulnérabilités de type buffer overflow habituelles. Voici les payloads :

11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111:

1:ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7ywf7

Le second payload est seulement 1:"a"150. Nous générons donc deux QRCodes avec ces données et nous voyons que le deuxième déclenche cette erreur :

*** Error in `/home/pi/piscanner': free(): invalid next size (normal): 0x0003d160 ***

Contrôler le Program Counter

Nous devons d'abord savoir ce qui crash exactement et ou l'overflow se produit. Invalid next size ? OK, on pense donc que c'est un buffer overflow sur la heap...

En analysant la fonction où l'overflow se produit, nous voyons que cette fonction est utilisée pour créer un object myRequest. Voyons le contenu de cette structure :

C
struct myRequest
{
    struct quirc_data   *data;
    long* something_not_used;    // this could be erased

    char padding[20];             // something that is either not used and not initialised
    /* Those pointers shows "OK" for the first one or "ERROR" for the last ones */
    void            (*funcOK[3])(struct myRequest* request);

    struct hostent      *hostinfo;
    uint16_t        port;
    /* The http requests send to the ticketing remote service  */
    char            *request;
};

On voit que l'on arrive à overflow dans une structure contenant des pointeurs de fonction. Intéressant...