Sygus.net

Aller au contenu | Aller au menu | Aller à la recherche

Mot-clé - mémoire

Fil des billets - Fil des commentaires

samedi, juin 12 2010

SSTIC 2010 : quelle orientation pour la sécurité ?

Le SSTIC 2010 s'est terminé sur une remarque intéressante concernant la tendance qu'à la communauté de la sécurité à privilégier la recherche de vulnérabilité au détriment de l'élaboration de systèmes réellement sécurisés. Je pense qu'il faut distinguer deux choses :

  • la vision court terme : l'analyse de sécurité d'un système spécifique (site web, applications, COTS, etc.) et la nécessité, pour faire bouger les choses, de démontrer que des vulnérabilités sont effectivement exploitables ;
  • la vision moyen/long terme : la nécessité de créer des cadres sécurisés dans lesquels des erreurs de développement ou d'utilisation ne mettent pas en péril la sécurité des systèmes.

Je crois moyennement à l'éducation des utilisateurs et des développeurs aux bonnes pratiques de sécurité (hormis à quelques règles de base, et encore...). Même après de nombreuses séances de sensibilisation, de formation, ou de démonstration, par l'exemple, de l'existence de vulnérabilités, il restera toujours quelques personnes qui coderont avec les pieds, introduisant alors une faille dans le système. Bref, l'éducation de 100% des gens aux bonnes pratiques me semble illusoire comme objectif et comme horizon pour que la situation s'améliore significativement.

Il faut plutôt considérer les utilisateurs et développeurs d'applications comme des sources potentielles de vulnérabilités. Je pense donc que le petit monde de la sécurité doit avant tout s'atteler à créer des cadres dans lesquels les utilisateurs et les développeurs peuvent se tromper, faire des erreurs, sans mettre en péril la sécurité des systèmes. Autrement dit, créer des systèmes tolérants aux failles et créer des environnements de développement empêchant de coder avec les pieds. Les différents guides de bonnes pratiques et de recommandations sont biensur intéressants et nécessaires. Cependant, un vrai environnement de développement sécurisé devrait être capable d'imposer de lui-même ces règles. La porte dérobée introduite involontairement dans le challenge SSTIC est un exemple typique d'erreur de développement. Un environnement de développement sécurisé devrait être capable de détecter, par exemple, de telles mauvaises utilisations d'API crypto.

