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/ INSA Toulouse, Novembre 2012 2/34
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 INSA Toulouse, Novembre 2012 5/34
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. INSA Toulouse, Novembre 2012 7/34
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... INSA Toulouse, Novembre 2012 9/34
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 INSA Toulouse, Novembre 2012 13/34
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! INSA Toulouse, Novembre 2012 15/34
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. INSA Toulouse, Novembre 2012 17/34
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); INSA Toulouse, Novembre 2012 19/34
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); INSA Toulouse, Novembre 2012 21/34
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. INSA Toulouse, Novembre 2012 23/34
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 INSA Toulouse, Novembre 2012 26/34
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 INSA Toulouse, Novembre 2012 28/34
/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 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,...) INSA Toulouse, Novembre 2012 30/34
Exemple : serveur X Main X server Init 1 privileged revoke privileges Fork Main X server Init 2 unprivileged request ack Child kill_parent Main X server Main loop unprivileged request descriptor open_device privileged 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 INSA Toulouse, Novembre 2012 33/34
Questions?