29
juin
'17

L'attaque du porte-clé intercepteur

Publié le 29 juin 2017

Lors de la quinzième édition de la Nuit du Hack, qui avait lieu ce week-end au New York Hotel de Disneyland Paris, j'ai présenté un moyen simple et pas cher de concevoir un équipement permettant de réaliser des attaques sur le protocole Bluetooth Low Energy (BLE), voire de simuler le comportement de n'importe quel équipement BLE. Je n'ai pas eu le temps d'entrer dans les détails, aussi vais-je le faire dans ce billet de blog.

Recyclage de Gablys

Cela fait quelques années que je cotoie des Gablys, des porte-clés connectés en BLE à des smartphones permettant de détecter la perte de clés ou d'objets, qui peut accessoirement être utilisé comme télécommande pour déclencher des selfies ou retrouver son smartphone égaré (une fonction de localisation inversée, en quelque sorte).

Un Gablys est assez simple: l'ensemble repose sur un transceiver de Nordic Semiconductor capable de communiquer en BLE, le nRF51822. Ce transceiver intègre un CPU ARM ainsi qu'une mémoire Flash de 256Kio, et a la particularité d'être débuggable et programmable via OpenOCD. De plus, il possède de base un bouton poussoir ainsi qu'un buzzer piezzo, qui permettent une interaction utilisateur (quoique l'on peut s'en passer selon les usages).

Les avantages du nRF51822 sont multiples:

Bref, il est dès lors possible de reprogrammer un Gablys afin de lui faire faire ce que l'on veut, à l'aide d'outils standards.

Installation des logiciels nécessaires

Pour réussir à développer et tester un firmware opensource pour Gablys, j'ai commencé par télécharger la version 11.0 du SDK de Nordic Semiconductor. Pourquoi la version 11.0 me direz-vous ? Parce que c'était celle a priori employée pour développer le firmware original de la bête. Notez que la dernière version du SDK devrait tout aussi bien fonctionner, mais il faudra prendre en compte les évolutions des fonctions exposées par ce dernier.

Tout d'abord, on télécharge la version voulue du SDK (la version 12.0 par exemple sur le site de Nordic Semiconductor. On dézippe le fichier dans un dossier prévu à cet effet:

$ unzip nRF5_SDK_12.3.0_d7731ad.zip
$ mv nRF5_SDK_12.3.0_d7731ad nrf5-sdk

Il nous faut ensuite installer la toolchain ARM et en ce qui me concerne j'ai préféré installer une version à jour plutôt que celle de ma debian, en la téléchargeant directement à partir du site d'ARM. J'en ai profité pour la décompresser dans un dossier à part, nommé toolchain:

$ cd nrf5-sdk && mkdir toolchain && cd toolchain
$ wget "https://developer.arm.com/-/media/Files/downloads/gnu-rm/6_1-2017q1/gcc-arm-none-eabi-6-2017-q1-update-linux.tar.bz2?product=GNU%20ARM%20Embedded%20Toolchain,64-bit,,Linux,6-2017-q1-update"
$ tar xvjf gcc-arm-none-eabi-6-2017-q1-update-linux.tar.bz2

Une fois la toolchain ARM décompressée, il suffit de la déclarer dans le fichier components/toolchain/gcc/Makefile.posix du SDK Nordic Semiconductor:

GNU_INSTALL_ROOT := /home/virtualabs/nrf5-sdk/toolchain/gcc-arm-none-eabi-6-2017-q1-update/
GNU_VERSION := 6.3.1
GNU_PREFIX := arm-none-eabi

Enfin, il nous faut ajouter une configuration particulière pour notre Gablys dans le fichier examples/bsp/gablys.h. Pour ce faire, il suffit de copier un fichier de configuration existant:

$ cp pca20006.h gablys.h

Nous en profitons pour ajouter une ligne dans le fichier boards.h du même dossier afin de déclarer notre board:

...

#elif defined(BOARD_N5DK1)
  #include "n5_starterkit.h"
#elif defined(BOARD_GABLYS)
  #include "gablys.h"
#elif defined(BOARD_CUSTOM)
  #include "custom_board.h"
#else
#error "Board is not defined"

...

Puis de modifier la fin du fichier afin de ne pas utiliser de quartz basse-fréquence, absent sur notre Gablys:

// Low frequency clock source to be used by the SoftDevice
#define NRF_CLOCK_LFCLKSRC      *                      \
    .source        = NRF_CLOCK_LF_SRC_RC,            \
    .rc_ctiv       = 16,                                                 \
    .rc_temp_ctiv  = 2,                                               \
   .xtal_accuracy = NRF_CLOCK_LF_XTAL_ACCURACY_250_PPM \
*

De cette manière, tous les codes exemples fonctionneront sans problème avec notre Gablys, ce dernier ne possédant pas d'oscillateur basse-fréquence. Il est alors temps de lancer notre première compilation, et voir si ces modifications portent leurs fruits.

