GNU/Linux >> Znalost Linux >  >> Linux

Existuje nějaký způsob, jak zablokovat LD_PRELOAD a LD_LIBRARY_PATH na Linuxu?

V zásadě musíte ovládat spouštěcí prostředí aplikací. Není v tom žádná magie. Pár řešení, která mě napadají:

  1. Mohli byste nějak nastavit všechny binární soubory, které vás znepokojují, jako setuid/setgid (což neznamená, že je musí vlastnit root, pokud vím). Linux normálně brání připojení k procesu setuid/setgid. Prosím ověřte, zda tak činí pro setuid nevlastněný rootem!

  2. Ke spouštění aplikací můžete použít bezpečný zavaděč namísto ld, který odmítá uznat LD_PRELOADs. To může poškodit některé stávající aplikace. Více najdete v práci Mathiase Payera, i když pochybuji, že existuje nějaký běžně dostupný nástroj, který můžete použít.

  3. Můžete znovu vytvořit své binární soubory pomocí knihovny libc, která zakáže LD_PRELOAD a dlsym. Slyšel jsem, že to musl dokáže, pokud projde správnými možnostmi, ale momentálně nemohu najít informace o tom, jak.

  4. A nakonec můžete své aplikace umístit do izolovaného prostoru a zabránit aplikacím v přímém spouštění jiných procesů s vlastním prostředím nebo v úpravě domovského adresáře uživatele. Ani na to neexistuje žádný hotový nástroj (je na tom hodně práce a zatím není nic použitelné).

Pravděpodobně existují omezení pro výše uvedená řešení a další kandidátská řešení v závislosti na tom, jaké aplikace potřebujete spustit, kdo jsou uživatelé a jaký je model hrozby. Pokud můžete svou otázku zpřesnit, pokusím se ji odpovídajícím způsobem vylepšit.

Upravit: mějte na paměti, že uživatel se zlými úmysly může upravit pouze své vlastní spouštěcí prostředí (pokud nemůže eskalovat oprávnění na root pomocí nějakého exploitu, ale pak budete muset řešit další problémy). Uživatel by tedy obvykle nepoužíval injekce LD_PRELOAD, protože již může spouštět kód se stejnými oprávněními. Útoky mají smysl pro několik scénářů:

  • prolomení bezpečnostních kontrol na klientské straně softwaru klient-server (obvykle cheaty ve videohrách nebo nucení klientské aplikace obejít některé kroky ověření se serverem svého distributora)
  • instalace trvalého malwaru, když převezmete relaci nebo proces uživatele (buď proto, že se uživatel zapomněl odhlásit a vy máte fyzický přístup k zařízení, nebo proto, že jste zneužili některou z jeho aplikací s vytvořeným obsahem)

Většina bodů Steva DL je dobrá, „nejlepší“ přístup je použít run-time linker (RTLD), nad kterým máte větší kontrolu. "LD_ " proměnné jsou pevně zakódovány do glibc (začínají elf/rtld.c ). Glibc RTLD má mnoho „funkcí“ a dokonce i samotný ELF má několik překvapení se svými položkami DT_RPATH a DT_RUNPATH a $ORIGIN (viz https://unix.stackexchange.com/questions/22926/where-do-executables-look-for-shared-objects-at-runtime).

Normálně, pokud chcete zabránit (nebo změnit) určité operace, když nemůžete použít normální oprávnění nebo omezený shell, můžete místo toho vynutit načtení knihovny, aby zabalila volání libc – to je přesně ten trik, který malware používá, a to znamená je těžké proti tomu použít stejnou techniku.

Jednou z možností, která vám umožní zapojit RTLD do akce, je audit Chcete-li tuto funkci použít, nastavte LD_AUDIT k načtení sdíleného objektu (obsahujícího definované funkce auditovacího API). Výhodou je, že můžete připojit jednotlivé načítané knihovny, nevýhodou je, že je to řízeno proměnnou prostředí...

Méně používaný trik je další z ld.so "features" :/etc/ld.so.preload . Co s tím můžete udělat, je načíst svůj vlastní kód do každého dynamického procesu, výhodou je, že je řízen omezeným souborem, uživatelé bez oprávnění root jej nemohou upravovat ani přepisovat (v rozumných mezích, např. pokud si uživatelé mohou nainstalovat svůj vlastní toolchain nebo podobné triky).

Níže je několik experimentálních Chcete-li to provést, měli byste si to před použitím ve výrobě důkladně promyslet, ale ukazuje, že to lze udělat.

#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;
}

Kompilace s gcc -nostartfiles -shared -Wl,-soname,ldwrap2.so -ldl -o ldwrap2 ldwrap2.c .Můžete to otestovat pomocí LD_PRELOAD bez úpravy /etc/ld.so.conf :

$ 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

(ano, zastavilo a zastavilo proces, protože se detekovalo, protože tato cesta není „důvěryhodná“.)

