staging.inyokaproject.org

Befehle in chroot Umgebung lauffähig machen

Achtung!

Die Verwendung dieses Howto geschieht auf eigene Gefahr. Bei Problemen mit der Anleitung melde dies bitte in der dazugehörigen Diskussion und wende dich zusätzlich an den Verfasser des Howtos.

Hinweis:

Diese Howto-Anleitung wurde zuletzt von bugblatterbeast am 22.12.2021 unter Ubuntu 20.04 - Server erfolgreich getestet.

Einfache Befehle in chroot Umgebung lauffähig machen

Dieser Beitrag behandelt die Bereitstellung von einfachen Kommandozeilenbefehlen in einer chroot-Umgebung, die dazu genutzt werden soll, den Zugriff und den Handlungsspielraum von Benutzern aus Sicherheitsgründen einzuschränken. In dieser Umgebung sollen nur zwingend notwendige Ressourcen und Binaries verfügbar gemacht werden.

Die hier vorgestellte Methode ermöglicht es einfache Kommandozeilenbefehle in einem Verzeichnis das als chroot verwendet wird auszuführen. Dabei werden nur Shared Objects (gemeinsam genutzte dynamische Bibliotheken) von denen eine offensichtliche Abhängigkeit besteht berücksichtigt. Shared Objects die zur Laufzeit nachgeladen werden können mit dieser Methode jedoch nicht ermittelt werden. Diese Vorgehensweise kann auch als Grundlage für die Bereitstellung komplexer Programme und Dienste in einer chroot Umgebung verwendet werden.

chroot Umgebungen

Achtung!

Generell ist eine chroot Umgebung kein Sicherheitsmerkmal. Die vorgesehenen Einsatzbereiche von chroot sind das Testen ob eine Software in einer bestimmten Umgebung funktioniert, die Bereitstellung von spezifischen Entwicklungsumgebungen und die Reparatur beschädigter Systeme.

Ursprünglich wurde chroot als Sicherheitsmerkmal vorgestellt, dieser Status wurde jedoch mittlerweile aberkannt. Unter der strengen Einhaltung bestimmter Regeln kann eine chroot Umgebung jedoch als angemessenes Sicherheitsmerkmal für bestimmte Einsatzbereiche anerkannt werden.

  • superuser Prozess darf auf keinen Fall in der chroot Umgebung möglich sein

  • UIDs und GIDs der chroot Benutzer dürfen auf keinen Fall mit UIDs und GIDs der Benutzer übereinstimmen die außerhalb der chroot Umgebung arbeiten

  • wurzelverzeichnis der chroot Umgebung muss root.root gehören und schreibgeschützt sein

  • sensible Dateien dürfen auf keinen fall schreibbar sein

  • in sensiblen Verzeichnissen darf es nicht erlaubt sein, Dateien anzulegen

  • keine sensiblen Devices bereitstellen (unverzichtbar sind meist die random devices, die standard Ein- und Ausgaben und tty)

  • den Ordner /proc nur mounten wenn es auf keinen Fall anders geht und dann die weiteren Sicherheitshinweise beachten

  • nach Möglichkeit sollte für jeden Anwendungsfall und jeden Benutzer eine eigene chroot Umgebung bereitgestellt werden

  • wenn möglich sollte das gesamte chroot Verzeichnis mit Unterordnern root.root gehören und schreibgeschützt sein

Wenn es unvermeidbar ist /proc zu mounten, muss man folgendes wissen: Alle proc Dateisysteme erben die mount Optionen vom zuerst gemounteten proc Dateisystem. Daher muss der Ordner /proc im Hauptsystem mit der Option hidepid=2 gemountet werden. Es ist unmöglich /proc im Hauptsystem mit der ro Option zu mounten. Daher sollte der /proc Ordner in der chroot Umgebung nicht normal gemountet sondern mit der Option ro gebunden werden. Zusätzlich kann man nicht unbedingt benötigte Unterverzeichnisse in der chroot Umgebung unlesbar zu machen indem man leere Dateien oder Ordner auf diese Verzeichnisse bindet (natürlich schreibgeschützt).

Fast alle Methoden aus einer chroot Umgebung auszubrechen setzen root Rechte voraus. Das Unterbinden von Superuser-Prozessen ist daher die wichtigste Regel. Ein gängiger Weg ohne root Rechte auszubrechen ist der Missbrauch des Befehls ptrace (angeblich bei Ubuntu unmöglich - habe ich aber noch nicht getestet). Dafür wird aber ein Prozess eines Benutzers mit gleicher UID/GID außerhalb der chroot benötigt. Aus diesem Grund dürfen die UIDs/GIDs der chroot Benutzer nicht mit denen der Benutzer außerhalb der chroot Umgebung übereinstimmen.

