Programmation défensive

Texte intégral

(1)

Programmation défensive

Matthieu Herrb

INSA Toulouse, Novembre 2012

http://homepages.laas.fr/matthieu/cours/mh-prog-defensive.pdf

Licence

Ce document est sous licence

Creative Commons Paternité - Partage à l’Identique 3.0 non transposé.

Le texte complet de cette licence est disponible à l’adresse : http://creativecommons.org/licenses/by-sa/3.0/

(2)

Agenda

1 Introduction

2 Bibliographie

3 API plus sûres

4 Limitation des privilèges

5 Conclusion

INSA Toulouse, Novembre 2012 3/34

Introduction

Ce cours devrait être inutile...

Mais :

langages de programmation courants

→ erreurs inévitables

limites/coût des méthodes formelles

⇒ techniques de programmation pour limiter l’impact des erreurs

(3)

Contexte

Programmation « système » : noyau,

démons réseau, outils bas-niveau

langage : essentiellement C

Expériences acquises par le projet OpenBSD Reprises ailleurs, généralisables

INSA Toulouse, Novembre 2012 6/34

Règles de codage

Correction du code d’abord → meilleure fiabilité, meilleure sécurité.

Conception recherchant la simplicité.

Principe de revue par les pairs à tous les niveaux Recherche systématique des erreurs

Nouvelles fonctionnalités de gcc : option -Wbounded,

attribut __sentinel__

Outils : llvm/clang, Parfait, etc.

(4)

Technologies pour la sécurité

strlcpy/strlcat

protection de la mémoire

révocation des privilèges (ex. ping) séparation des privilèges (ex. OpenSSH) mise en cage (chroot)

uids distincts par service

protection de la pile (SSP) & Stackgap introduction d’aléas (ld.so, malloc, mmap)

INSA Toulouse, Novembre 2012 8/34

Niveaux de sécurité dans *BSD

Parti-pris : pas de politique à grain fin:

trop complexe donc potentiellement dangereux.

Par défaut 3 niveaux de privilège : noyau

root

utilisateur

Flags du système de fichiers (immutable, append-only) pour limiter les accès de root.

Interdiction de modification de certains réglages de sécurité.

Limite les accès à /dev/mem.

Exception : X...

(5)

Bibliographie

A Bug Hunter’s Diary, A Guided Tour Through the Wilds of Software Security, Tobias Klein, No Starch Press, 2011. ISBN: 978-1-59327-385-9.

Secure Coding in C and C++, Robert C. Seacord, Addison Wesley, 2006. ISBN:

978-0-321-33572-2.

Guide superflu de programmation en C, Matthieu Herrb, 2004.

http://homepages.laas.fr/matthieu/cours/c-superflu/

http://www.openbsd.org/papers/index.html :

Using OpenBSD Security Features to Find Software Bugs, Peter Valchev, Reflections/Projections, Champaign-Urbana, 2007

Security Measures in OpenSSH, Damien Miller Asia BSD Conference 2007 Exploit Mitigation Techniques, Theo de Raadt OpenCON 2005, Venice, Italy A Secure BGP Implementation, Henning Brauer SUCON 04

Preventing Privilege Escalation, Niels Provos, Markus Friedl and Peter

Honeyman, 12th USENIX Security Symposium, Washington, DC, August 2003.

Enhancing XFree86 security, Matthieu Herrb LSM, Metz 2003.

INSA Toulouse, Novembre 2012 11/34

API plus sûres

beaucoup d’API ne sont pas pensées pour la sécurité risques d’erreurs non détectées à l’exécution

difficiles à utiliser

proposer des remplacements plus sûrs encourager l’abandon des API dangereuses ne pas refaire ces erreurs :

attention à la sécurité dans les nouvelles API

(6)

Manipulation de chaînes de caractères en C

Problème numéro un : débordement de buffer strcpy(path, getenv("$HOME"));

strcat(path, "/");

strcat(path, ".foorc");

len = strlen(path);

INSA Toulouse, Novembre 2012 14/34

API Dangereuses

strcat(), strcpy() : API dangereuse, aucune vérification sur la taille des buffers → À Bannir !

(7)

API Dangereuses (2)

variantes « n » (strncat(), strncpy()) très difficiles (impossible ?) à utiliser correctement : laissent les chaînes non terminées par nul.

strncpy(path, homedir, sizeof(path) - 1);

path[sizeof(path) - 1] = ’\ 0’;

strncat(path, "/", sizeof(path) - strlen(path) - 1);

strncat(path, ".foorc", sizeof(path) - strlen(path) - 1);

len = strlen(path);

INSA Toulouse, Novembre 2012 16/34

Remplacement

Solution proposée par Todd Miller (1999): strlcpy() et strlcat() : tronquent les chaînes trop longues mais

garantissent qu’on a toujours un résultat qui est une chaîne valide.

Possible de tester si troncation a eu lieu.

(8)

strl* : exemple

