UT2003 Server im Chroot

Von Arne

In diesem Artikel werde ich beschreiben, wie man einen Unreal Tournament Dedicated Server unter Linux in einem Chroot-Gefängnis ausführt. Wie sich gezeigt hat funktioniert das selbe Vorgehen auch mit dem UT2004 Server.

Das Konzept hinter chroot

Chroot steht für change root. Mit root ist hiermit gemeint welchen Teil des Dateisystems ein Prozess als Wurzel ( / ) sieht. Normalerweise sieht jeder Prozess als root die Wurzel des Dateisystems. Wird nun eine Sicherheitslücke in einem Programm entdeckt und ein Hacker kann diese aussuchen so hat er im schlimmsten Fall zugriff auf das gesammte Dateisystem des betroffenen Rechners. Hier kommt nun das Tool chroot ins Spiel. Mit chroot wird es möglich einem Prozess nur einen Teil des Dateisystems sehen zu lassen. Somit könnte dieser Prozess dann im schlimmsten Fall auch nur Daten aus diesem Bereich des Dateisystems lesen.

Betrachten wir einen Teil eines Dateisystems aus der Sicht der Tatsächlichen Verzeichniswurzel:

/
/bin
/boot
/dev
/etc
/home
/lib
/mnt
/proc
/root
/sbin
/tmp
/usr
`--local
.....`--gameserver
..........`--ut2003 
/var

Mit chroot wird es nun möglich einen Prozess, z.B. einen UT2003 Server in ein Verzeichnis einzusperren (vgl. jail). In diesem Fall wäre es möglich dass der UT2003 Server Prozess das Verzeichnis /usr/local/gameserver/ut2003 für seine Verzeichnisbaumwurzel hällt, es also als / sieht.
Wird der UT2003 Server kompromitiert erhällt der Angreifer nur zugriff auf alle Daten unterhalb von /usr/local/gameserver/ut2003 , womit er das System deutlich weniger schädigen kann als wenn er vollen Zugriff auf das gesammte Dateisystem hätte.
Der Vollständigkeit halber sei noch erwähnt dass es wohl auch möglich ist als Angreifer aus einem Chroot-Gefängnis auszubrechen, aber wenn der im chroot laufende Prozess nicht als root ausgeführt wird stehen die Chancen für den Angreifer sehr schlecht. Trozdem ist chroot keine Möglichkeit um unsichere Programme bzw. Programme mit bekannten Sicherheitslücken sicher zu machen. Chroot dient nur dazu die letzte Verteidungslienie zu sein, wenn z.B. eine neue Sicherheitslücke in einem Programm auftaucht, bevor man sie selbst kennt, oder Zeit zum updaten hatte. In diesem Fall kann chroot größere Schäden verhindern, ersetzt das Updaten jedoch nicht.

Vorbereiten des Chroot-Gefängnisses

Damit ein Prozess in einer Chroot-Umgebung laufen kann müssen einige Voraussetzungen erfüllt sein. Da das Programm in der Chroot-Umgebung keinen Zugriff mehr auf Daten hat, die außerhalb liegen müssen Kopien dieser Dateien im Chroot-Gefängnis vorhanden sein, und zwar in einer Verzeichnis-Hierachie in der sie das Programm vermuten würde, wenn es ohne chroot ausgeführt werden würde.
Ein Beispiel für eine Datei die sehr häufig benötigt wird ist das Device /dev/null. In unserem Fall wollen wir den UT2003 Server unter
/usr/local/gameserver/ut2003
chrooten, d.h. oben genannter Pfad wird zum / des Prozesses. Damit der Server das /dev/null findet müssen wir es an folgender Stelle anlegen:
/usr/local/gameserver/ut2003/dev/null
Zuerst legen wir den Ordner "dev" an, danach erstellen wir das null-device:

# cd ./dev && mknod ./null c 1 3

wobei wir uns durch das "cd ./dev" im Verzeichnis /usr/local/gameserver/ut2003/dev befinden.
Das c steht für charakter device (es gibt auch blockdevices) und 1 und 3 sind die major und minor device-numbers.
Danach noch ein
# chmod 0666 ./null

damit auch jeder User auf das Device schreiben darf.

Bibliotheken

Die Server-Binary braucht noch eine Reihe von Bibliotheken, die zur Laufzeit geladen werden. Eine Großzahl davon bringt der Server schon mit, sie befinden sich im "/System" Verzeichnis, unter anderem z.B. Engine.so (Man erkennt die Bibliotheken an der Dateiendumg .so). Außerdem kann man mit strace noch herausfinden welche Systembibliotheken gebraucht werden. Und der "Loader" für das Laden der Bibliotheken wird benötigt.
Wir legen zunächst ein "lib" Verzeichnis an:

# mkdir /usr/local/gameserver/ut2003/lib

danach kopieren wir und alle benötigenten Bibliotheken und den Loader dort hinein, das wären folgende:
ld-linux.so.2
libc.so.6
libdl.so.2
libm.so.6
libnsl.so.1
libpthread.so.0
libresolv.so.2
libnss_dns.so.2
libnss_files.so.2

Konfigurationsdateien in /etc

Da einige Bibliotheken auf bestimmte Dateien in /etc zugreifen, kann es Vorteile haben, wenn diese Dateien auch vorhanden sind. Nicht dass es nicht auch ohne diese Dateien ginge, aber z.B. die korrekte Zeitzone oder ein paar Informationen zum DNS-Setup des Rechners können hilfreich sein.

Wir legen uns also folgenden Ordner an: /usr/local/gameserver/ut2003/etc:

# mkdir /usr/local/gameserver/ut2003/etc

und kopieren folgende Dateien hinein:
etc/localtime
etc/resolv.conf
etc/nsswitch.conf
etc/hosts
etc/protocols
etc/services

ucc versus ucc-bin

Nornalerweise wird der UT2003-Dedicated Server über das Script ucc gestartet, dies fehlt zwar in der 2225er Version, funktioniert aber trozdem noch. Das Script bereitet ein paar Einstellungen für die dynamischen Bibliotheken vor und reicht ansonsten die Optionen für den Server einfach weiter und startet danach die ucc-bin Binary im System-Verzeichnis.
Wollen wir dieses Script im chroot benutzen, dann benötigen wir eine bash-shell um das Script auszuführen, das ist aber eine Sicherheitslücke, da wir einem Angreifer der den UT2003 Server kompromittiert keine Shell zur verfügung stellen wollen, also können wir das Script nicht benutzen.

Schwierig wird es beim Starten der ucc-bin Binary, diese erwartet es im System-Verzeichnis gestartet zu werden und nicht aus dem übergeordneten Verzeichnis, also habe ich ein kleines C-Program ( utwrapper ) geschrieben, welches erst das Verzeichnis wechselt (nach /System) und danach ucc-bin startet. Argumente werden einfach an die ucc-binary durchgereicht um kompatibel mit dem ucc-script zu bleiben. Somit gibt es keine Probleme mit den Pfaden zu Bibliotheken.

UT2003 im Chroot ausführen

Der UT2003 Server soll mit möglichts wenig Rechten laufen, also müssen wir einen eigenen User für den Server anlegen. In diesem Fall nennen wir ihn ut und ebenfalls wird die Gruppe ut benötigt.
Zum Starten des Servers im Chroot benutzen wir das Programm chrootuid und zwar sieht der Aufruf zum Starten so aus:

# chrootuid /usr/local/gameserver/ut2003 ut utwrapper <server-options>

wobei utwrapper das C-Programm ist, welches ins /System-Verzeichnis wechselt und den Server startet. <server-options> sind beliebige Argumente die man sonst an ucc oder ucc-bin übergeben würde, sie werden vom Wrapper einfach durchgereicht und von der ucc-binary interpretiert.

Fazit

Prozesse in einem Chroot-Gefängnis auszuführen macht die Prozesse nicht sicherer, kann aber größeren Schaden verhindern, für den Fall dass das Program eine Sicherheitslücke aufweist. Das ausbrechen aus einem Chroot-Gefängnis ist relativ schwer, solange man keine Root-Rechte hat, deshalb sollten die Prozesse im Chroot nie mit Root-Rechten laufen.
Prozesse in Chroot-Gefängnisse zu sperren sollte nicht als Möglichkeit gesehen werden, auf Updates oder eine sichere und fehlerfreie Konfiguration zu verzichten, es ist eher so dass ein Chroot-Gefängnis eine letzte Abwehrlinie ist, wenn es eigentlich schon zu spät ist. Der Schaden wird begrenzt, aber nicht verhindert.
Ob man den Mehraufwand der Chroot-Konfiguration in kauf nehmen will sollte davon abhängen wie sehr man dem Program vertraut. Wäre man 100% sicher, dass das Programm sicher ist dann könnte man sehr wohl auf das Chroot-Gefängnis verzichten.

 


 

Vielleicht auch interessant:


Über den Autor

Wenn Arne keine Artikel für planet-rcs schreibt dann programmiert er Webanwendungen in Python und trainiert mit dem Fahrrad für die Vattenfall-Cyclassics.

Feedback