Aller au contenu principal

Attaquer n’importe quel serveur comme dans les films

Lorsqu’un film représente une personne attaquant un système informatique, on nous montre parfois quelqu’un tapant frénétiquement sur un clavier des commandes plus ou moins crédibles.

Si seulement il existait une commande shell pour faire n’importe quelle attaque, donc adaptée à n’importe quel scénario…
Par exemple :
$ attack --rf extraterrestres #Independance day
$ attack le_grand_mechant_caricatural #la liste ne rentrera jamais sur 80 colonnes

Malheureusement, on obtient un résultat de ce genre :
$ attack nsa
bash: attack : commande introuvable

Sur Ubuntu, un message d’erreur signalera que la commande est disponible une fois le paquet adéquat installé. Sur Debian, il faut installer le paquet command-not-found et mettre à jour pour avoir les suggestions. Sur une autre distribution, j’imagine que c’est aussi disponible mais j’ignore la procédure.
# aptitude install command-not-found
# update-command-not-found

Et là pouf, on progresse :
$ attack nsa
The program 'attack' is currently not installed. To run 'attack' please ask your administrator to install the package 'ladr4-apps'
attack: command not found

Le paquet ladr4-apps semble donc la solution magique, sauf qu’elle ne l’est pas. C’est un ensemble d’outils et de bibliothèques pour construire des preuves.

LADR (Library for Automated Deduction Research) is a library for
use in constructing theorem provers. Among other useful routines it
provides facilities for applying inference rules such as resolution
and paramodulation to clauses. LADR is used by the prover9 theorem
prover, and by the mace4 countermodel generator.

…Encore raté ! Il faudra encore continuer à copier/coller du code pour illustrer les aventures du héros. ;-)

Évolution comparée du nombre de bogues entre distribution Linux

Il est difficile de connaitre la vitalité d’une distribution Linux. Il est possible de s’en faire une idée en fonction du nombre de posts sur des forums, de recherche sur un moteur de recherche, du nombre de téléchargement ou la visibilité sur Distrowatch, etc. Regarder le nombre de bogues en est une autre. Après tout, on ne signale des bogues que sur les distributions (et les logiciels) que l’on utilise… C’est celle qu’utilise bubulle.

Le but de l’article est de visualiser l’évolution du nombre de bogues entre Arch Linux, Debian, Ubuntu et RedHat. Puis de poser des pistes sur les causes possibles. À la fin de l’article, des liens vers les scripts de récupération des données (en python, à l’aide des bibliothèques requests et beautifulSoup) et les représentations graphiques (en python, avec la bibliothèque matplotlib) sont fournis.

Résultats obtenus

En bogues cumulés

Bogues cumulés

ArchLinux a un score extrêmement faible (moins de 40.000 bogues) comparé aux autres distributions.
Je connais mal ArchLinux donc je resterai au stade des hypothèses :

  • Je n’ai pas regardé le bon bug tracker. J’ai utilisé les données fournies sur bugs.archlinux.org.
  • Arch aurait un mode de travail différent des 3 autres distributions (par exemple si les problèmes sont signalés directement au responsable du paquet).
  • Arch n’aurait quasiment pas de bogue. Mon expérience personnelle de cette distribution me fait exclure cette hypothèse.
  • Arch aurait finalement peu d’utilisateurs.

Lors de la deuxième moitié 2007, Redhat a une augmentation durable dont j’ignore l’origine. La sortie de RHEL 5 ne me semble pas être une explication très convaincante.

Ubuntu, dernière distribution créée, dépasse toute les autres en 2010. La croissance reste rapide au fil du temps.

Sur le long terme, la croissance de Debian est globalement la même. La croissance des rapports de bogues est plus faible pour Debian qu’Ubuntu. Quelques hypothèses, non exclusives entre elles :

  • Moins d’utilisateurs pour Debian qu’Ubuntu. Ubuntu ayant été créée après, elle aurait dépassée Debian au début 2007 (en supposant un nombre de rapports envoyés par utilisateur égal entre les deux distributions).
  • L’interface web d’Ubuntu faciliterait plus le signalement de bogues que l’utilisation d’e-mails pour communiquer avec le BTS utilisé par Debian
  • On m’a signalé que reportbug, l’outil de signalement de bogues de Debian, aurait lui-même eu des problèmes pendant un moment. Cependant je ne pense pas que ce soit une cause significative sur une période longue.
  • Il y aurait plus d’envois automatiques lors de plantage chez Ubuntu que chez Debian (par exemple lorsque les paquets n’arrivent pas à être reconstruit – FTBFS).