Wie überall muss man sich auch beim Einsatz von chroot Umgebungen klar machen, dass es so etwas wie absolute Sicherheit in der Realität nicht geben kann. Es gibt nur die Möglichkeit, den für einen erfolgreichen Angriff benötigten Aufwand zu erhöhen und damit auch die Wahrscheinlichkeit dass ein potentieller Angreifer die Motivation verliert. Ebenso wie ein Fahrradschloss immer in einem angemessenem Verhältnis zum Wert und dem Abstellort des Fahrrades stehen sollte, sollten auch IT-Sicherheitsmechanismen in einem angemessenem Verhältnis zum Wert der zu schützenden Daten, dem Ausmaß des möglichen Schadens und dem Ausmaß der Bedrohung stehen.

An dieser Stelle soll keine Aussage darüber gemacht werden, wie sinnvoll oder effektiv der Einsatz einer solchen chroot Umgebung im Einzelfall ist. Die Erkennung und Einschätzung von Sicherheitsrisiken erfordert viel Erfahrung und ist immer von der jeweiligen Situation abhängig. Die Vor- und Nachteile eines Einsatzes müssen für jeden Einzelfall sorgfältig ermittelt und abgewogen werden. Eine sorgfältig abgesicherte chroot Umgebung kann in bestimmten Fällen angemessenen Schutz bei einem leicht erhöhten aber immer noch verhältnismäßig geringem Sicherheitsbedarf geben. Eine chroot Umgebung kann sehr gut dazu geeignet sein vor Anwenderfehlern die nicht in boshafter Absicht erfolgen zu schützen.

Achtung!

Eine sorgfältig abgesicherte chroot Umgebung sollte noch lange nicht als ausreichendes Sicherheitsmerkmal für den Schutz von sensiblen Daten angesehen werden.

Manuelle Vorgehensweise

Im nun folgenden Beispiel sollen die Befehle cd, ls, cp und mv in einer minimalen Chroot-Umgebung bereitgestellt werden. Um überhaupt eine Kommandozeileninteraktion zu ermöglichen, muss eine shell vorhanden sein, die selbst auch als Befehl angesehen werden kann. Der Befehl cd ist schon in der bash (Bourne-again shell) integriert. Zusätzlich müssen noch die Befehle ls, cp und mv bereitgestellt werden. Für alle diese Befehle werden nun mit Hilfe des Befehls ldd aus dem Paket libc-bin die benötigten Shared Objects ermittelt.

$ ldd /bin/bash
	linux-vdso.so.1 (0x00007ffd9031f000)
	libtinfo.so.5 => /lib/x86_64-linux-gnu/libtinfo.so.5 (0x00007f26f370a000)
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f26f3506000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f26f3115000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f26f3c4e000)
$ ldd /bin/ls
	linux-vdso.so.1 (0x00007ffc4931f000)
	libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f3a1c66a000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f3a1c279000)
	libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007f3a1c007000)
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f3a1be03000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f3a1cab4000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f3a1bbe4000)
$ ldd /bin/cp
	linux-vdso.so.1 (0x00007ffeea1c2000)
	libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007fe782f88000)
	libacl.so.1 => /lib/x86_64-linux-gnu/libacl.so.1 (0x00007fe782d80000)
	libattr.so.1 => /lib/x86_64-linux-gnu/libattr.so.1 (0x00007fe782b7b000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe78278a000)
	libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007fe782518000)
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fe782314000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fe7833d3000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fe7820f5000)
$ ldd /bin/mv
	linux-vdso.so.1 (0x00007ffe44bfa000)
	libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f30d00e0000)
	libacl.so.1 => /lib/x86_64-linux-gnu/libacl.so.1 (0x00007f30cfed8000)
	libattr.so.1 => /lib/x86_64-linux-gnu/libattr.so.1 (0x00007f30cfcd3000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f30cf8e2000)
	libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007f30cf670000)
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f30cf46c000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f30d052a000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f30cf24d000) 

Wie man sieht sind die Abhängigkeiten von cp und mv identisch. Die Abhängigkeiten von ls sind auch in cp und mv enthalten. Die VDSO wird vom Kernel bereitgestellt und muss nicht weiter beachtet werden.

Es ist auch möglich, ldd mit mehreren Argumenten aufzurufen aber leider werden die Abhängigkeiten dann nicht zusammengefasst ausgegeben. Man erhält die gleiche Ausgabe als wenn man ldd nacheinander mit den einzelnen Argumenten aufgerufen hätte.