Première compilation

Pour essayer notre configuration, nous allons tenter de compiler une application BLE sur mesure, ble_app_beacon. Pour ce faire, nous allons ajouter une configuration propre à notre Gablys, et faire en sorte de tout configurer pour que cela compile correctement.

Dans un premier temps, il nous faut créer une arborescence sur mesure:

$ cd nrf5-sdk/examples/ble_peripheral/ble_app_beacon/
$ mkdir -p gablys/s130/armgcc/
$ cp pca20006/s130/config ./gablys/s130/ -rf
$ cp pca20006/s130/armgcc ./gablys/s130/armgcc -rf

Puis nous éditons dans un premier temps le fichier Makefile situé dans le dossier gablys/s130/armgcc/, afin de déclarer notre board plutôt que celle d'origine (PCA20006). Pour ce faire, il faut changer les options de compilation -DBOARD_PCA20006 en -DBOARD_GABLYS:

# Libraries common to all targets
LIB_FILES += \

# C flags common to all targets
CFLAGS += -DSWI_DISABLE0
CFLAGS += -DSOFTDEVICE_PRESENT
CFLAGS += -DNRF51
CFLAGS += -DNRF51822
CFLAGS += -DS130
CFLAGS += -DBLE_STACK_SUPPORT_REQD
CFLAGS += -DBOARD_GABLYS
CFLAGS += -DNRF_SD_BLE_API_VERSION=2
CFLAGS += -mcpu=cortex-m0
CFLAGS += -mthumb -mabi=aapcs
CFLAGS +=  -Wall -Werror -O3 -g3
CFLAGS += -mfloat-abi=soft
# keep every function in separate section, this allows linker to discard unused ones
CFLAGS += -ffunction-sections -fdata-sections -fno-strict-aliasing
CFLAGS += -fno-builtin --short-enums

# C++ flags common to all targets
CXXFLAGS += \

# Assembler flags common to all targets
ASMFLAGS += -x assembler-with-cpp
ASMFLAGS += -DSWI_DISABLE0
ASMFLAGS += -DSOFTDEVICE_PRESENT
ASMFLAGS += -DNRF51
ASMFLAGS += -DNRF51822
ASMFLAGS += -DS130
ASMFLAGS += -DBLE_STACK_SUPPORT_REQD
ASMFLAGS += -DBOARD_GABLYS
ASMFLAGS += -DNRF_SD_BLE_API_VERSION=2

Et on lance la compilation avec make:

$ make
mkdir _build
Compiling file: nrf_log_backend_serial.c
Compiling file: nrf_log_frontend.c
Compiling file: app_button.c
Compiling file: app_error.c
Compiling file: app_error_weak.c
Compiling file: app_timer.c
Compiling file: app_util_platform.c
Compiling file: hardfault_implementation.c
Compiling file: nrf_assert.c
Compiling file: sdk_errors.c
Compiling file: boards.c
Compiling file: nrf_drv_clock.c
Compiling file: nrf_drv_common.c
Compiling file: nrf_drv_gpiote.c
Compiling file: nrf_drv_uart.c
Compiling file: bsp.c
Compiling file: bsp_nfc.c
Compiling file: main.c
Compiling file: RTT_Syscalls_GCC.c
Compiling file: SEGGER_RTT.c
Compiling file: SEGGER_RTT_printf.c
Compiling file: ble_advdata.c
Compiling file: ble_conn_params.c
Compiling file: ble_srv_common.c
Assembling file: gcc_startup_nrf51.S
Compiling file: system_nrf51.c
Compiling file: softdevice_handler.c
Linking target: _build/nrf51822_xxaa.out

   text        data     bss     dec     hex filename
  11808         136     616   12560    3110 _build/nrf51822_xxaa.out

Preparing: _build/nrf51822_xxaa.hex
Preparing: _build/nrf51822_xxaa.bin

Le code compile parfaitement, et le Makefile produit un fichier binaire ainsi qu'un fichier au format ihex, dont nous allons nous servir pour programmer le nRF51822 présent sur le Gablys.

Programmation du Gablys

Le Makefile standard est fait pour fonctionner avec l'outil nrfjprog, cependant il est possible de le faire fonctionner avec OpenOCD et un STLink v2 à la chinoise.

Première étape, installer OpenOCD. La version 0.10.0 tirée de Github fera l'affaire. Vous pouvez tout aussi bien l'installer via les paquets standards de votre distribution, cela devrait fonctionner de la même façon.

On modifie donc la fin de notre fichier Makefile pour y intégrer deux cibles: la première nommée flash permettant de flasher l'application, la seconde nommée flash_softdevice permettant de flasher le SoftDevice [1].

flash: nrf51822_xxaa
    @echo Flashing: $(OUTPUT_BINARY_DIRECTORY)/$<.hex
    openocd -f interface/stlink-v2.cfg -f target/nrf51.cfg -c init -c "reset init" -c "halt" -c "program _build/nrf51822_xxaa.hex verify" -c "reset" -c "exit"