Variations de la croissance du nombre de bogues

Variations au fil du temps

On constate un ralentissement de l’augmentation ces dernières dernières années pour Ubuntu (depuis 2010) et Debian (depuis 2006-2007) et une accélération pour RedHat, au point de rattraper celle de la phase base d’Ubuntu. En effet, la croissance pour Ubuntu subit des cycles en fonctions des publications tous les 6 mois.

C’est le même phénomène que celui de Debian lors de la préparation d’une nouvelle version stable. Phénomène peu visible sur le graphique ci-dessus, il faut regarder un graphique limité aux bogues de Debian.
Concernant Debian, la mesure de l’usage faite avec PopCon montre une augmentation ; il serait intéressant de voir si l’usage ralentit parallèlement à celle des rapports de bogues.

Si la mesure en nombre de bogues est pertinente, la distribution qui monte est RedHat plutôt qu’Ubuntu ou Arch.

Limites de l’approche

L’estimation du nombre de bogues est basé sur les identifiant des bogues donc les résultats peuvent être approximatifs.

Quelques données semblaient aberrantes, elles ont été supprimées des résultats. Les voici :

distrb id_bug AAAA MM JJ
redhat 100213 2002 05 06
redhat 780001 2010 09 29
redhat 900001 2012 03 08
ubuntu 80001 2005 07 25

La comparaison des valeurs absolues entre distribution n’a pas forcément de sens car on ne mesure pas les mêmes choses. C’est d’autant plus vrai que les distributions sont éloignées. Les variations et l’évolution de la variation semble plus pertinente même si elles sont à prendre avec précaution vu les erreurs de mesure…

Méthode de récolte des données et traitement

La récolte des données a été faite avec un script python qui télécharge des pages sur les interfaces web des rapports de bogues (merci requests !) puis cherche la date de création de l’alerte (merci BeautifulSoup !).

Le script est téléchargeable : dl_stats.py.
La bibliothèque requests permet de simplifier la récupération du code html. Lorsqu’il y a une redirection, la bibiothèque suit le lien automatiquement pour obtenir le contenu. Par exemple, pour Ubuntu, l’adresse https://bugs.launchpad.net/bugs/308191 redirige automatiquement vers https://bugs.launchpad.net/ubuntu/+source/xf86-input-multitouch/+bug/308191. Il suffit donc de faire :

r = requests.get("https://bugs.launchpad.net/bugs/308191")

Les statistiques sont écrites sur la sortie standard et doivent être copiées dans des fichiers .txt, un par distribution. Les fichiers seront lus par le script de traçage des graphiques. Ce script utilise matplotlib et est disponible à http://stephane.yaal.fr/evolution-rapports-de-bogues/draw_stats.py. Les fichiers de statistiques utilisés sont disponibles dans le même répertoire (http://stephane.yaal.fr/evolution-rapports-de-bogues/).

Inspirations

La création des graphique est basée sur l’exemple de l’évolution du prix de l’essence disponible sur geophysique.be (évolution du prix de l’essence).

La documentation de matplolib pour personnaliser ces graphiques : pyplot.plot

Merci à Christian Perrier (bubulle) pour cette façon originale d’estimer la vitalité d’une distribution.

Merci aussi à Nirgal, Joey Hess et tout ceux avec qui j’en ai parlé à la DebConf 13 pour leurs réflexions.

Histogramme cumulé de la répartition des cartes du jeu The City avec matplotlib

The city est un jeu de cartes où chaque joueur pose des bâtiments, représentés par des cartes. Elles ont chacune un coût (compris entre 0 à 11), génère un revenu et des points de victoires. La plupart d’entre elles existent en plusieurs exemplaires. Certaines cartes possèdent aussi des symboles (fontaine, caddie et voiture) qui influencent le revenu ou les points gagnés.