Funguje to takto:

  • použijte funkci s názvem _init() získat kontrolu před zahájením procesu (důmyslným bodem je, že to funguje, protože ld.so.preload starty jsou vyvolány před těmi jakýmikoli LD_PRELOAD knihovny, i když to nemohu najít zdokumentované )
  • použijte dl_iterate_phdr() iterovat přes všechny dynamické objekty v tomto procesu (přibližně ekvivalentní prohrabování v /proc/self/maps )
  • vyřešte všechny cesty a porovnejte je s pevně zakódovaným seznamem důvěryhodných předpon
  • najde všechny knihovny načtené při spuštění procesu, dokonce i ty nalezené pomocí LD_LIBRARY_PATH , ale ne ty následně načtené dlopen() .

Toto má jednoduchý geteuid()>100 stav pro minimalizaci problémů. Nedůvěřuje RPATHS ani je nijak zvlášť nezpracovává, takže tento přístup potřebuje pro takové binární soubory nějaké vyladění. Můžete triviálně změnit kód přerušení tak, aby se místo toho přihlašoval přes syslog.

Pokud upravíte /etc/ld.so.preload a pokud se něco z toho zmýlíte, mohli byste vážně narušit svůj systém . (Máte staticky propojený záchranný shell, že?)

Mohli byste užitečně testovat kontrolovaným způsobem pomocí unshare a mount --bind omezit jeho účinek (tj. mít soukromý /etc/ld.so.preload ). Potřebujete root (nebo CAP_SYS_ADMIN ) pro unshare ačkoli:

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"

Pokud vaši uživatelé přistupují přes ssh, pak ForceCommand OpenSSH a Match group pravděpodobně by se dal použít, nebo přizpůsobený spouštěcí skript pro specializovaného démona sshd "nedůvěryhodný uživatel".

Abych to shrnul:jediný způsob, jak můžete udělat přesně to, co požadujete (zabránit LD_PRELOAD), je použití hacknutého nebo lépe konfigurovatelného linkeru za běhu. Výše uvedené je řešení, které vám umožní omezit knihovny důvěryhodnou cestou, která takový kradmý malware odstraní.

Jako poslední možnost můžete přinutit uživatele, aby používali sudo pro spouštění všech programů to pěkně vyčistí jejich prostředí a protože je to setuid, samo to neovlivní. Jen nápad;-) K tématu sudo , používá stejný trik s knihovnami, aby zabránil programům poskytovat uživatelům backdoor shell se svým NOEXEC funkce.


Ano, existuje způsob:nedovolte tomuto uživateli spouštět libovolný kód. Dejte jim omezený shell, nebo lépe, pouze předdefinovanou sadu příkazů.

Spuštění žádného malwaru byste nezabránili, pokud byste nepoužili nějaký nestandardní mechanismus eskalace oprávnění, který tyto proměnné nevymaže. Normální mechanismy eskalace oprávnění (spustitelné soubory setuid, setgid nebo setcap; meziprocesová volání) tyto proměnné ignorují. Nejde tedy o prevenci malwaru, jde pouze o detekci malwaru.

LD_PRELOAD a LD_LIBRARY_PATH umožňuje uživateli spouštět nainstalované spustitelné soubory a přimět je, aby se chovaly odlišně. Velký problém:uživatel může spouštět své vlastní spustitelné soubory (včetně staticky propojených). Jediné, co byste získali, je trochu odpovědnosti, pokud zaznamenáváte všechny execve hovory. Ale pokud se na to spoléháte při detekci malwaru, je toho tolik, co může uniknout vašemu dohledu, že bych se tím neobtěžoval. Mnoho programovacích jazyků nabízí vybavení podobné LD_LIBRARY_PATH :CLASSPATH , PERLLIB , PYTHONPATH , atd. Nechystáte je všechny na černou listinu, užitečný by byl pouze přístup na bílou listinu.

Přinejmenším byste museli zablokovat ptrace také:s ptrace , může být vytvořen jakýkoli spustitelný soubor ke spuštění libovolného kódu. Blokování ptrace může to být dobrý nápad – ale především proto, že kolem toho bylo nalezeno tolik zranitelností, že je pravděpodobné, že pár jich zůstane neobjevených.

S omezeným shellem, LD_* proměnné jsou ve skutečnosti problémem, protože uživatel může spouštět pouze předem schválenou sadu programů a LD_* umožňuje jim toto omezení obejít. Některé omezené shelly umožňují, aby proměnné byly pouze pro čtení.


Linux
  1. Dhcpd:Jak zkontrolovat stav fondu Dhcp?

  2. Jaký způsob, jak zjistit velikost mezipaměti L1, L2, L3 a RAM v Linuxu?

  3. Existují v Linuxu nějaké standardní kódy ukončení?

  1. Existuje STDCALL v Linuxu?

  2. Existuje způsob, jak získat UID uživatele na počítači se systémem Linux pomocí javy?

  3. ukončení procesů mmap, msync a linux

  1. Existuje způsob, jak zlepšit výkon linuxových trubek?

  2. Existuje nějaký způsob, jak získat 64bitový time_t v 32bitových programech v Linuxu?

  3. Existuje způsob, jak získat správné CLOCK_TAI na Linuxu?