## Flash softdevice
flash_softdevice:
    @echo Flashing SoftDevice s130 ...
    openocd -f interface/stlink-v2.cfg -f target/nrf51.cfg -c init -c "reset init" -c "halt" -c "nrf51 mass_erase" -c "program $(abspath ../../../../../../components/softdevice/s130/hex/s130_nrf51_2.0.1_softdevice.hex) verify" -c "reset" -c "exit"

Ensuite, il faut connecter correctement le STLink à notre Gablys:

Puis flasher d'abord le SoftDevice, et ensuite l'application:

$ make flash_softdevice && make flash
Flashing SoftDevice s130 ...
openocd -f interface/stlink-v2.cfg -f target/nrf51.cfg -c init -c "reset init" -c "halt" -c "nrf51 mass_erase" -c "program /home/virtualabs/nrf5-sdk/components/softdevice/s130/hex/s130_nrf51_2.0.1_softdevice.hex verify" -c "reset" -c "exit"
Open On-Chip Debugger 0.10.0+dev-00146-g1025be36 (2017-06-16-16:35)
Licensed under GNU GPL v2
For bug reports, read
    http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
adapter speed: 1000 kHz
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : clock speed 950 kHz
Info : STLINK v2 JTAG v17 API v2 SWIM v4 VID 0x0483 PID 0x3748
Info : using stlink api v2
Info : Target voltage: 3.246408
Info : nrf51.cpu: hardware has 4 breakpoints, 2 watchpoints
target halted due to debug-request, current mode: Thread
xPSR: 0xc1000000 pc: 0x000006d0 msp: 0x000007c0
Info : nRF51822-QFAA(build code: G2) 256kB Flash
target halted due to debug-request, current mode: Thread
xPSR: 0xc1000000 pc: 0xfffffffe msp: 0xfffffffc
** Programming Started **
auto erase enabled
Info : Padding image section 0 with 2112 bytes
Warn : using fast async flash loader. This is currently supported
Warn : only with ST-Link and CMSIS-DAP. If you have issues, add
Warn : "set WORKAREASIZE 0" before sourcing nrf51.cfg to disable it
target halted due to breakpoint, current mode: Thread
xPSR: 0x61000000 pc: 0x2000001e msp: 0xfffffffc
wrote 110592 bytes from file /home/virtualabs/nrf5-sdk/components/softdevice/s130/hex/s130_nrf51_2.0.1_softdevice.hex in 4.979112s (21.691 KiB/s)
** Programming Finished **
** Verify Started **
target halted due to breakpoint, current mode: Thread
xPSR: 0x61000000 pc: 0x2000002e msp: 0xfffffffc
target halted due to breakpoint, current mode: Thread
xPSR: 0x61000000 pc: 0x2000002e msp: 0xfffffffc
verified 108448 bytes in 0.698049s (151.717 KiB/s)
** Verified OK **
Flashing: /nrf51822_xxaa.hex
openocd -f interface/stlink-v2.cfg -f target/nrf51.cfg -c init -c "reset init" -c "halt" -c "program _build/nrf51822_xxaa.hex verify" -c "reset" -c "exit"
Open On-Chip Debugger 0.10.0+dev-00146-g1025be36 (2017-06-16-16:35)
Licensed under GNU GPL v2
For bug reports, read
    http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
adapter speed: 1000 kHz
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : clock speed 950 kHz
Info : STLINK v2 JTAG v17 API v2 SWIM v4 VID 0x0483 PID 0x3748
Info : using stlink api v2
Info : Target voltage: 3.246408
Info : nrf51.cpu: hardware has 4 breakpoints, 2 watchpoints
target halted due to debug-request, current mode: Thread
xPSR: 0xc1000000 pc: 0x000006d0 msp: 0x000007c0
target halted due to debug-request, current mode: Thread
xPSR: 0xc1000000 pc: 0x000006d0 msp: 0x000007c0
** Programming Started **
auto erase enabled
Info : nRF51822-QFAA(build code: G2) 256kB Flash
Warn : using fast async flash loader. This is currently supported
Warn : only with ST-Link and CMSIS-DAP. If you have issues, add
Warn : "set WORKAREASIZE 0" before sourcing nrf51.cfg to disable it
target halted due to breakpoint, current mode: Thread
xPSR: 0x61000000 pc: 0x2000001e msp: 0x000007c0
wrote 12288 bytes from file _build/nrf51822_xxaa.hex in 0.589847s (20.344 KiB/s)
** Programming Finished **
** Verify Started **
target halted due to breakpoint, current mode: Thread
xPSR: 0x61000000 pc: 0x2000002e msp: 0x000007c0
verified 11944 bytes in 0.088029s (132.503 KiB/s)
** Verified OK **