Une carte du jeu

La répartition de ces symboles n’est pas uniforme comme on peut le constater sur l’histogramme suivant :
Répartition des cartes

Le graphique a été réalisé avec matplotlib, une bibilothèque python pour créer des graphiques 2D ou 3D. La façon de le réaliser sera abordée dans une seconde partie, la première étant consacrée à quelques remarques mise en évidence par le graphique.

Interprétation et limitation

Quelques faits mis en lumière par l’histogramme :

  • Plus les cartes coûtent cher, plus elles sont rares. Les cartes les plus courantes sont souvent en plusieurs exemplaires alors que les dernières sont uniques. Cependant certaines des moins onéreuses ne peuvent être joués qu’en un seul exemplaire.
  • Une stratégie Fontaine-Voiture semble plus difficile que Fontaine-Caddie ou Caddie-Voiture car le nombre de cartes ayant les deux couleurs ensemble est bien plus faible.
  • La carte Caddie la plus forte vaut 7, alors que pour les deux autres couleurs c’est 11. Plus coûteux, mais plus puissant…

Le graphique a aussi des limites :

  • La stratégie sans couleur ne semble pas pertinente alors que les Villas (sans couleur) valent 4 et ont une très bonne synergie entre elles.
  • Le graphique compte le nombre de cartes, mais certaines cartes de la moitié supérieure peuvent avoir plusieurs fois le même symboles (maximum trois). Le graphique donne donc une idée de la probabilité d’obtenir une carte avec le symbole donné, pas la probabilité du nombre de symboles obtenus.

Réalisation

L’interface de programmation ressemble à Pychart, une autre bibliothèque python pour faire des graphiques, présentée dans un article précédent. (Entre les deux, préférez matplotlib. (Oui, j’aime bien les parenthèses. (Pas vous ?)))

Par défaut, la sortie du graphique est dans une fenêtre de l’environnement de bureau. C’est pratique lorsqu’on fait des tests avec l’interpréteur python. C’est prévu pour, il existe même une option pour avoir directement matplotlib chargé au démarrage avec ipython (ipython --pylab).
Il est possible de changer la sortie au démarrage du script, à condition de le faire avant d’importer pyplot.
Pour avoir une sortie en PNG :

import matplotlib
matplotlib.use('Agg') # au début du script
from matplotlib import pyplot as plt

# ...
# plein de code malin et très lisible
# ...

plt.savefig("/tmp/thecity-histogramme.png")

Les données sont fournies dans un n-uplet représentant la distribution d’une valeur sur l’ensemble des barres. Par exemple, pour représenter la combinaison Fontaine-Voiture-Caddie (notée BVO) :

BVO = (3, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0) # répartition de la caractéristique BVO
p1 = plt.bar(index, BVO, WIDTH, color="#D70751") # représentation dans le graphique

C’est comme si on empilait les couches de données les unes au-dessus des autres. Pour les deuxièmes caractéristiques suivantes, il faut ajouter un paramètre bottom qui fournit la hauteur de départ pour la nouvelle couche.

p2 = plt.bar(index,
             BO,
             WIDTH,
             color="#D217F4",
             bottom=BVO)

Comme la hauteur sera différente pour chaque valeur, j’ai ajoutée une fonction qui fait la somme des listes pour la troisième couche et supérieures. C’est du code Python générique, donc utilisable dans n’importe quel autre contexte :

import itertools
def sum_cards(cards):
    sum_cards_by_value = lambda x, y: map(sum, itertools.izip(x, y))
    return reduce(sum_cards_by_value, cards)

Comportement :

In [4]: sum_cards([[1, 2, 3], [10, 20, 30]])
Out[4]: [11, 22, 33]

In [5]: sum_cards([[1, 2, 3], [10, 20, 30], [100, 200, 300]])
Out[5]: [111, 222, 333]

Le reste du code reste compréhensible simplement en le lisant ; il est fourni en fin de l’article.

