Im Wesentlichen müssen Sie die Ausführungsumgebung der Apps steuern. Darin liegt keine Magie. Ein paar Lösungen, die mir in den Sinn kommen:
-
Sie könnten irgendwie alle Binärdateien, die Sie beunruhigen, als setuid/setgid festlegen (das bedeutet nicht, dass sie root gehören müssen, soweit ich weiß). Linux verhindert normalerweise das Anhängen an einen setuid/setgid-Prozess. Bitte überprüfen Sie jedoch, ob dies für Setuids ohne Root-Besitz gilt!
-
Sie könnten einen sicheren Loader verwenden, um Ihre Apps anstelle von ld auszuführen, das sich weigert, LD_PRELOADs zu bestätigen. Dies kann einige vorhandene Apps beschädigen. Weitere Informationen finden Sie in der Arbeit von Mathias Payer, obwohl ich bezweifle, dass es ein Standardtool gibt, das Sie einfach anwenden können.
-
Sie könnten Ihre Binärdateien mit einer libc neu erstellen, die LD_PRELOAD und dlsym deaktiviert. Ich habe gehört, dass man das tun kann, wenn man die richtigen Optionen erfüllt, aber ich kann im Moment keine Informationen darüber finden, wie.
-
Und schließlich könnten Sie Ihre Apps in einer Sandbox ausführen und verhindern, dass Apps direkt andere Prozesse mit einer benutzerdefinierten Umgebung starten oder das Home-Verzeichnis des Benutzers ändern. Auch dafür gibt es kein fertiges Tool (es ist noch viel Arbeit und noch nichts einsatzbereit).
Es gibt wahrscheinlich Grenzen für die oben genannten Lösungen und andere Lösungskandidaten, je nachdem, welche Apps Sie ausführen müssen, wer die Benutzer sind und wie das Bedrohungsmodell aussieht. Wenn Sie Ihre Frage genauer stellen können, werde ich versuchen, diese Antwort entsprechend zu verbessern.
Bearbeiten: Denken Sie daran, dass ein böswilliger Benutzer nur seine eigene Ausführungsumgebung ändern kann (es sei denn, er kann mit einem Exploit die Root-Rechte eskalieren, aber dann müssen Sie andere Probleme lösen). Daher würde ein Benutzer normalerweise keine LD_PRELOAD-Injektionen verwenden, da er bereits Code mit denselben Berechtigungen ausführen kann. Angriffe sind für einige Szenarien sinnvoll:
- Durchbrechen sicherheitsbezogener Überprüfungen auf der Client-Seite von Client-Server-Software (typischerweise Cheats in Videospielen oder eine Client-App dazu bringen, einen Validierungsschritt mit dem Server ihres Distributors zu umgehen)
- Installieren von permanenter Malware, wenn Sie die Sitzung oder den Prozess eines Benutzers übernehmen (entweder weil er vergessen hat, sich abzumelden und Sie physischen Zugriff auf das Gerät haben, oder weil Sie eine seiner Apps mit manipulierten Inhalten ausgenutzt haben)
Die meisten Punkte von Steve DL sind gut, der "beste" Ansatz besteht darin, einen Laufzeitlinker (RTLD) zu verwenden, über den Sie mehr Kontrolle haben. Die Datei „LD_
" Variablen sind fest in glibc codiert (beginnen Sie mit elf/rtld.c
). Die glibc RTLD hat viele "Features", und sogar ELF selbst hat einige Überraschungen mit seinen DT_RPATH- und DT_RUNPATH-Einträgen und $ORIGIN
(siehe https://unix.stackexchange.com/questions/22926/where-do-executables-look-for-shared-objects-at-runtime).
Normalerweise, wenn Sie bestimmte Operationen verhindern (oder ändern) wollen, wenn Sie keine normalen Berechtigungen oder eine eingeschränkte Shell verwenden können, können Sie stattdessen das Laden einer Bibliothek erzwingen, um libc-Aufrufe zu verpacken – das ist genau der Trick, den die Malware verwendet, und das bedeutet es ist schwer, dieselbe Technik dagegen einzusetzen.
Eine Option, mit der Sie das RTLD in Aktion setzen können, ist das Audit Funktion, um diese zu verwenden, stellen Sie LD_AUDIT
ein zum Laden eines gemeinsam genutzten Objekts (das die definierten benannten Funktionen der Prüf-API enthält). Der Vorteil ist, dass Sie die einzelnen geladenen Bibliotheken einbinden können, der Nachteil ist, dass es mit einer Umgebungsvariable gesteuert wird ...
Ein weniger verwendeter Trick ist einer der ld.so
"Eigenschaften":/etc/ld.so.preload
. Was Sie damit tun können, ist, Ihren eigenen Code in jeden dynamischen Prozess zu laden, der Vorteil ist, dass er von einer eingeschränkten Datei gesteuert wird, Nicht-Root-Benutzer können ihn nicht ändern oder außer Kraft setzen (in angemessenem Rahmen, z. B. wenn Benutzer ihre eigene Toolchain installieren können oder ähnliche Tricks).
Unten finden Sie einige experimentelle Code, um dies zu tun, sollten Sie wahrscheinlich gründlich darüber nachdenken, bevor Sie ihn in der Produktion verwenden, aber er zeigt, dass es möglich ist.
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <limits.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <dlfcn.h>
#include <link.h>
#include <assert.h>
#include <errno.h>
int dlcb(struct dl_phdr_info *info, size_t size, void *data);
#define DEBUG 1
#define dfprintf(fmt, ...) \
do { if (DEBUG) fprintf(stderr, "[%5i %14s#%04d:%8s()] " fmt, \
getpid(),__FILE__, __LINE__, __func__, __VA_ARGS__); } while (0)
void _init()
{
char **ep,**p_progname;
int dlcount[2]={0,0};
dfprintf("ldwrap2 invoked!\n","");
p_progname=dlsym(RTLD_NEXT, "__progname");
dfprintf("__progname=<%s>\n",*p_progname);
// invoke dlcb callback for every loaded shared object
dl_iterate_phdr(dlcb,dlcount);
dfprintf("good count %i, bad count %i\n",dlcount[0],dlcount[1]);
if ((geteuid()>100) && dlcount[1]) {
for (ep=environ; *ep!=NULL; ep++)
if (!strncmp(*ep,"LD_",3))
fprintf(stderr,"%s\n", *ep);
fprintf(stderr,"Terminating program: %s\n",*p_progname);
assert_perror(EPERM);
}
dfprintf("on with the show!\n","");
}
int dlcb(struct dl_phdr_info *info, size_t size, void *data)
{
char *trusted[]={"/lib/", "/lib64/",
"/usr/lib","/usr/lib64",
"/usr/local/lib/",
NULL};
char respath[PATH_MAX+1];
int *dlcount=data,nn;
if (!realpath(info->dlpi_name,respath)) { respath[0]='\0'; }
dfprintf("name=%s (%s)\n", info->dlpi_name, respath);
// special case [stack] and [vdso] which have no filename
if (respath && strlen(respath)) {
for (nn=0; trusted[nn];nn++) {
dfprintf("strncmp(%s,%s,%i)\n",
trusted[nn],respath,strlen(trusted[nn]));
if (!strncmp(trusted[nn],respath,strlen(trusted[nn]))) {
dlcount[0]++;
break;
}
}
if (trusted[nn]==NULL) {
dlcount[1]++;
fprintf(stderr,"Unexpected DSO loaded from %s\n",respath);
}
}
return 0;
}
Mit gcc -nostartfiles -shared -Wl,-soname,ldwrap2.so -ldl -o ldwrap2 ldwrap2.c
kompilieren .Sie können dies mit LD_PRELOAD
testen ohne /etc/ld.so.conf
zu ändern :
$ LD_PRELOAD=./ldwrap2.so ls
Unexpected DSO loaded from /home/mr/code/C/ldso/ldwrap2.so
LD_PRELOAD=./ldwrap2.so
Terminating program: ls
ls: ldwrap2.c:47: _init: Unexpected error: Operation not permitted.
Aborted
(Ja, es hat den Prozess angehalten, weil es sich selbst erkannt hat, da dieser Pfad nicht "vertrauenswürdig" ist.)
Das funktioniert folgendermaßen:
- Verwenden Sie eine Funktion namens
_init()
um die Kontrolle zu erlangen, bevor der Prozess beginnt (ein subtiler Punkt ist, dass dies funktioniert, weilld.so.preload
Startups werden vor diesenLD_PRELOAD
aufgerufen Bibliotheken, obwohl ich dies nicht dokumentiert finden kann ) - benutze
dl_iterate_phdr()
um alle dynamischen Objekte in diesem Prozess zu durchlaufen (ungefähr gleichbedeutend mit dem Durchstöbern von/proc/self/maps
) - alle Pfade auflösen und mit einer hartcodierten Liste vertrauenswürdiger Präfixe vergleichen
- es findet alle Bibliotheken, die beim Start des Prozesses geladen wurden, sogar die, die über
LD_LIBRARY_PATH
gefunden wurden , aber nicht die nachträglich mitdlopen()
geladen werden .
Dies hat einen einfachen geteuid()>100
Bedingung, um Probleme zu minimieren. Es vertraut RPATHS nicht und behandelt diese in keiner Weise separat, daher muss dieser Ansatz für solche Binärdateien etwas angepasst werden. Sie könnten den Abbruchcode trivialerweise so ändern, dass er stattdessen über Syslog protokolliert wird.
Wenn Sie /etc/ld.so.preload
ändern und wenn Sie irgendetwas davon falsch machen, könnten Sie Ihr System ernsthaft beschädigen . (Sie haben doch eine statisch gelinkte Rescue-Shell, richtig?)
Mit unshare
können Sie sinnvollerweise kontrolliert testen und mount --bind
um seine Wirkung zu begrenzen (d.h. einen privaten /etc/ld.so.preload
zu haben ). Sie benötigen root (oder CAP_SYS_ADMIN
) für unshare
obwohl:
echo "/usr/local/lib/ldwrap2.so" > /etc/ld.so.conf.test
unshare -m -- sh -c "mount --bind /etc/ld.so.preload.test /etc/ld.so.preload; /bin/bash"
Wenn Ihre Benutzer über ssh zugreifen, dann ForceCommand
von OpenSSH und Match group
könnte wahrscheinlich verwendet werden, oder ein maßgeschneidertes Startskript für einen dedizierten "nicht vertrauenswürdigen Benutzer" sshd-Daemon.
Zusammenfassend:Die einzige Möglichkeit, genau das zu tun, was Sie anfordern (LD_PRELOAD verhindern), besteht darin, einen gehackten oder besser konfigurierbaren Laufzeitlinker zu verwenden. Oben ist eine Problemumgehung, mit der Sie Bibliotheken nach vertrauenswürdigen Pfaden einschränken können, wodurch solche heimliche Malware entschärft wird.
Als letzten Ausweg könnten Sie Benutzer zwingen, sudo
zu verwenden Um alle Programme auszuführen, wird dies ihre Umgebung schön aufräumen, und da es setuid ist, wird es selbst nicht betroffen sein. Nur eine Idee;-) Zum Thema sudo
, verwendet es denselben Bibliothekstrick, um zu verhindern, dass Programme Benutzern mit seinem NOEXEC
eine Backdoor-Shell geben Funktion.
Ja, es gibt einen Weg:Lassen Sie diesen Benutzer keinen beliebigen Code ausführen. Geben Sie ihnen eine eingeschränkte Shell oder besser nur einen vordefinierten Satz von Befehlen.
Sie würden die Ausführung von Malware nicht verhindern, es sei denn, Sie haben einen nicht standardmäßigen Rechteausweitungsmechanismus verwendet, der diese Variablen nicht löscht. Normale Privilegien-Eskalationsmechanismen (ausführbare setuid-, setgid- oder setcap-Dateien; Aufrufe zwischen Prozessen) ignorieren diese Variablen. Es geht also nicht darum, Malware zu verhindern, sondern nur darum, Malware zu erkennen.
LD_PRELOAD
und LD_LIBRARY_PATH
ermöglicht es einem Benutzer, installierte ausführbare Dateien auszuführen und sie anders verhalten zu lassen. Große Sache:Der Benutzer kann seine eigenen ausführbaren Dateien ausführen (einschließlich statisch verknüpfter). Alles, was Sie bekommen würden, wäre ein bisschen Rechenschaftspflicht, wenn Sie alle execve
protokollieren würden Anrufe. Aber wenn Sie sich darauf verlassen, Malware zu erkennen, gibt es so viel, das Ihrer Überwachung entgehen kann, dass ich mich nicht darum kümmern würde. Viele Programmiersprachen bieten ähnliche Möglichkeiten wie LD_LIBRARY_PATH
:CLASSPATH
, PERLLIB
, PYTHONPATH
usw. Sie werden sie nicht alle auf die schwarze Liste setzen, nur ein Whitelist-Ansatz wäre nützlich.
Zumindest müssten Sie ptrace
blockieren auch:mit ptrace
, kann jede ausführbare Datei dazu gebracht werden, jeden Code auszuführen. Blockieren von ptrace
kann eine gute Idee sein – aber vor allem, weil so viele Sicherheitslücken gefunden wurden, dass es wahrscheinlich ist, dass einige unentdeckt bleiben.
Bei einer eingeschränkten Shell ist die LD_*
Variablen sind eigentlich ein Problem, da der Benutzer nur einen vorab genehmigten Satz von Programmen und LD_*
ausführen kann ermöglicht es ihnen, diese Einschränkung zu umgehen. Einige eingeschränkte Shells erlauben es, Variablen schreibgeschützt zu machen.