La programmation du SoftDevice et de l'application se sont bien déroulées, tout comme la vérification de la bonne écriture du code. De base, le CPU a été remis à zéro après la programmation, et si vous avez laissé votre Gablys connecté, celui-ci doit annoncer un périphérique BLE ressemblant à un beacon.

Il est temps d'aller plus loin dans le code source, afin d'implémenter quelque chose d'utile !

Firmware opensource pour Gablys

J'ai profité de l'occasion pour développer un firmware opensource pour Gablys, réimplémentant les principaux services et leurs characteristics, et se comportant presque à l'identique. Le code source de ce firmware est disponible sur Github. Ce dernier peut être compilé et flashé comme décrit précédemment.

En pratique, les services propres au Gablys sont implémentés dans le fichier gss/gss.c, dont la fonction ble_gss_init a pour rôle la création et l'initialisation de l'ensemble de ces services et des characteristics associées. A noter l'astuce avec le SDK Nordic pour déclarer un service ayant un UUID sur 128 bits:

static const ble_uuid128_t m_btn_uuid128 =
    {
       {
           0x45, 0xfa, 0x56, 0xc1, 0xfb, 0x1b, 0xc0, 0x28,
           0x96, 0xa8, 0x67, 0x18, 0x01, 0x28, 0x17, 0x4f
       }
    };

    /* ... du code ici ... */

    /* Add Gablys Button service. */
    ble_btn_uuid.uuid = GSS_BTN_SERVICE_UUID;
    err_code = sd_ble_uuid_vs_add(&m_btn_uuid128, &ble_btn_uuid.type);
    APP_ERROR_CHECK(err_code);

    err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
                            &ble_btn_uuid,
                            &p_gss->btn_service_handle);

    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

Enfin, la stack BLE est initialisée dans le fichier main.c, grâce à la fonction ble_stack_init:

static void ble_stack_init(void)
{
    uint32_t err_code;

    nrf_clock_lf_cfg_t clock_lf_cfg = NRF_CLOCK_LFCLKSRC;

    // Initialize the SoftDevice handler module.
    SOFTDEVICE_HANDLER_INIT(&clock_lf_cfg, NULL);

    ble_enable_params_t ble_enable_params;
    err_code = softdevice_enable_get_default_config(CENTRAL_LINK_COUNT,
                                                    PERIPHERAL_LINK_COUNT,
                                                    &ble_enable_params);
    APP_ERROR_CHECK(err_code);

    //Check the ram settings against the used number of links
    CHECK_RAM_START_ADDR(CENTRAL_LINK_COUNT,PERIPHERAL_LINK_COUNT);

    // Enable BLE stack.
    sd_ble_enable(&ble_enable_params, &app_ram_base);
    ble_enable_params.common_enable_params.vs_uuid_count = 10;
    err_code = softdevice_enable(&ble_enable_params);
    APP_ERROR_CHECK(err_code);

    // Register with the SoftDevice handler module for BLE events.
    err_code = softdevice_ble_evt_handler_set(ble_evt_dispatch);
    APP_ERROR_CHECK(err_code);

    // Register with the SoftDevice handler module for BLE events.
    err_code = softdevice_sys_evt_handler_set(sys_evt_dispatch);
    APP_ERROR_CHECK(err_code);
}

C'est d'ailleurs dans cette fonction que l'on peut rajouter du code permettant d'usurper une adresse MAC, comme ci-dessous:

static void ble_stack_init(void)
{
    uint32_t err_code;
    ble_gap_addr_t address;

    address.addr_type = BLE_GAP_ADDR_TYPE_PUBLIC;
    address.addr[0] = 0xFE;
    address.addr[1] = 0xFE;
    address.addr[2] = 0xC0;
    address.addr[3] = 0x15;
    address.addr[4] = 0x4D;
    address.addr[5] = 0xDE;

    nrf_clock_lf_cfg_t clock_lf_cfg = NRF_CLOCK_LFCLKSRC;

    // Initialize the SoftDevice handler module.
    SOFTDEVICE_HANDLER_INIT(&clock_lf_cfg, NULL);

    ble_enable_params_t ble_enable_params;
    err_code = softdevice_enable_get_default_config(CENTRAL_LINK_COUNT,
                                                    PERIPHERAL_LINK_COUNT,
                                                    &ble_enable_params);
    APP_ERROR_CHECK(err_code);

    //Check the ram settings against the used number of links
    CHECK_RAM_START_ADDR(CENTRAL_LINK_COUNT,PERIPHERAL_LINK_COUNT);


    // Enable BLE stack.
    sd_ble_enable(&ble_enable_params, &app_ram_base);
    ble_enable_params.common_enable_params.vs_uuid_count = 10;
    err_code = softdevice_enable(&ble_enable_params);
    APP_ERROR_CHECK(err_code);

    sd_ble_gap_address_set(BLE_GAP_ADDR_CYCLE_MODE_NONE, &address);

    // Register with the SoftDevice handler module for BLE events.
    err_code = softdevice_ble_evt_handler_set(ble_evt_dispatch);
    APP_ERROR_CHECK(err_code);

    // Register with the SoftDevice handler module for BLE events.
    err_code = softdevice_sys_evt_handler_set(sys_evt_dispatch);
    APP_ERROR_CHECK(err_code);
}