À partir de matplotlib 1.2.0, un graphique pour des données continues est aussi disponible. Il est nommé stackplot (voir l’exemple fourni par matplotlib).

Sources

La boîte de jeu, amenée par Arthur, avec laquelle on joue dans la boîte.

Prise en main de matplotlib

Un exemple d’histogramme fourni par matplotlib

Script de génération de l’histogramme :

#! /usr/bin/env python
# -*- encoding: utf-8  -*-

import itertools
import matplotlib
matplotlib.use('Agg')

import numpy as np

from matplotlib import pyplot as plt


BVO = (3, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0)
BO  = (0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0)
BV  = (0, 4, 2, 5, 0, 0, 0, 1, 0, 0, 0, 0)
VO  = (0, 7, 5, 0, 2, 0, 1, 0, 1, 1, 0, 0)
B   = (0, 7, 0, 4, 1, 3, 2, 1, 3, 1, 0, 1)
V   = (0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0)
O   = (0, 0, 2, 3, 3, 0, 1, 0, 1, 0, 0, 1)
no  = (0, 13, 11, 0, 8, 0, 0, 0, 0, 0, 0, 1)

WIDTH = 0.55

def max_sum():
    return max([
        BVO[i] + BO[i] + BV[i] + VO[i] + B[i] + V[i] + O[i] + no[i]
        for i
        in range(data_length())])

def data_length():
    return len(BVO)

def higher_card_cost():
    return data_length() - 1


index = np.arange(data_length())

def sum_cards(cards):
    sum_cards_by_value = lambda x, y: map(sum, itertools.izip(x, y))
    return reduce(sum_cards_by_value, cards)


p1 = plt.bar(index, BVO, WIDTH, color="#D70751")
p2 = plt.bar(index, BO, WIDTH, color="#D217F4", bottom=BVO)
p3 = plt.bar(index, BV, WIDTH, color="#0ED5D3", bottom=sum_cards([BVO, BO]))
p4 = plt.bar(index, VO, WIDTH, color="#E7F417", bottom=sum_cards([BVO, BO, BV]))
p5 = plt.bar(index, B, WIDTH, color="#0101f3", bottom=sum_cards([BVO, BO, BV, VO]))
p6 = plt.bar(index, V, WIDTH, color="#2fc62b", bottom=sum_cards([BVO, BO, BV, VO, B]))
p7 = plt.bar(index, O, WIDTH, color="#F3B439", bottom=sum_cards([BVO, BO, BV, VO, B,  V]))
p8 = plt.bar(index, no, WIDTH, color="#999999", bottom=sum_cards([BVO, BO, BV, VO, B, V, O]))


plt.ylabel(u"Quantité")
plt.xlabel(u"Coût")
plt.title("Distribution des cartes de The City")
plt.xticks(index+WIDTH/2., [str(i) for i in range(data_length())])
plt.yticks(np.arange(0, (max_sum() + 1), 10))
plt.legend((p1[0], p2[0], p3[0], p4[0], p5[0], p6[0], p7[0], p8[0]),
           ('Fontaine, Caddie, Voiture (BVO)', 'Fontaine, Voiture (BO)',"Fontaine, Caddie (BV)", "Caddie, Voiture (VO)", "Fontaine (B)", "Caddie (V)", "Voiture (O)", "Aucune couleur"))

plt.savefig("/tmp/thecity-histogramme.png")

DebConf sur la planète

Le terme DebConf est une abréviation pour Debian Conference. Ce sont des conférences annuelles qui se déroulent à chaque fois dans une ville différente. La DebConf13, conférence ayant le numéro 13 mais qui est la quatorzième parce qu’il y a eu une conférence numéro 0, commence bientôt (le matin du dimanche 11 août 2013) et je vais leur infliger ma présence ! :)
Je vais à la DebConf13

Les précédentes DebConf ont eu lieu en Europe, en Amérique du Nord, Centrale et Sud. L’Afrique et l’Asie n’ont jamais accueilli une de ces conférences mais le nombre de développeurs Debian y est bien plus faible.
Voici l’emplacement des différentes DebConf sur un planisphère :
Les DebConf sur la planète