Zusammengefasst ergibt sich für dieses Beispiel nun folgende Liste mit Befehlen und Abhängigkeiten die in die chroot Umgebung kopiert werden müssen:

  • /bin/bash

  • /bin/ls

  • /bin/cp

  • /bin/mv

  • /lib/x86_64-linux-gnu/libacl.so.1

  • /lib/x86_64-linux-gnu/libattr.so.1

  • /lib/x86_64-linux-gnu/libc.so.6

  • /lib/x86_64-linux-gnu/libdl.so.2

  • /lib/x86_64-linux-gnu/libpcre.so.3

  • /lib/x86_64-linux-gnu/libpthread.so.0

  • /lib/x86_64-linux-gnu/libselinux.so.1

  • /lib/x86_64-linux-gnu/libtinfo.so.5

  • /lib64/ld-linux-x86-64.so.2

Um nun zum Beispiel das zu erstellende Verzeichnis /var/jail als eine chroot Umgebung vorzubereiten, in der eingesperrte Benutzer die oben genannten Befehle zur Verfügung haben, könnte man also folgendermaßen vorgehen.

sudo mkdir /var/jail
sudo mkdir /var/jail/bin
sudo mkdir /var/jail/lib
sudo mkdir /var/jail/lib/x86_64-linux-gnu
sudo mkdir /var/jail/lib64
sudo cp /bin/bash /bin/bash
sudo cp /bin/ls /bin/ls
sudo cp /bin/cp /bin/cp
sudo cp /bin/mv /bin/mv
sudo cp /lib/x86_64-linux-gnu/libacl.so.1 /var/jail/lib/x86_64-linux-gnu/libacl.so.1
sudo cp /lib/x86_64-linux-gnu/libattr.so.1 /var/jail/lib/x86_64-linux-gnu/libattr.so.1
sudo cp /lib/x86_64-linux-gnu/libc.so.6 /var/jail/lib/x86_64-linux-gnu/libc.so.6
sudo cp /lib/x86_64-linux-gnu/libdl.so.2 /var/jail/lib/x86_64-linux-gnu/libdl.so.2
sudo cp /lib/x86_64-linux-gnu/libpcre.so.3 /var/jail/lib/x86_64-linux-gnu/libpcre.so.3
sudo cp /lib/x86_64-linux-gnu/libpthread.so.0 /var/jail/lib/x86_64-linux-gnu/libpthread.so.0
sudo cp /lib/x86_64-linux-gnu/libselinux.so.1 /var/jail/lib/x86_64-linux-gnu/libselinux.so.1
sudo cp /lib/x86_64-linux-gnu/libtinfo.so.5 /var/jail/lib/x86_64-linux-gnu/libtinfo.so.5
sudo cp /lib64/ld-linux-x86-64.so.2 /var/jail/lib64/ld-linux-x86-64.so.2 

Diese Vorgehensweise ist aber sehr arbeitsintensiv und nur bedingt für chroot-Umgebungen geeignet die eine große Menge an Kommandos bereitstellen und die regelmäßig aktualisiert werden sollen. Es bietet sich an, die oben aufgeführten Arbeitsschritte zu automatisieren.

Automatisierung

Das folgende Shellscript führt die oben aufgeführten Arbeitsschritte selbstständig aus. Es handelt sich dabei um die Erweiterung eines Beispiels das auf der Seite https://www.cyberciti.biz/faq/bash-scripting-using-awk/ 🇬🇧 zu finden ist.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#!/bin/bash

[[ $(id -u) -eq 0 ]] || { echo >&2 "Error: Must be root to run $0"; exit 1; }

