Chroot – úvod, ukázka a zabezpečení



V tomto článku se budu věnovat prostředí chroot, jeho vytváření i použití a nakonec se dostanu k zabezpečení chrootu. Tento článek dělám hlavně jako rychlo-přehled určený pro čtenáře budoucích článků o virtualizaci. Každopádně by měl dobře posloužit jako ucelený přehled všeho co se chrootu týká.

Co je chroot?

Chroot je zkratkou pro výraz “change root”, to v POSIXových systémech (já se zabývám Linuxem) znamená možnost spouštět programy s jiným kořenovým adresářem. Existují dva chrooty. Máme chroot jako systémové volání (v unistd.h) a chroot jako příkaz (který toto volání samozřejmě využívá).

K čemu je chroot?

Chroot se dá použít pro mnoho účelů. Tady je krátký přehled těch nejvýznamnějších:
- Ladění nestabilních systémů mimo produkční prostředí
- Oddělení nějaké bugovaté aplikace od zbytku systému
- Přepnutí do jiného existujícího systému bez nutnosti rebootu
- Zabezpečení (pozor chroot rozhodně není všelék!!!)

Jak funguje chroot?

V jednoduchosti jde o to, že jakýkoliv program spuštěný pod chrootem vidí v adresář / obsah jiného adresáře, který stanovíte při zavolání chrootu. S tím přichází jeden problém. Programy vyžadující dynamicky linkované knihovny se k těmto knihovnám samozřejmě nedostanou pokud je tyto nejsou v našem novém root u (v chrootu). To se více méně běžně řeší třemi způsoby.

Například můžeme použít program ldd, který nám ke každé ELFí binárce dokáže zobrazit dynamické knihovny, na kterých je závislá.
harvie@harvie-ntb:~$ ldd /bin/ls
linux-gate.so.1 => (0xffffe000)
librt.so.1 => /lib/tls/i686/cmov/librt.so.1 (0xb7f10000)
libacl.so.1 => /lib/libacl.so.1 (0xb7f0a000)
libselinux.so.1 => /lib/libselinux.so.1 (0xb7ef4000)
libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7dc3000)
libpthread.so.0 => /lib/tls/i686/cmov/libpthread.so.0 (0xb7db1000)
/lib/ld-linux.so.2 (0xb7f34000)
libattr.so.1 => /lib/libattr.so.1 (0xb7dad000)
libdl.so.2 => /lib/tls/i686/cmov/libdl.so.2 (0xb7da9000)
libsepol.so.1 => /lib/libsepol.so.1 (0xb7d68000)

Z výpisu je tedy vidět že pokud chceme mít v chrootu příkaz ls, potřebujeme mít v chrootu kromě souboru /bin/ls také například knihovnu /lib/tls/i686/cmov/librt.so.1 a všechy další zmíněné. Dělat to vše ručně je poměrně mravenčí práce a pokud to chcete dělat ručně, asi vám za chvíli jebne. Alternativou může být tvorba skriptu, který za vás všechny potřebné knihovny zjistí a zkopíruje do adresáře, který bude jako chroot vězení fungovat.

Možná vám bude stačit jen naprosto minimální shell, který umožní použít minimum příkazů a operací se soubory. V takovém případě můžeme použít program SASH (Standalone Shell). Jde o shell se zabudovanými příkazy, který je celý v jedné statické binárce. Podobnou alternativou může být třeba BusyBox. Oba programy se nachází v repozitářích většiny distribucí. Vytvoření chrootu a práce se SASH může vypadat třeba následovně:

harvie-ntb:/home# mkdir chroot
harvie-ntb:/home# which sash
/bin/sash
harvie-ntb:/home# cp /bin/sash ./chroot/
harvie-ntb:/home# cd chroot/
harvie-ntb:/home/chroot# ls -la
celkem 596
drwxr-xr-x 2 root root 4096 2008-12-03 19:18 .
drwxr-xr-x 16 harvie root 20480 2008-12-08 21:02 ..
-rwxr-xr-x 1 root root 578460 2008-12-03 19:18 sash
harvie-ntb:/home/chroot# chroot /home/chroot/ /sash
Stand-alone shell (version 3.7)
> help
...výpis zkrácen...
> exit
harvie-ntb:/home/chroot#