En rouge, les DebConf passées.
En blanc celle qui est imminente et à Vaumarcus, en Suisse.
En jaune, celle qui sera faite l’année prochaine et à Portland, aux États-Unis.

Réalisation de la carte

La carte a été réalisée avec xplanet, installable avec le paquet éponyme. Éponyme signifiant « du même nom », faites « apt-get install xplanet », pas « apt-get install eponyme ».

commande utilisée

xplanet -output debconf_planet.png -geometry 1024x512 -projection Mercator -config debconf_planet.conf -num_times 1

…qui permet d’écrire une image nommée debconf_planet.png, de 1024 pixels sur 512. La projection de la carte est celle de Mercator. Par défaut, on n’a qu’une vue partielle de la Terre (celle que l’on verrait de l’espace). Des éléments de configuration sont dans un fichier nommé debconf_planet.conf et la commande ne sera exécutée qu’une seule fois. Par défaut, xplanet tourne en boucle et rafraichit l’image régulièrement. Cela permet, par exemple, de mettre à jour un économiseur d’écran basé sur xplanet.

debconf_planet.conf

[earth]
shade=100
marker_file=coords.txt
marker_fontsize=15

shade vaut 100, ce qui permet d’ignorer l’effet jour/nuit qui est appliqué par défaut. coords.txt contient les latitudes et longitudes des points à afficher sur la carte.

coords.txt

+44.80 +0.58 "0&1" #Bordeaux, France
+43.65 -79.38 "2" #Toronto, Canada
+59.92 +10.75 "3" #Oslo, Norway
-29.99 -51.22 "4" #Porto Alegre, Brazil
+60.22 +24.66 "5" #Espoo, Finland
+18.91 -98.97 "6" #Oaxtepec, Mexico 
+55.96 -3.19 "7" #Edinburgh, Scotland
-37.96 -57.59 "8" #Mar del Plata, Argentina
+39.60 -6.08 "9" #Extremadura, Spain
+40.74 -74.00 "10" #New York City, USA
+44.78 +17.21 "11" #Banja Luka, Republika Srpska, Bosnia and Herzegovina
+12.14 -86.25 "12" #Managua, Nicaragua
+46.87 +6.75 "13" color=white #Le Camp, Vaumarcus, Switzerland
+45.53 -122.67 "14" color=yellow #Portland, Oregon, USA

Références

Les dates et lieux des conférences passées et présente sont listées sur la page
https://wiki.debian.org/DebConf.
L’annonce de la conférence 2014 qui se tiendra à Portland a été faite par le responsable du projet Debian.

xplanet dispose de nombreuses autres options. En plus de la page de manuel, plusieurs README sont disponibles dans les répertoires contenus dans /usr/share/xplanet/.
Un exemple de ce qu’il est aussi possible de faire : http://en.wikipedia.org/wiki/Wikipedia:Producing_maps_with_xplanet

Modifier une image BMP avec Construct (et sans PIL)

Le module le plus utilisé pour manipuler des images en Python est probablement PIL (ou Pillow pour les utilisateurs de pypi). Construct est un module pour analyser et contruire des fichiers binaires. La définition des fichiers binaires doit être faite pour chaque format que l’on souhaite traiter. Cependant, certains formats de fichiers binaires sont déjà définis, comme les formats elf, ext2, bmp, gif, etc. (« etc » n’étant pas un format binaire mais juste l’abréviation de et caetera.) Les objectifs des deux modules sont différents donc il ne faut pas espérer remplacer l’un par l’autre.

L’objectif de l’article est faire une rapide présentation de Construct en changeant la couleur de certains pixels d’une image au format .bmp.

Remplacement des pixels blancs par des pixels noirs

Remplacement des pixels blancs par des pixels noirs

Installer Construct

Pour contruire un virtualenv et installer construct :

$ virtualenv democonstruct
$ ./democonstruct/bin/pip install construct

Lire un fichier

La méthode de base est parse() :

import construct.formats.graphics.bmp
with open("/tmp/python_fond_blanc.bmp") as f:
    content = f.read()
obj = construct.formats.graphics.bmp.bitmap_file.parse(content)