if [ $# -lt 2 ]; then
        echo "Syntax : $0 /path/to/jail /path/to/executable [/path/to/executable [...]]"
        echo "Example: $0 /var/jail /bin/bash /bin/ls /bin/cp /bin/mv"
        exit 1
fi

if [ ! -d $1 ]; then
        echo >&2 "Error: Directory $1 does not exist"
        exit 1;
fi

# dependencies
ALL_DEPS=()

# ld-linux
ALL_LDLS=()

for FILE in "${@:2}"
do
        if [ -f $FILE ]; then
                DIR="$(dirname $FILE)"
                if [ ! -d "$1""$DIR" ]; then
                        echo creating dir "$1""$DIR"
                        mkdir -p "$1""$DIR"
                        chown root.root "$1""$DIR"
                        chmod 755 "$1""$DIR"
                fi
                echo copying file "$FILE" to "$1""$FILE"
                cp -f -L --preserve=mode,timestamps "$FILE" "$1""$FILE"

                # get shared library dependencies
                DEPS="$(ldd $FILE | awk '{ print $3 }' | egrep -v ^'\(')"
                for DEP in $DEPS
                do
                        ALL_DEPS+=("$DEP")
                done

                # get ld-linux
                ALL_LDLS+=("$(ldd $FILE | grep 'ld-linux' | awk '{ print $1}')")
        else
                echo "Error: File $FILE does not exist"
        fi
done

echo preparing dependencies
# get unique array from all dependencies
DEPS=$(echo "${ALL_DEPS[@]}" | tr ' ' '\n' | sort -u)
# copy all dependencies to chroot
for DEP in $DEPS
do
        if [ -f $DEP ]; then
                DIR="$(dirname $DEP)"
                if [ ! -d "$1""$DIR" ]; then
                        echo creating dir "$1""$DIR"
                        mkdir -p "$1""$DIR"
                        chown root.root "$1""$DIR"
                        chmod 755 "$1""$DIR"
                fi
                echo copying file "$DEP" to "$1""$DEP"
                cp -f -L --preserve=mode,timestamps "$DEP" "$1""$DEP"
        fi
done

echo preparing ld-linux
LDLS=$(echo "${ALL_LDLS[@]}" | tr ' ' '\n' | sort -u)
for LDL in $LDLS
do
        if [ -f $LDL ]; then
                DIR="$(dirname $LDL)"
                if [ ! -d "$1""$DIR" ]; then
                        echo creating dir "$1""$DIR"
                        mkdir -p "$1""$DIR"
                        chown root.root "$1""$DIR"
                        chmod 755 "$1""$DIR"
                fi
                echo copying file "$LDL" to "$1""$LDL"
                cp -f -L --preserve=mode,timestamps "$LDL" "$1""$LDL"
        fi
done

Wenn man dieses Skript unter dem Namen cmd2chroot.sh gespeichert und ausführbar gemacht hat, reichen folgende Aufrufe um zu dem Ergebnis der oben aufgeführten manuellen Vorgehensweise zu gelangen.

sudo mkdir /var/jail
sudo ./cmd2chroot.sh /var/jail /bin/bash /bin/ls /bin/cp /bin/mv 

Anwendungsbeispiel

Nun kann man sshd so konfigurieren, dass bestimmte Benutzer automatisch im Verzeichnis /var/jail eingesperrt werden, wenn sie sich über ssh einloggen. Das geht beispielsweise indem man diese Benutzer einer speziell dafür erstellen Gruppe hinzufügt und für diese Gruppe eine Regel in der Datei /etc/ssh/sshd_config erstellt.

Match Group chrusers
    chrootDirectory /var/jail/
    X11Forwarding no
    AllowTcpForwarding no

Achtung: Falls diese Benutzer ein Home-Verzeichnis haben sollen, muss es selbstverständlich in diesem chroot Verzeichnis liegen und beim Anlegen des Benutzers muss der Pfad zum Home-Verzeichnis relativ zu dem chroot-Verzeichnis angegeben werden. So muss der Eintrag für einen Benutzer mit dem Home-Verzeichnis /var/jail/home/chroot_user in der Datei /etc/passwd aussehen:

chroot_user:x:1001:1001::/home/chroot_user:/bin/bash

Der Benutzer chroot_user wird nun nach der Anmeldung im Verzeichnis /var/jail eingesperrt, das aus seiner Sicht wie das Wurzelverzeichnis aussieht. Er hat in dieser minimalen Umgebung Zugriff auf die Kommandozeile und kann wie für dieses Beispiel gewünscht die Befehle cd, ls, cp und mv ausführen.

Mit folgenden Hilfsmitteln kann eine chroot Umgebung auf Sicherheitslücken getestet werden. Das Programm chw00t ist genau für diese Art Tests entwickelt und überprüft ob der Ausbruch aus einer chroot Umgebung mit gängigen Mitteln möglich ist. Die anderen Tools überprüfen noch ausführlicher als chw00t ob es für den ausführenden Benutzer möglich ist root-Rechte zu erlangen. Die Beschreibungen und Anweisungen dieser Tools sollten sorgfältig gelesen und verstanden werden um sicherzustellen, dass bei der Vorbereitung der Tests keine unnötigen Sicherheitslücken geöffnet oder nach den Tests keine durch den Testbetrieb entstandenen Sicherheitslücken zurückgelassen werden. Die Tools sollten auch auf keinen Fall nach einem Test auf dem Produktivsystem bleiben da es natürlich komplett unerwünscht wäre einem potentiellen Angreifer diese Hilfsmittel zur Verfügung zu stellen.

Diese Revision wurde am 22. Dezember 2021 19:43 von bugblatterbeast erstellt.
Die folgenden Schlagworte wurden dem Artikel zugewiesen: Howto