UPDATE: SASH uz neni nijak moc vyvijeny, tak se da nahradit statickym buildem (cca 1,5MB) BusyBoxu, s tim, ze binarku “busybox” prejmenujete na “sh”, nebo treba udelate link: ln -s /bin/busybox /bin/sh. Výhodou busyboxu je to, že toho umí fakt hafo. Dá se buď volat “busybox prikaz”, nebo pomoci prejmenovani/prelinkovani binarky na ten prikaz. Pokud nechcete linkovat, tak staci ten shell, ze kteryho se pak ostatni prikazy daji spoustet. Je to takový celý systém v jedné binárce a to včetně různých vychytávek jako HTTP server s podporou PHP (php se musí nainstalovat samozřejmě zvlášť) a mnoho dalšího.

Potom je ještě možnost nainstalovat do chrootu nějaký těžkopádnější systém. Například pomocí bootstrap instalátoru vaší oblíbené distribuce (debootstrap, archbootstrap,…) nebo pomocí balíčkovacího systému (dpkg+apt-get, pacman,…). Bootstrap je speciální druh instalátoru, kterým disponují některé GNU/Linuxové distribuce (V našem případě Debian i Arch Linux). Tento instalátor nám pomůže nainstalovat balíčky, které tvoří základní systém (ovšem bez bootloaderu). Většinou stačí bootstrapu zadat repozitář (verzi distribuce) a adresář, kam se má nainstalovat a pak už se jen řídit příkazy na monitoru. Tak můžete snadno provozovat v chrootu třeba testovací verzi Debianu uvnitř stabilní. Já stejným způsobem instaluji virtuální stroje, k tomu ale už nepoužívám chroot, protože z takového chrootu by se případný útočník mohl snadno dostat do hostitelského systému.

Manuál k Debianímu debootstrapu si můžete přešíst například zde. Typické použití je asi toto:
debootstrap sid ./sid-chroot http://ftp.debian.org/debian

Některé distribuce také nabízí skripty pro instalaci vybraných balíčků do chrootu tak, aby byly zachovány i závislosti, nebo to lze řešit nějakým přepínačem u správce balíků.

Do takto vytvořeného systému se dostaneme například pomocí následujícího příkazu (jak jsme již viděli výše):
chroot ./adresar /bin/sh
Ten nejdříve chrootne do ./adresar a pod tímto novým rootem pustí program /bin/sh (z pohledu hostitelského systému se jedná o ./adresar/bin/sh). Pokud nezadáme binárku, která se má spustit, bude spuštěn defaultní shell odvozený od hostitelského prostředí (většinou /bin/sh).

Oprava druhého systému přes chroot

Další užitečnou možností chrootu je práce s jinou instalací systému. Stačí připojit všechny oddíly jiného systému do nkěterého adresáře a potom se do tohoto chrootnout. Tím dostaneme možnost pracovat v tomto systému a i když systém třeba nebootuje, nefunguje init, nebo tak, je možné pohodlně s ním pracovat a spouštět všechny funkční programy z přím v něm a přímo z něj. Já toto s oblibou používám pro obnovu pošpatněného zavaděče (většinou GRUB nebo LILO) pomocí nějakého live cd, není potom potřeba upravovat instalátor bootloaderu tak, aby pracoval s jiným systémem.

Je nutné podotknout, že pro plnohodnotný provoz systému je potřeba připojit /proc, /dev, /tmp, swap, nahodit init, Xserver a podobně, což nemusí být vždy možné, protože chroot prostředí nemá vlastní kernel, ale používá ten z hostitelského systému, proto chroot není plnou náhradou bootu a pro pokročilejší separaci se používají různá virtualizační řešení, o tom ale až v dalším článku.

Volání chroot()

Již jsem se zmínil, že pro toto volání potřebujeme načíst hlavičkový soubor unistd.h (v případě C/C++). Volání se prakticky neliší od příkazu, ale přijímá jen první parametr (cestu k adresáři, který se stane novým rootem). Výhodou může být to, že je možné chrootnout proces až potom, co načte dynamické knihovny a tudíž není nutné je dávat do chroot jailu (vězení).

Chroot nemusíme volat přímo, ale můžeme použít třeba i nějakého prostředníka, jako interpreter PERLu, PHP, Pythonu, případně bindingy pro další jazyky…

Před zavoláním chroot() je dobré/nutné změnit pracovní adresář na budoucí root. Také je příhodné použít absolutní cestu. Nejjednodušší chrootnutí bude tedy vypadat takto:
chdir("/foo/bar");
chroot("/foo/bar");

Pravděpodobně budeme také chtít aby si program už chroota “nesundal” a tedy se z něj nedostal. Proto po zavolání chroot pomocí setuid ještě nastavíme programu jiné uživatelské ID než má root (což je 0), dejme tomu, že chceme program pustit pod běžným uživatelem s UID 1000. Tento uživatel už nemá právo chroot() provést:
setuid(1000);

