Le blog de numerunique

Les limites de l'open source
03/09/2018

ATTENTION : cet article est destiné aux informaticiens ou, à la rigueur, aux personnes vraiment très curieuses à propos de la lutte contre les pirates.

Cette histoire commence par l'observation de messages inhabituels dans les journaux système :

Aug 31 08:43:31 host sshd[14231]: dispatch_protocol_error: type 90 seq 5 [preauth]
Aug 31 08:44:01 host sshd[14231]: dispatch_protocol_error: type 96 seq 6 [preauth]
Aug 31 08:44:01 host sshd[14231]: dispatch_protocol_error: type 97 seq 7 [preauth]

En première approche, l'instinct d'administrateur système suggère d'ignorer ces messages. Ce sera effectivement la conclusion de l'analyse qu'ils motivèrent. Si vous êtes là juste pour savoir ce qu'il faut en faire, la lecture de la suite est optionnelle.

Un 31 août c'est encore un peu les vacances. On a donc un peu plus de temps et on aimerait bien savoir à quoi correspondent ces mystérieux "type 90, 96 ou 97" pour lesquels il y a une "dispatch_protocol_error".

A l'intention du profane, il est utile de préciser que ces messages d'erreur émanent du démon sshd. Pour faire simple, un démon est un processus système qui tourne en tâche de fond. Et le processus sshd est celui qui gère les connexions à distance. Dans le contexte de l'administration de serveur c'est à la fois un dispositif essentiel et incontournable et, en conséquence, la principale cible des pirates.

Une recherche méthodique sur Internet à propos de ces mystérieux messages d'erreur n'aboutit à aucun résultat probant. Il faut donc aller à la source de l'information : les sources d'OpenSSH.

On obtient ainsi plus de 740 fichiers qui totalisent plus de 200 000 lignes de code... la recherche de l'information convoitée est un véritable jeu de piste.

dispatch.c

L'instruction qui produit le message d'erreur "dispatch_protocol_error: type t seq n" est facile à trouver dans la fonction dispatch_protocol_error().

auth2.c

Cependant, cette fonction n'est appelée nulle part directement mais un pointeur sur cette fonction est utilisée dans l'instruction ssh_dispatch_init(ssh, &dispatch_protocol_error).

dispatch.c

En examinant de plus près ce que la fonction ssh_dispatch_init() fait de son paramètre dflt on découvre que le pointeur sur la fonction responsable des mystérieux messages est mémorisé dans la structure ssh->dispatch[i].

packet.c

En cherchant l'usage de cette information on apprend que l'appel de la fonction se fait dans ssh_dispatch_run() par l'instruction (*ssh->dispatch[type])(type, seqnr, ssh). A ce stade type est déjà défini auparavant dans la même fonction par l'une des instruction ssh_packet_read_seqnr(ssh, &type, &seqnr) ou ssh_packet_read_poll_seqnr(ssh, &type, &seqnr).

dispatch.c

En étudiant le code de ces deux fonctions on en déduit que le fameux type est défini dans la fonction ssh_packet_read_poll2(). Il y est d'abord initialisé à la valeur SSH_MSG_NONE puis renseigné par sshbuf_get_u8(state->incoming_packet, typep).

ssh2.h

Ah, là on tient un indice intéressant : SSH_MSG_NONE qui mène à la mine d'informations recherchée :

/* channel related messages */

#define SSH2_MSG_CHANNEL_OPEN              90
#define SSH2_MSG_CHANNEL_OPEN_CONFIRMATION 91
#define SSH2_MSG_CHANNEL_OPEN_FAILURE      92
#define SSH2_MSG_CHANNEL_WINDOW_ADJUST     93
#define SSH2_MSG_CHANNEL_DATA              94
#define SSH2_MSG_CHANNEL_EXTENDED_DATA     95
#define SSH2_MSG_CHANNEL_EOF               96
#define SSH2_MSG_CHANNEL_CLOSE             97
#define SSH2_MSG_CHANNEL_REQUEST           98
#define SSH2_MSG_CHANNEL_SUCCESS           99
#define SSH2_MSG_CHANNEL_FAILURE           100

Donc ces mystérieux "type 90, 96 ou 97" pour lesquels il y a une "dispatch_protocol_error" correspondent à :

SSH2_MSG_CHANNEL_OPEN
SSH2_MSG_CHANNEL_EOF
SSH2_MSG_CHANNEL_CLOSE

Et là c'est beaucoup plus clair !

Il s'agit vraisemblablement de tentatives de connexion faites avec des paquets incohérents. Une telle approche évoque une technique similaire à celle mise en œuvre pour exploiter la faille CVE-2018-15473 : on génère des erreurs volontairement pour glaner indirectement des informations sur la cible.

Pour ce qui concerne les dispositions à prendre pour contrer une nouvelle forme d'attaque, il n'y a rien de plus à faire pour consolider les défenses du serveur que ce qui est déjà fait.

Tout ce qui précède n'est en fait qu'un préambule au véritable sujet de cet article : une réflexion sur l'open source.

Tout d'abord cela illustre directement la réflexion de l'article "WHAT IS BETTER THAN OPEN SOURCE?" : une documentation concise serait bien plus utile en complément des fichiers sources ; elle permettrait d'éviter un reverse engineering laborieux pour décoder les sens cachés de message d'erreur.

Mais surtout, l'analyse faite à l'occasion de cette recherche sur un élément essentiel et crucial des serveurs exposés sur Internet donne le frisson.

On y découvre un style de programmation artificiellement compliqué. Par exemple : mais pourquoi diable passer par un pointeur sur une fonction laborieusement stockée en de multiples exemplaires plutôt que d'appeler tout simplement la fonction elle-même ? Le reste est à l'avenant, avec notamment un recours avide aux pointeurs, une fonctionnalité très puissante lorsqu'elle est bien employée mais une source redoutable d'erreurs.

Cela a sans doute peu d'impact sur les performances mais aboutit à un code difficile à comprendre et donc à la fois propice aux bugs et difficile à maintenir ;  un véritable terreau pour les failles de sécurité et un terrain très favorable aux portes dérobées.

On aboutit au résultat opposé à celui recherché par l'open source !


Précédent | Suivant