Exemple simple sans vérifications :

strlcpy(path, homedir, sizeof(path));

strlcat(path, "/", sizeof(path));

strlcat(path, ".foorc", sizeof(path));

len = strlen(path);

→ risque de troncation, mais pas de débordement

INSA Toulouse, Novembre 2012 18/34

strl*: exemple avec vérifications

len = strlcpy(path, homedir, sizeof(path);

if (len >= sizeof(path)) return (ENAMETOOLONG);

len = strlcat(path, "/", sizeof(path);

if (len >= sizeof(path)) return (ENAMETOOLONG);

len = strlcat(path, ".foorc", sizeof(path));

if (len >= sizeof(path)) return (ENAMETOOLONG);

(9)

TOCTOU

Time Of Check, Time Of Use

Comment créer un fichier temporaire dans /tmp sans écraser un fichier existant ?

/* Generate random file name */

name = mktemp("/tmp/my-temp-file.XXXXXXXX");

/* verify that it is non-existant */

if (stat(name, &statbuf) == 0) { return EEXISTS;

}

/* Good, open it */

fd = open(name, O_RDWR);

→ pas bon !

INSA Toulouse, Novembre 2012 20/34

TOCTOU - remèdes

mkstemp() remplace mktemp() et open() de manière sûre.

fd = mkstemp("/tmp/my-temp-file.XXXXXXXX");

Autre solution: le flag O_CREAT de open() qui produit une erreur si le fichier existe déjà:

fd = open(name, O_RDWR | O_CREAT);

(10)

Débordements arithmétiques

Allouer n structures d’un type donné : n = getIntFromUser();

if (n <= 0)

return EINVAL;

data = (struct MyStruct *)malloc(n*sizeof(struct MyStruct));

if (data == NULL) return ENOMEM;

Si n est assez grand → débordement et allocation d’une zone mémoire trop petite, puis débordement mémoire.

INSA Toulouse, Novembre 2012 22/34

Débordement arithmétiques (2)

Utiliser calloc() :

data = (struct MyStruct *)calloc(n,

sizeof(struct MyStruct));

if (data == NULL) return ENOMEM;

Pas de débordement arithmétique possible.

Si n trop grand, calloc retournera NULL.

(11)

Débordements arithmétiques (3)

n = getIntFromUser();

if (n <= 0 || n*sizeof(struct MyStruct) > MAX_BUF_SIZE) return EINVAL;

→ si n est assez grand, la condition testée va être vraie.

Utiliser :

if (n <= 0 || n > MAX_BUF_SIZE/sizeof(struct MyStruct)) return EINVAL;

INSA Toulouse, Novembre 2012 24/34

Applications privilégiées

Applications qui ont besoin des privilèges de root ouverture de ports TCP/UDP < 1024

écriture dans répertoires système allocation de pseudo-tty

accès à des périphériques à accès restreint Deux grandes familles d’applications privilégiées :

démons système démarrés par root programmes avec le bit setuid

(12)

Privilèges et vulnérabilités

Une erreur dans un programme privilégié a un impact plus élevé exécution du code malicieux avec privilèges

Réduire les privilèges :

si plus nécessaires, révocation définitive après initialisation sinon : séparation des privilèges

INSA Toulouse, Novembre 2012 27/34

Réduction des privilèges

Révoquer définitivement les privilèges des commandes privilégiées (setuid) ou démons lancés avec privilège (par root au démarrage) une fois que toutes les opérations nécessitant un privilège sont effectuées.

Grouper ces opérations le plus tôt possible.

Exemples:

ping named

(13)

/usr/src/sbin/ping/ping.c

int

main(int argc, char *argv[]) {

/* Declarations ... */

if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) err(1, "socket");

/* revoke privs */

uid = getuid();

if (setresuid(uid, uid, uid) == -1) err(1, "setresuid");

....

}

INSA Toulouse, Novembre 2012 29/34

Séparation des privilèges

[Provos 2003]

exécuter les démons système avec un uid 6= 0

dans une cage chroot(2)

processus additionnel d’aide qui reste privilégié mais vérifie de manière paranoïaque toutes ses actions.

Une douzaine de démons ainsi protégés (sshd, ntpd, bgpd,...)

(14)

Exemple : serveur X

Main X server

unprivileged

unprivileged privileged

Child

Main X server Main X server

Main loop Init 2 Init 1

request ack

request descriptor

kill_parent

open_device privileged revoke Fork

privileges

http://www.openbsd.org/cgi-bin/cvsweb/xenocara/xserver/os/privsep.c

INSA Toulouse, Novembre 2012 31/34

Conclusion

Erreurs de programmation → impact potentiel sur la sécurité Impossible de les éliminer à 100 %

Adopter des bonnes pratiques pour limiter les risques d’erreurs API plus sûres

Limiter les privilèges Faire relire son code

(15)

Questions ?

Figure

Updating...

Références

Updating...

Sujets connexes :