Une fois compilé et flashé, le nouvel équipement est visible avec l'adresse MAC DE:4D:15:C0:FE:FE, comme le montre l'exemple ci-dessous (testé avec l'exemple beacon et non le firmware opensource):

Vous vous imaginez bien à ce stade de la lecture qu'il n'est pas très difficile de recréer n'importe quel équipement Bluetooth Low Energy à partir de ce petit porte-clé, et qu'avec un peu d'imagination et de temps on pourrait mettre à mal la sécurité de quelques équipements, voire réaliser des attaques très ciblées sur des périphériques communiquant en BLE.

Ok, et quoi d'autre ?

Nous avons démontré qu'il était possible d'utiliser un Gablys comme plateforme de développement peu onéreuse, à l'aide de logiciels libres et d'équipement à bas coût (STLink v2 notamment), et de profiter des avantages de ce dernier: il est petit, peu gourmand en énergie, programmable à volonté et alimenté par une pile bouton !

Il est dès lors possible de le mettre dans une poche et de simuler un équipement existant jusqu'à utiliser la même adresse Bluetooth (MAC), disons par exemple un cadenas connecté, et d'amener une application sur smartphone à transmettre à ce faux cadenas un code confidentiel (cette dernière faisant confiance à l'adresse Bluetooth du cadenas), comme je l'ai démontré l'année dernière lors de la Nuit du Hack.

J'ai placé le SDK modifié avec les examples cités dans cet article sur un repository spécifique, si certains d'entre vous veulent tester sans tout modifier à la main.

[1] Pour plus d'information sur les SoftDevice de NordicSemiconductor, consulter la page dédiée sur le site du fondeur

23
juil.
'12

Reverse-engineering d'application Flex

Publié le 23 juillet 2012

Tout récemment, j'ai eu à auditer un jeu en ligne pour un client, et je m'attendai à un jeu super travaillé qui repose sur des standards connus (un jeu en HTML5/CSS3/JS comme l'excellent CutTheRope). Mais non, je me suis retrouvé face à un jeu basé sur Flash et la technologie Flex. Si vous êtes un lecteur régulier de ce blog, vous devez savoir que les applications Flash ne me rebuttent pas, bien au contraire !

La prise en main du jeu est simple, mais le dialogue avec le serveur de jeu reste très obscur (car il s'agit d'un MEUPORG, bien sûr). D'après les captures effectuées, les données respectent le format Action Message Format (AMF) défini par Adobe, j'y avais déjà touché à l'époque des débuts de Deezer mais je suis loin d'en être un grand fan. Après de plus amples recherches, j'ai découvert qu'il s'agissait d'une application Flex, dialoguant avec plusieurs serveurs de jeu via de l'AMFRPC, un protocole activement employé par Flex. J'ai donc du m'équiper.

1. Outillage

Pour analyser des applications Flash, rien de mieux qu'un décompilateur Flash de renom. Pour ma part, je n'utilise pas Flare car incompatible avec les dernières versions de Flash, mais son pendant payant et pour Windows SWFDecompiler de Sothink. Cet outil reste LA référence dans le domaine, et permet de désassembler (presque) toutes les applications Flash. A cela s'ajoute la suite SWFTOOLS, bien pratique pour extraire des informations d'applications Flash (des ressources notamment).

Pour le proxy web, j'ai opté pour Charles Web Proxy. Il s'agit encore une fois d'un outil pour Windows, mais vu que de toute façon j'utilise déjà SWFDecompiler ... Charles Web Proxy permet de faire ce que tout bon proxy web transparent doit normalement savoir faire: espionner les requêtes, autoriser des points d'arrêt et surtout être capable de décoder et d'encoder les données selon plusieurs formats, dont l'AMF. On pourrait aussi employer Burp, qui possède la même fonctionnalité.

Pour terminer, la bibliothèque Python PyAMF permet de coder rapidement des clients AMFRPC en Python, très pratique dans notre cas.

Muni de ces outils, il est alors aisé d'intercepter les appels AMFRPC, de les analyser et de comprendre le fonctionnement global du service distant.

2. Flex, services et méthodes

Les applications Flex reposent sur des endpoints dans lesquels des services sont exposés, qui implémentent des méthodes que l'application Flash peut appeler à distance. Il s'agit ici de ce qu'on appelle couramment un système d'appel de procédure distante, ou Remote Procedure Call (RPC). Ce système de service est très courant, et permet d'offrir plusieurs services sur un seul point de connexion (ou endpoint.

Du point de vue d'un pentester, un service Flex est une grosse boîte noire possédant des services qui implémentent des méthodes, qu'il va falloir découvrir. Pour cela, plusieurs solutions sont envisageables:

Le dernier point est un peu plus ardu, car nécessite le développement d'un client AMFRPC dédié. Les deux premières méthodes sont donc préférables. J'ai ainsi débuté mon analyse par une décompilation du fichier SWF contenant le coeur de l'application. Et les ennuis ont commencés: SWFDecompiler plante lâchement durant l'analyse, et impossible donc d'obtenir le code de l'application. J'ai fait de nombreuses tentatives, avec différents logiciels, sans succès. Il semblerait que SWFDecompiler ait montré ses limites. Cela signifie-t-il qu'il est impossible d'extraire quoique ce soit de ce fichier ? Que nenni (© Tixlegeek)

2.1. Flasm + strings + grep = FTW (plan B)

N'ayant pas la possibilité de désassembler l'application, le plan B consiste à extraire directement du fichier les chaînes de caractères et à essayer de déduire les services des informations s'y trouvant. Avant toute chose, on fait appel à la commande file pour identifier le type d'application:

$ file ZOMFG.swf
ZOMFG.swf: Macromedia Flash data (compressed), version 10

Il s'agit d'une application Flash >=10 compressée. Autrement dit, rien ne sert de grepper directement le contenu du fichier, c'est compressé. Il nous faut donc décompresser ce fichier, grâce à flasm:

$ flasm -x ZOMFG.swf
ZOMFG.swf successfully decompressed, 7958828 bytes
$ file ZOMFG.swf
ZOMFG.swf: Macromedia Flash data, version 10

Il est ensuite trivial d'extraire les chaînes de caractères avec strings, et de grepper le résultat à la recherche d'éléments intéressants:

$ strings ZOMFG.swf > ZOMFG.txt
$ grep ZOMFG.txt -e "some keywords here"

Il est aussi possible d'utiliser vim et ses recherches par motif, ou autre.

2.2. Analyse de flux

L'analyse de flux est relativement simple à réaliser grâce à Charles Web Proxy: celui-ci parse vraiment bien les messages AMF mais n'affiche pas tout (comme par exemple certaines listes), cependant cela reste un compagnon de choix ! Cette analyse de flux permet de mettre à jour un certain nombres de services, ainsi que de méthodes.

Il suffit d'utiliser l'application, et de noter les différents services et les méthodes associées. De plus, il peut être intéressant de repérer les différents messages et les champs associés, afin d'envisager des attaques par rejeu par la suite.

Le champ destination contient la référence au service, ici appelé "SMC". Le champ operation décrit la méthode à appeler, en l'occurrence "ExecuteServerCall".

{2.3. Enumération des {services et des méthodes associées}}

Cette dernière possibilité repose sur les résultats produits par les deux étapes précédentes: à l'aide de la première analyse, on établit une liste potentielle de services et de noms de méthodes, puis à l'aide d'un client AMFRPC maison, on teste la validité des services puis on détermine les méthodes existantes à partir des services identifiés.

Cette technique permet de déterminer de manière plus efficace les services et les méthodes associées, et permet de trouver dans certains cas des éléments non-présents dans l'application. J'ai développé dans le cadre de ce test d'application un outil semblable à DeBlaze, mais permettant de tester un service Flex distant (et aussi beaucoup moins poussé). Cet outil repose sur la bibliothèque Python PyAMF, et tente de déterminer les services offerts par un endpoint donné. Cela suppose que l'on connaisse le nom de cet endpoint, qui peut être trouvé via les deux méthodes précédentes.

Je ne peux pas dévoiler le code source de cet outil dans ce post (ooooh!), car il est soumis à des règles relativement strictes de confidentialité, propres à Sysdream. Cependant, l'algorithme de base est le suivant (aaaah !):

Pour chaque nom de service probable:
    resultat = tenter un appel à la méthode Trololololo
    Si pas d'erreur d'invocation:
        Affiche 'Service: ' + service
        Pour chaque méthode probable:
            resultat =appeler  la méthode 'methode' du service
            Si aucune erreur d'invocation ni message de méthode non trouvée:
                  Affiche '- Methode:' + methode

De cette manière, on teste l'ensemble des services possibles et des méthodes probables. On peut aussi envisager des combinaisons de nom, des changement de casse, etc ... A noter que la référence à l'endpoint est obligatoire dans les headers Flex.

3. Sessions Flex

Lors de mes tests, j'ai été rapidement rejeté par le service Flex à cause des sessions. En effet, les applications Flex font appel à une gestion de session en Java (donc un cookie JSESSIONID par défaut), et il faut gérer ce cookie dans le client AMF pour pouvoir maintenir une session correcte. De même, l'envoi des requêtes via des "messages" AMF est basé sur une numérotation qui est continuellement incrémentée. Celle-ci doit aussi être maintenue par notre client AMF.

Lors des tests, et tout particulièrement lors des tests fonctionnels, les sessions sont primordiales et bien souvent mises de côté par les outils qui ne font pour la plupart que des tests unitaires. Ce qui impose dans bien des cas l'implémentation d'un client Flex maison prenant en charge ces sessions, quand il n'y a pas d'autres éléments de cookie à prendre en compte, bien sûr.

4. Conclusion

Les tests d'applications Flex se rapprochent fortement des tests de services web, car au fond le système Flex n'est rien d'autre qu'un service web basé sur AMF (au lieu des services SOAP que l'on retrouve souvent). Sans les possibilités de découverte des services et méthodes offertes par WSDL. Je pense avoir fait le tour des principaux soucis rencontrés durant le test de ce type d'application, même si le cas évoqué ici était un brin récalcitrant. D'ailleurs à ce jour, je n'ai pas trouvé de logiciel de décompilation permettant de décompiler ce satané SWF. Si quelqu'un a une idée (ou une révélation divine), me contacter via twitter ou via gmail.

24
févr.
'12

La face cachée des tickets RATP

Publié le 24 février 2012

Grand utilisateur de tickets de métro (émis par le STIF/RATP), je me suis posé la question un jour (oui, j'ai de ces idées pourries des fois) de savoir comment sont codées les informations sur ce support magnétique. L'idée m'a semblé intéressante à creuser, mais nécessitait un brin de bidouille électronique. L'occasion de tester mon nouveau fer à souder reçu à Noël, et de découvrir l'univers des bandes magnétiques et plus particulièrement celui des tickets de la RATP.

Les bandes magnétiques

J'ai fureté pendant des heures sur Internet, à la recherche de documentation sur la lecture des bandes magnétiques, ainsi que sur les normes de codage employées. J'ai déniché quelques papiers intéressants:

Ces trois ressources sont très intéressantes, le white paper du CCC étant celle qui m'a apporté le plus. J'en ai discuté au boulot avec Tixlegeek, et il m'a lui aussi soumis l'idée d'utiliser la carte son de mon ordinateur portable pour échantillonner les données stockées sur le ticket (à l'instar de ce qui est documenté dans le papier de Joseph Battaglia). Il s'agit là d'un hack archi-simple, vu qu'il suffit de trouver une tête de lecture d'un vieux lecteur cassette et de la souder sur un jack 3.5 puis de le brancher dans la prise micro. La carte son fera le reste.

En ce qui concerne le codage des données sur bande, celui-ci peut se faire de deux manières:

La première méthode est celle employée pour stocker des données audio, mais est peu robuste. La seconde permet de stocker de manière plus robuste, et indépendamment de la vitesse de lecture ou d'écriture, des données binaires. C'est celle qui est préférée dans la grande majorité des systèmes de stockage numériques sur bande.

Le codage de fréquence le plus classique a un joli surnom: F2F. Ou plus généralement connu sous le nom de Aiken Biphase. L'idée est de coder le 0 avec un signal de fréquence F, et le 1 avec un signal de fréquence 2F. L'avantage de ce codage, c'est que peu importe la vitesse de lecture (qui est variable selon les périphériques), le décodage reste fiable. Bon par contre ça complique un brin la phase de décodage. Patrick Gueulle explique très bien cela dans son livre "Carte magnétique et PC".

Traitement du signal

Le papier du CCC [2] présente deux programmes (nommés DAB et DMBS) visant à respectivement décoder le codage Aiken Biphase (F2F) et à extraire les données selon les normes ISO781X. Ces mêmes programmes ont été utilisés par K1wy dans [1]. Le souci c'est que ceux-ci ont été principalement conçus pour les cartes magnétiques rigides à trois pistes, pas forcément pour les tickets de métro. De plus, selon les réglages audio de l'ordinateur servant à la capture (Mic-Boost notamment). Il me fallait une solution plus malléable, car ces deux programmes sont écrits en C et pas forcément bien documentés.

J'ai trouvé plusieurs modules python permettant de réaliser la capture d'un signal sur l'entrée micro et le traitement de celui-ci:

A l'aide de ces modules, j'ai réalisé quelques scripts de capture de signal et de sauvegarde des données sous format PCM et WAV. l'avantage de pouvoir sauvegarder les captures audio est double: on peut facilement refaire des analyses et surtout on peut les lire avec des outils comme Audacity. J'ai donc réalisé un script de capture du signal, et un script de traitement visant à traiter le signal afin de mettre en évidence le codage fréquentiel.

J'ai réalisé les tests de capture à l'aide d'un micro, et utilisé pylab pour afficher l'allure du signal. Pour le coup, c'était assez efficace:

Il m'a fallut ensuite réaliser mon lecteur de bande magnétique à la manière du CCC, à l'aide d'un vieux lecteur de cassette audio, d'un fer à souder, d'un jack 3.5 et d'un cable IDE =). J'ai du sacrifier mon super lecteur CD/K7 de mes 18 ans (de toute façon je n'ai plus de K7), mais c'est pas grave.

Fabrication du lecteur de bande magnétique maison

Premièrement, j'ai démonté le lecteur pour pouvoir accéder à la partie électro-mécanique au niveau du lecteur de K7:

Une fois les têtes de lecture et d'écriture démontées, j'ai pu souder la tête de lecture (stéréo, mais on s'en moque) sur le jack 3,5mm. Bon la méthode de connection étant pas trop documentée, j'ai un peu galéré mais j'y suis arrivé. Pour information, si vous essayez de refaire le montage chez vous et que vous employez un jack 3,5mm stéréo, faites très attention au contact central: il délivre du 5V (cette tension est utile pour les microphones de type Electret). Pour ma part j'ai pas gazé car j'aurais du relier la sortie de la seconde bobine de lecture à la première, pour cumuler les tensions de sortie, alors que dans mon montage je l'ai mise à la masse (donc je n'utilise qu'une seule des deux bobines de lecture).

J'ai utilisé trois fils couplés d'un cable IDE, de manière à avoir de quoi manipuler la tête de lecture et l'éloigner de l'ordinateur portable (pour limiter les interférences). Bon au final ça n'a pas tellement aidé, le cable IDE faisant une merveilleuse antenne. Une meilleure solution serait d'utiliser un cable blindé à trois brins dont le blindage est mis à la masse. Une fois celui-ci connecté à mon ordinateur portable, j'ai pu balayer un ticket de métro et capturer avec mon précédent script le signal issu de la tête de lecture (signal brut).

Décodage du signal et analyse

A l'aide de ce lecteur de bande magnétique fait maison et de mes quelques scripts, j'ai codé un second script permettant de mettre en évidence les données codées sur la bande magnétique, et ça n'a pas été une paire de manche. J'ai réalisé trois décodages pour la forme:

Et voilà les données codées (Aiken Biphase, ou F2F) extraites des captures de signaux:

Le premier est issu d'un ticket neuf, et les deux suivants de deux tickets a priori validés à Saint Lazare (Paris). Il est flagrant de voir qu'un motif général est présent, motif que l'on retrouve en partie dans le ticket de métro neuf. Chose intrigante, le ticket de métro neuf possède moins d'information que les tickets compostés. J'en déduis donc que des données sont ajoutées lors de la validation, et qu'un ticket de métro neuf est codé d'une manière particulière.Si vous regardez bien, il y a de légères variations de période, mais cela est dû au fait que je scanne manuellement, et ma vitesse de balayage n'est pas constante.

J'ai donc entrepris de décoder les premiers bits significatifs communs aux trois tickets, et voici ce qui en ressort:

Ticket neuf:
11111111 00100 11111 11111 11111 0

Ticket #1:
11111111 00100 11111 00110 01001 0

Ticket #2:
11111111 00100 11111 00110 01110 1

Les espaces sont de mon fait ;). J'ai pu déduire le rôle de chacun (ou du moins tenter de deviner) à partir des informations décodées:

Si on regarde de plus près le codage, on peut voir que sur le premier ticket validé les troisième et quatrième séquences de 5 bits correspondent aux valeurs 69 et 6E. Ou plus précisemment aux valeurs 6 et 9, et 6 et 14. Cela ressemble bien aux codes de stations connus de Paris, tels que décrit sur Wikipédia. Cela signifie qu'un de mes tickets a été validé à Saint-Lazare même (code station 0609), l'autre à la station ayant pour code 0614 (inconnu sur Wikipédia oO). Le dernier bit que j'ai repéré semble être un bit de parité.

Le plus drôle dans l'histoire, c'est de voir que le ticket vierge n'a aucune valeur paramétrée, si ce n'est le type de ticket (première séquence de 5 bits). Donc facile à cloner.

Conclusion

La sécurité des tickets de métro ... heuu.... quelle sécurité ? Il est a priori aisé de dupliquer des tickets neufs (aucune limite dans le temps ni dans l'espace}, la seule limitation est technique (il faut un matériel particulier). De même, je n'ai pas détaillé ici le décodage complet mais la suite des bits stockés représentent très certainement la date de validation et l'heure de validation, mais je n'ai pas vraiment cherché à le décoder.

Heureusement qu'il y a le passe Navigo pour tous nous sauver (spéciale dédicace à Nono2357 ;).



Les contenus disponibles sur ce blog sont publiés sous licence Creative Commons BY-NC-SA.
Vous pouvez réutiliser tout ou partie de ces contenus à condition de citer l'auteur et l'origine, vous ne pouvez en faire une utilisation commerciale, et enfin vous devez partager tout travail ou œuvre dérivée sous les mêmes conditions — c'est-à-dire avec la même licence d'utilisation Creative Commons.