To je věc, která o programování suid programů (programů určených pro běh pod rootem) platí vždy. Tj. že (mimo jiné) by měl programátor pomocí setuid() zahodit rootovská práva hned jak je nebude potřebovat. Ideálně urychleně provést nějakou operaci a práva okamžitě zahodit, než dá nějakému uličníkovi možnost toho zneužít.

Detekce chrootu u jednotlivých procesů

Ještě bych mohl zmínit, že každý běžící proces má v adresáři /proc svůj adresář, jehož jméno odpovídá PID daného procesu. V tomto adresáři jsou různé věci týkající se tohoto procesu. Mimo jiné se tam nachází i symlink se jménem root. Tento symlink ukazuje na adresář, který se pro tento proces tváří jako root. Na příkladu můžete vidět výpis ls -l pro PID 1, který dokazuje, že init běží opravdu bez chrootu ;)
harvie-ntb:~# PID=1
harvie-ntb:~# ls -l "/proc/$PID/root"
lrwxrwxrwx 1 root root 0 2008-12-09 18:58 /proc/1/root -> /

Zabezpečení chroot prostředí (vězení)

Nebudeme si nic nalhávat, Chroot rozhodně není hodný označení “zabezpečení” a jeho Linuxová implementace je v porovnání s FreeBSD protějškem Jail je dost ubohá. I když Jail už existuje jako Linuxový modul. Označení jail se také používá pro prostředí chrootu i na Linuxu. Každopádně můžeme chroot použít přinejmenším jako další vrstvu bezpečnosti, která i když nemusí být velkou překážkou, pořád se jedná o něco navíc.

Nyní se budeme zabývat teorií toho, co se stane v případě, že útočník získá shell v našem chrootu. Pokud používáme chroot k zabezpečení webserveru, může mu k tomu stačit třeba i chyba v PHP skriptu.

Musíme tedy zajistit několik věcí
- Chrootnuté procesy (webserver,…) neběží pod UID 0 (tedy pod rootem), ale naopak pod uživatelem s co nejmenšími právy (nobody, www-data, apod…). Tento uživatel by taky neměl existovat ve zbytku systému (vlastnit soubory, vykonávat procesy).
- V chrootu není žádná setuid binárka, která by útočníkovy umožnila spouštět cokoli.
- Pokud je to možné, vyplatí se před chrootnutím zrušit práva na zápis do budoucího rootu pomocí chmod, to by se mělo doplňovat s tím, že v chrootu nebude nic, co tam být nemusí, nebo nesmí.
- Ideálně neexistuje v chrootu žádný skript ani binárka, která by se dala zneužít k získání jiného UID, všem spustitelným i jiným věcem můžeme v rámci možností nastavit chmod a-w a také chattr +i (tzv. immutable bit, který zabrání v zápisu i rootovi).
- Všechno jako když zabezpečujeme libovolný jiný podobný systém (třeba i mimo chroot)
- Všichni uživatelé, kteří se mohou do chrootu dostat by měli být omezeni pomocí /etc/security/limits.conf, zvláště by se jim měly omezit maximální počet spuštěných procesů, využití paměti, využití času CPU. To celé může být trochu volněji nastaveno i pro celý systém, zvláště některé z těchto nastavení mohou všeobecně zabránit úspěšnosti (D)DoS útoků a vzájemnému ovlivňování jedné služby druhou.

Dále by neměly být v chrootu zbytečně věci jako kompilátor C, systémové hlavičkové soubory, interpreter PERLu a dalších jazyků, které umožňují chroot, dál samozřejmě bezpečnosti nepomůže binárka chrootu, chattr, chmod, chown.

Každopádně útočník by se neměl bez nějaké 0day chyby v kernelu dostat z chrootu bez toho, aby v chrootu samotném měl root shell (možnost spouštět příkazy jako root).

Další informace o vybourávání se z chroot “vězení” se dozvíte tady a tady. Jinak samozřejmě na googlu.

Odkazy na některé další zdroje a články

ABCLinuxu – Chroot prostředí I.
ABCLinuxu – Chroot prostředí II.
How to break out of a chroot() jail
EN.Wikipedia.org – Chroot
CS.Wikipedia.org – Chroot




Líbí se vám článek? Chcete se o něj podělit? Přidejte ho! (volba topclanky.cz nevyžaduje registraci)

Leave a Reply