Nästan alla program i Linux- och Unix-system använder dynamiskt länkade bibliotek. En praktisk funktion som gör att programmen blir mindre och det går att uppdatera biblioteken utan att programmen behöver kompileras om.

Men det har även sina nackdelar när det kommer till säkerheten. Med miljövariabeln LD_PRELOAD går det att byta ut valfri funktion i ett program mot en egen variant. När LD_PRELOAD-variabeln är satt kommer alla program att leta efter funktioner i den sökvägen – före de vanliga sökvägarna för bibliotek.

Det går även att sätta preload på systemnivå via filen /etc/ld.so.preload. Då påverkas alla användare, inte bara den användare som har satt miljövariabeln LD_PRELOAD.

Blandade säkerhetstips – del 3

Säkerhetsimplikationerna av detta är flera och det senaste exemplet när detta användes “in the wild” var i april i år. Det var Juniper som upptäckte ett rootkit där inkräktaren först hackar Control Web Panel och därefter laddar ner ett bibliotek på datorn. Bibliotekets kod injiceras sedan i SSH via /etc/ld.so.preload. Rootkitet har senare fått namnet Facefish av Netlab.

ANNONS

Enkla exempel

Vi kan demonstrera ett exempel på att det går att modifiera vilket program som helst med LD_PRELOAD med ytterst lite kod.

Programmet whoami använder funktionen geteuid() för att få fram användarens EUID (Effective User ID). Därefter använder whoami UID-numret för att slå upp användarens namn. (Källkoden för whoami finns på GitHub.)

Genom att skapa en egen variant av geteuid() i ett eget bibliotek och därefter sätta LD_PRELOAD kan vi lura whoami.

Utan några modifieringar säger whoami att jag är jack, vilket är helt korrekt:

$> whoami
jack

Nu skapar vi en egen variant av C-funktionen geteuid(). Skapa en fil med namnet myuid.c med följande innehåll:

int geteuid()
{
    return 65534;
}

I koden ovan kommer funktionen geteuid() alltid att returnera UID 65534, ett UID som ofta används för användaren nobody. Detta är en användare som används för demoner och dylikt.

Nu skapar vi ett dynamiskt bibliotek utifrån C-koden ovan:

$> gcc -shared -fPIC myuid.c -o myuid.so

Nu kan vi sätta miljövariabeln LD_PRELOAD och därmed lura whoami.

$> export LD_PRELOAD="/home/jack/myuid.so"
$> whoami
nobody

Detta var så klart en oskyldig demonstration, det går att göra mycket allvarligare saker än så här. Men det visar att det är möjligt att byta ut funktioner i vilket program som helst som använder dynamiska bibliotek – vilket i princip alla program gör.

/etc/ld.so.preload

Det går även att sätta dynamiska bibliotek som ska läsas in före alla andra på systemnivå. Detta görs i filen /etc/ld.so.preload. Detta kan ha allvarligare konsekvenser. Om vi fortsätter på samma tema som tidigare med att lura program på vårt egna UID, kan vi lura både su och passwd att vi är root. Vi kan då – som en vanlig användare – växla till root-användaren utan att behöva ange lösenordet för root. Vi kan också byta lösenord för vilken användare som helst på systemet med passwd.

ANNONS

Detta hade inte varit möjligt med LD_PRELOAD då systemet inte tillåter att vi laddar in okänd kod i binärer som har SetUID-biten satt. Men denna begränsningen finns inte för /etc/ld.so.preload. Eftersom vi kan skriva till /etc-katalogen har vi förmodligen root-rättigheter och därför tillåts det.

Gör inte denna laborationen på ett system som du använder live! En del program slutar att fungera, däribland SSH-demonen. Vem som helst kan också bli root på systemet! Inloggningar till systemet kan också påverkas!

Både su- och passwd-kommandot använder funktionen getuid() för att avgöra användarens UID. Root-användarens UID är alltid 0. Vi skriver därför en ny funktion som ser ut så här:

int getuid()
{
    return 0;
}

Därefter gör vi ett bibliotek av det:

$> gcc -shared -fPIC rootuid.c -o rootuid.so

Nu lägger vi till biblioteket i filen /etc/ld.so.preload. Filen är en lista med bibliotek som separeras med ett mellanslag. I mitt system är filen tom så jag lägger in sökvägen till rootuid.so:

#> echo "/home/jack/rootuid.so" > /etc/ld.so.preload

Nu kan vi växla till root-användaren med su utan att behöva ange något lösenord (su tror ju att vi redan är root):

$> su
#> whoami
root

Vi kan också byta lösenord för root – eller vilken annan användare som helst – utan att behöva använda varken su eller sudo:

$> passwd root
New password:
Retype new password:
passwd: password updated successfully

Skydd

Det är svårt att skydda sig mot denna typen av attacker. När det kommer till vanliga användare, som använder datorn interaktivt, är det enklast och effektivast att helt enkelt se till att LD_PRELOAD inte är satt, eller skriva över den med en tom sträng.

Det går även att se vilka bibliotek som kommer att användas. Om vi fortsätter med samma exempel som tidigare kan vi titta på whoami med ldd. Vi ser då att /home/jack/myuid.so ligger före libc som är standardbiblioteket.

$ ldd /usr/bin/whoami 
    linux-vdso.so.1 (0x00007ffeccda6000)
    /home/jack/myuid.so (0x00007efdcb92f000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007efdcb731000)
    /lib64/ld-linux-x86-64.so.2 (0x00007efdcb940000)

Detta skyddar dock inte mot automatiserade attacker, så som exempelvis Facefish som nämndes i inledningen. Här använder rootkitet istället /etc/ld.so.preload. Här gäller det istället att se till att inkräktaren inte får fotfäste från första början.

En säkerhetsåtgård skulle dock kunna vara att låta exempelvis Nagios eller Icinga2 (eller ett annat liknande övervakningsprogram) kontrollera att /etc/ld.so.preload antingen är tom eller inte finns alls. Finns filen, alternativt innehåller något, meddelas administratören. Då upptäcks åtminstone intrånget även om det inte har förhindrats.


Kommentarer

Kommentarsfältet är modererat. Det innebär att alla kommentarer granskas av ansvarig utgivare före publicering.

Du väljer själv om du vill ange ditt riktiga namn, en pseudonym eller vara helt anonym. Ingen registrering behövs.