Il existe aussi une méthode plus courte pour faire l’équivalent :

obj = construct.formats.graphics.bmp.bitmap_file.parse_stream(open("/tmp/python_fond_blanc.bmp"))

L’objet analysé dispose en attributs des données extraites du fichier :

Container({'palette': <construct.lib.container.LazyContainer object at 0xe23fc8>,
           'vertical_dpi': 2835,
           'image_data_size': 20160,
           'compression': 'Uncompressed',
           'planes': 1,
           'bpp': 24,
           'signature': 'BM',
           'important_colors': 0,
           'header_size': 40,
           'height': 80,
           'width': 84,
           'version': 'v3',
           'data_offset': 54,
           'file_size': 20214,
           'number_of_pixels': 6720,
           'horizontal_dpi': 2835,
           'pixels': <construct.lib.container.LazyContainer object at 0xdea7e0>,
           'colors_used': 0})

Ici, le fichier est encodé selon la version 3 du format, fait 84 pixels de large et 80 pixels de hauts, etc. Par contre, les pixels ne sont pas directement accessibles mais le sont par l’intermédiaire d’un LazyContainer, un conteneur paresseux dans le but de ne pas charger en mémoire des données qui pourraient être énormes (par exemple une photo à taille réelle d’une grosse baleine en surpoid).
Pour accéder aux pixels, il suffit de faire :

>>> obj.pixels
<construct.lib.container.LazyContainer object at 0x287c7e0>
>>> obj.pixels.value
[[[255, 255, 255], [255, 255, 255], #ça continue encore et encore...
...                                 #c'est que le début, d'accord, d'accord
, [255, 255, 255], [255, 255, 255], [255, 255, 255]]]

obj.pixels.value est une liste de lignes. Chaque ligne est une liste de pixels. Chaque pixel est une liste de 3 entiers compris entre 0 et 255 (rouge, vert et bleu codées sur un octet).

Modifier un fichier

Il est possible de construire un fichier binaire en passant un objet en paramètre de la méthode build(). L’objet peut être défini complètement ou obtenu en modifiant un objet existant préalablement. Dans l’exemple ci-dessous, on va changer les pixels blancs (défini par [255, 255, 255]) en noirs (défini par [0, 0, 0]).

def blanc_vers_noir(src_pixels):
    BLANC = [255, 255, 255]
    NOIR = [0, 0, 0]
    dst_pixels = []
    for index_ligne, ligne in enumerate(src_pixels):
        dst_pixels.append([])
        for pixel in ligne:
            if pixel == BLANC:
                dst_pixels[index_ligne].append(NOIR)
            else:
                dst_pixels[index_ligne].append(pixel)
    return dst_pixels

conversion = blanc_vers_noir(obj.pixels.value)
obj.pixels = conversion

with open("/tmp/python_fond_noir.bmp", "w") as f:
    f.write(construct.formats.graphics.bmp.bitmap_file.build(obj))

La nouvelle image est enregistrée avec les pixels transformés. À noter que les yeux ont aussi été convertis et que le détourage du logo est mal fait car la valeur n’est pas celle d’un blanc pur.

Utilité et limites de Construct

S’il s’agit de modifier des images, PIL fait probablement mieux l’affaire car il s’agit d’une bibliothèque de plus haut niveau (par exemple, et en toute objectivité, le meilleur exemple du monde). La seule utilité que j’y vois serait pour générer automatiquement des dégradés ou des figures géométriques simples.

D’un point de vue plus général, Construct permet de décrire par une structure Python un format de données binaires pour naviguer et modifier plus facilement des fichiers. L’utilisation qui en est faite ici est surtout à but de démonstration. S’il est possible de s’en servir pour naviguer dans des fichiers texte, d’autres bibliothèques seront plus adaptées (BeautifulSoup ou lxml pour du HTML ou XML).

Construct devrait surtout servir à décrire les fichiers binaires à manipuler dans une application. C’est faisable en créant une nouvelle classe décrivant la structure binaire du fichier. Les méthodes parse() et build() sont alors disponible automatiquement pour la nouvelle classe.