Pour des systèmes tolérants aux failles, il s'agit notamment d'atténuer le risque d'exploitation. Voici quelques exemples de mécanismes ou de pistes qu'il est pourtant possible de mettre en place pour tenter de se protéger des programmes vulnérables :

  • activer les différents mécanismes qui rendent difficiles les exploitations en mémoire (bit NX, DEP, PaX, ASLR, canaris, UDEREF, etc.) ;
  • mettre en place ces mêmes mécanismes au niveau noyau ;
  • sandboxer les processus (chroot, SECCOMP) et les drivers (avec l'IOMMU) ;
  • limiter les droits des processus (capabilities sous Linux, etc.) ;
  • sortir des systèmes monolithiques (dont la moindre faille dans un driver ou une sous-partie obscure du noyau expose l'ensemble du noyau), pour utiliser des OS à base de micro-noyau (L4, Hurd, Coyotos, etc.) combinant les principes de moindre privilège et de minimalité ;
  • etc., etc. ;
  • d'autres mécanismes restent encore à imaginer !

Il existe des OS ou des prototypes d'OS durcis (Qubes, Gentoo Hardened, SeL4, etc.). Ces exemples doivent devenir la norme dans les futurs déploiements ou devenir la base de travail pour les futurs OS opérationnels.

mercredi, juin 3 2009

La mémoire sous Linux : analyse de /dev/mem

Le système de fichiers /dev sous Linux offre un moyen élégant d'accéder directement aux périphériques d'une machine, et notamment à sa mémoire physique par l'intermédiaire de /dev/mem. L'objectif de ce billet est de présenter le fonctionnement de /dev/mem sur un Linux 2.6.26-2 tournant sur une architecture x86 32 bits, et de voir quelles sont les différentes restrictions d'accès.

/dev/mem offre un accès direct à la mémoire physique du système (mémoire RAM, mémoire graphique, BIOS, etc.). L'analyse de ce fichier peut être facilitée par la lecture d'un autre fichier spécial, /proc/iomem, qui présente le mapping des périphériques au sein de la mémoire physique. Le mapping sur le système étudié est le suivant :

00000000-0009fbff : System RAM
0009fc00-0009ffff : reserved
000a0000-000bffff : Video RAM area
000c0000-000cffff : Video ROM
000e0000-000e17ff : Adapter ROM
000f0000-000fffff : System ROM
00100000-3ffeffff : System RAM
00100000-002ba4ea : Kernel code
002ba4eb-0037661f : Kernel data
003bc000-0041f57f : Kernel bss
...

Autrement dit, le BIOS est accessible dans l'espace d'adressage physique 0x000f0000-0x000fffff, la mémoire graphique est mappée dans 0x000a0000-0x000cffff, et la mémoire RAM, mappée à partir de 16 Mo (c'est à dire dans la ZONE_NORMAL et dans la ZONE_HIGHMEM : voir cet ancien article), est atteignable entre 0x00100000 et 0x3ffeffff. Ce dernier espace fait 1 Go. Ca tombe bien, c'est précisément la taille de la mémoire RAM de la machine :

$ cat /proc/meminfo 
MemTotal:      1036084 kB
...

/proc/iomem nous informe également de l'emplacement du code du noyau et de ses segments de données .data et .bss. Les éléments du noyau (code et données) sont donc directement accessibles en lecture et en écriture par /dev/mem. Biensur, n'importe qui ne peut pas lire ce périphérique : il faut soit être root, soit donner la bonne capability à un processus non-root. Ce dernier cas de figure est typiquement adapté pour le serveur graphique X, qui dépend de /dev/mem pour récupérer des informations liées à la carte graphique, et dont son exécution avec les privilèges root n'est pas nécessaire (hormis pour l'accès à dev/mem). Il suffit de lui donner la capability CAP_SYS_RAWIO, qui autorise, notamment, l'accès à /dev/mem. Ensuite, il faut placer l'utilisateur exécutant le serveur X dans le groupe kmem. L'exemple suivant illustre la mise en place d'une capability propre à l'exécutable dd, lui permettant de lire dans /dev/mem sans la nécessité d'être exécuté en root.

$ cp /bin/dd /tmp/dd
$ sudo setcap cap_sys_rawio=ep /tmp/dd
$ cat /etc/group
...
kmem:x:15:sygus
...
$ /tmp/dd if=/dev/mem of=/tmp/mem.dump 
1835008+0 enregistrements lus
1835008+0 enregistrements écrits
939524096 bytes (940 MB) copied, 17,7379 s, 53,0 MB/s

L'accès à l'intégralité de la mémoire n'est néanmoins pas pertinent d'un point de vue sécurité : la compromission d'un processus root ou d'un processus ayant la capability CAP_SYS_RAWIO peut permettre à un attaquant d'accéder directement à tout le noyau. Il est heureusement possible de configurer Linux pour limiter l'accès à /dev/mem uniquement au premier Mo de mémoire physique et seulement aux pages mémoire ne correspondant pas à de la RAM (ce qui est suffisant pour faire tourner X). C'est d'ailleurs le comportement par défaut sur Ubuntu, mais pas sur Debian Lenny. La restriction d'accès au premier Mo s'active avec l'option CONFIG_NONPROMISC_DEVMEM introduite sur le 2.6.26 (ou CONFIG_STRICT_DEVMEM sur les versions de Linux depuis le 2.6.27) lors de la compilation du noyau. Voici le résultat d'une tentative de lecture de l'ensemble de /dev/mem :

$ sudo dd if=/dev/mem of=/tmp/mem2.dump 
dd: reading `/dev/mem': Operation not permitted
2056+0 records in
2056+0 records out
1052672 bytes (1.1 MB) copied, 0.149356 seconds, 7.0 MB/s

Hop, bloqué au premier Mo... Ou plus exactement au bout de 256+1 pages mémoires ; la dernière page mémoire étant considérée comme appartenant au BIOS par certains systèmes.

Il existe d'autres mécanismes restreignant l'accès à /dev/mem. Citons le patch GrSecurity qui intègre une protection de l'accès à /dev/mem : il empêche l'accès en écriture sur l'ensemble de la mémoire physique. GrSecurity réalise cependant une exception pour le processus correspondant au serveur X.

Bref, bien que /dev/mem ait longtemps été considéré comme une trou de sécurité important, il est possible de limiter de manière importante ses droits d'accès à la mémoire système (option CONFIG_STRICT_DEVMEM du noyau), ou de limiter l'accès en écriture (patch GrSecurity).

dimanche, juin 8 2008

La mémoire sous Linux : cartographie de la mémoire

La version 2.6.25 du noyau Linux a vu l'apparition d'une série de patchs écrits par Matt Mackall, qui permettent de connaitre précisément le mapping entre les pages virtuelles et les pages physiques, et tout ceci au niveau userland. Cela se fait par l'intermédiaire des fichiers spéciaux /proc/PID/pagemap, /proc/kpagecount et /proc/kpageflags. Le premier indique les adresses des pages physiques utilisées par un processus précis. Le second montre, pour chaque page physique, le nombre de pages virtuelles qui pointent dessus. En effet, la pagination permet de faire correspondre à une même page physique plusieurs page virtuelles appartenant à des processus différents. Enfin, /proc/kpageflags permet de connaitre le statut d'une page mémoire, qui peut être une combinaison des flags suivants :

#define KPF_LOCKED     0
#define KPF_ERROR      1
#define KPF_REFERENCED 2
#define KPF_UPTODATE   3
#define KPF_DIRTY      4
#define KPF_LRU        5
#define KPF_ACTIVE     6
#define KPF_SLAB       7
#define KPF_WRITEBACK  8
#define KPF_RECLAIM    9
#define KPF_BUDDY     10

Avec toutes ces infos, il est possible d'établir une cartographie de l'utilisation de la mémoire vive. C'est ce que fait l'outil Pagemap-Demo, développé par le même auteur que ces patchs. Le graphique suivant montre la mémoire virtuelle d'un processus (en l'occurence, firefox). On peut voir le segment de code (le ptit truc tout en haut à gauche), ainsi que le tas (le rectangle du haut), le pile et les différentes librairies (en bas). La correspondance avec le nom des segments est faite en lisant les informations délivrées par le fichier /proc/PID/maps. Il suffit donc de survoler un segment pour connaitre à quoi il correspond et sur quelle adresse nous sommes.

/proc/PID/pagemap

Le graphique suivant représente le même processus, firefox, mais après avoir ouvert de nombreux sites web. Le jaune indique les pages les plus récemment ajoutées.

/proc/PID/pagemap

Maintenant, on passe à l'affichage d'une cartographie de la mémoire physique :

/proc/kpagecount

Enfin, on peut connaitre clairement comment la mémoire est utilisée :

/proc/kpageflags

  • En blanc : les objets utilisés par l'allocateur slab au sein du noyau.
  • En rose : les pages ayant le flag dirty activé.
  • En bleu : les pages utilisées par l'algorithme LRU (Least Recently Used).

Le patch permet également de connaitre la quantité de mémoire physique partagée entre différents processus ainsi que la mémoire physique utilisée uniquement par un processus. Ceci permet d'obtenir une nouvelle valeur, le PSS (Proportional Set Size), qui indique le nombre de pages mémoires divisé par le nombre de processus qui l'utilisent. Une information pertinente pour évaluer la quantité de mémoire utilisée par un processus.

vendredi, avril 25 2008

La mémoire sous Linux : analyse du fichier /proc/meminfo

Dans cet article, je vais tenter d'expliquer les différentes informations que le noyau Linux nous donne au travers du fichier /proc/meminfo. Comme support, je vais afficher les données qui concernent mon système. Il s'agit d'un noyau 2.6.24-1 dans une Debian unstable. Ma machine repose sur une architecure x86 en 32 bits et contient une barrette de 1 Go de mémoire RAM.

$ cat meminfo 
MemTotal:      1036256 kB
MemFree:        191256 kB
Buffers:         51824 kB
Cached:         468392 kB
SwapCached:           0 kB
Active:         398852 kB
Inactive:       404108 kB
HighTotal:      131008 kB
HighFree:          256 kB
LowTotal:       905248 kB
LowFree:        191000 kB
SwapTotal:      524280 kB
SwapFree:       524280 kB
Dirty:              44 kB
Writeback:           0 kB
AnonPages:      282736 kB
Mapped:          82108 kB
Slab:            22552 kB
SReclaimable:    14468 kB
SUnreclaim:       8084 kB
PageTables:       2232 kB
NFS_Unstable:        0 kB
Bounce:              0 kB
CommitLimit:   1042408 kB
Committed_AS:   880596 kB
VmallocTotal:   114680 kB
VmallocUsed:      7540 kB
VmallocChunk:   105972 kB
HugePages_Total:     0
HugePages_Free:      0
HugePages_Rsvd:      0
HugePages_Surp:      0
Hugepagesize:     4096 kB

Commençons par les premières lignes :

  • MemTotal : la quantité de mémoire vive utilisable (ma barrette fait 1 Go (1 073 742 Ko), mais il y a quelques Mo réquisitionnés par le noyau (visiblement 12 615 Ko)).
  • MemFree :la quantité de mémoire vive actuellement non allouée.
  • Buffers : la quantité de mémoire vive actuellement allouée dans des buffers.
  • Cached : la quantité de mémoire actuellement utilisée en tant que cache (par exemple, des caches pour les transferts vers et depuis le disque dur).
  • SwapCached : la quantité de mémoire actuellement sortie du swap, mais dont une copie est toujours présente dedans.
  • Active : la quantité de mémoire vive récemment utilisée.
  • Inactive : la quantité de mémoire qui n'a pas été utilisée depuis un certain temps (le noyau pioche d'abord dans ces pages s'il a besoin de mémoire).

Sur une architecture x86 32 bits, la mémoire physique est séparée en trois zones. D'une part, la zone ZONE_DMA, qui correspond aux adresses physiques de 0 à 16 Mo. D'autre part, la zone ZONE_NORMAL qui va de 16 à 896 Mo. Et enfin, la zone ZONE_HIGHMEM qui va de 896 à 4 Go (voire 64 Go avec l'extension PAE). Sur ma machine, la mémoire vive remplie donc les 2 premières zones, plus une partie de la ZONE_HIGHMEM.

  • HighTotal : la quantité de mémoire vive présente dans la zone ZONE_HIGHMEM.
  • HighFree : la quantité de mémoire vive non utilisée et présente dans la zone ZONE_HIGHMEM.
  • LowTotal : la quantité de mémoire vive présente dans le zone ZONE_NORMAL.
  • LowFree : la quantité de mémoire vive non utilisée et présente dans la zone ZONE_NORMAL.

Vérifions :

HighTotal + LowTotal = 131008 + 905248 = 1036256 = MemTotal
HighFree + LowFree = 252 + 57468 = 57720 = MemFree

Chouette, ça colle !

  • SwapTotal : la taille du swap.
  • SwapFree : la place disponible dans le swap.

Dans mon cas, le swap n'est pas du tout utilisé (en effet, OpenOffice n'est pas encore lancé... :). C'est également vérifiable avec l'utilitaire swapon :

$ sudo swapon -s
Filename           Type        Size    Used Priority
/dev/mapper/lvm-swap  partition  524280   0   -1
  • Dirty : la quantité de mémoire en attente d'être écrite sur le disque.
  • Writeback : la quantité de mémoire qui est actuellement en train d'être écrite sur le disque.
  • AnonPages : la quantité de mémoire allouée avec la fonction mmap() avec le flag MAP_ANONYMOUS.
  • Mapped : la quantité de mémoire allouée avec la fonction mmap() à partir de fichiers ou de devices.
  • Slab : la quantité de mémoire utilisée au sein du noyau, et allouée avec l'allocateur Slab, pour ses propres objets et structures.
  • PageTables : la quantité de mémoire utilisée en tant que table de pages pour gérer la pagination.
  • CommitLimit : une estimation de la quantité maximale de mémoire disponible pour être allouée.

Linux sépare la mémoire virtuelle en deux partie. Une première, de 3 Go, et qui contient l'espace d'adressage virtuel d'un processus. La seconde, qui elle fait 1 Go, et qui contient l'espace d'adressage noyau. C'est dans cet espace que seront mappés les zones ZONE_DMA et ZONE_NORMAL. Or, celles-ci ont une taille maximale cumulée de 896 Mo. Il est donc nécessaire d'avoir un autre espace, l'espace d'adressage virtuel du noyau, qui permet d'atteindre les 1 Go évoqués juste avant. La valeur VmallocTotal donne la taille de cet espace.

  • VmallocTotal : la quantité totale de mémoire de l'espace d'adressage virtuel du noyau.
  • VmallocUsed : la quantité de mémoire utilisée dans l'espace d'adressage virtuel du noyau.
  • VmallocChunk : le plus grand bloc contigu de mémoire virtuelle (et non physique) disponible dans l'espace d'adressage virtuel du noyau.
  • HugePages_Total : le nombre de hugepage allouée. Une hugepage est une page d'une grande taille (sisi :), utilisée par des programmes qui ont des besoins précis de mémoire.
  • HugePages_Free : le nombre de hugepage disponibles.
  • Hugepagesize : la taille d'une hugepage.