Ausführen eines Bash-Skripts oder einer AC-Binärdatei auf einem Dateisystem mit der Option noexec

10

Kann jemand im Detail erklären, was mit dem Folgenden los ist. Stellen wir uns vor, ich mounte ein Verzeichnis mit der folgenden noexecOption:

mount -o noexec /dev/mapper/fedora-data /data

Um dies zu überprüfen, lief ich mount | grep data:

/dev/mapper/fedora-data on /data type ext4 (rw,noexec,relatime,seclabel,data=ordered)

Jetzt /dataerstelle ich ein einfaches Skript mit dem Namen hello_world:

#!/bin/bash

echo "Hello World"
whoami

Also habe ich das Skript ausführbar gemacht von chmod u+x hello_world(dies hat jedoch keine Auswirkungen auf ein Dateisystem mit noexecOptionen) und versucht, es auszuführen :

# ./hello_world
-bash: ./hello_world: Permission denied

Das Voranstellen bashder Datei ergibt jedoch Folgendes :

# bash hello_world
Hello World
root

Also habe ich eine einfache hello_world.cmit folgenden Inhalten erstellt:

#include <stdio.h>

int main()
{
    printf("Hello World\n");
    return 0;
}

Kompiliert mit cc -o hello_world hello_world.c

Jetzt läuft:

# ./hello_world
-bash: ./hello_world: Permission denied

Also habe ich versucht, es mit auszuführen

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

Der Fehler:

./hello_world: error while loading shared libraries: ./hello_world: failed to map segment from shared object: Operation not permitted

Dies ist natürlich wahr, da lddFolgendes zurückgegeben wird:

ldd hello_world
ldd: warning: you do not have execution permission for `./hello_world'
    not a dynamic executable

Auf einem anderen System, auf dem die noexecMount-Option nicht angewendet wird, wird Folgendes angezeigt:

ldd hello_world
    linux-vdso.so.1 (0x00007ffc1c127000)
    libc.so.6 => /lib64/libc.so.6 (0x00007facd9d5a000)
    /lib64/ld-linux-x86-64.so.2 (0x00007facd9f3e000)

Meine Frage lautet nun: Warum funktioniert das Ausführen eines Bash-Skripts auf einem Dateisystem mit noexecOption, jedoch nicht auf einem ckompilierten Programm? Was passiert unter der Haube?

Valentin Bajrami
quelle
Hinweis: chmod o+x «filename»Erteilt dem Benutzer oder der Gruppe, die die Datei besitzt, keine Ausführungsberechtigung. Verwenden Sie chmod ugo+x «filename»oderchmod +x «filename»
Strg-Alt-Delor
Du hast recht. Ich habe verwendet, chmod u+x hello_worldaber vergessen, das in der Frage zu ändern. Vielen Dank für den Hinweis.
Valentin Bajrami

Antworten:

26

Was in beiden Fällen passiert, ist dasselbe: Um eine Datei direkt auszuführen, muss das Ausführungsbit gesetzt werden und das Dateisystem kann noexec nicht gemountet werden. Aber diese Dinge hindern nichts daran , diese Dateien zu lesen .

Wenn das Bash-Skript als ausgeführt wird ./hello_worldund die Datei nicht ausführbar ist (entweder kein Exec-Berechtigungsbit oder noexec im Dateisystem), wird die #!Zeile nicht einmal überprüft , da das System die Datei nicht einmal lädt. Das Skript wird niemals im relevanten Sinne "ausgeführt".

Im Fall von bash ./hello_world, na ja, die Option noexec-Dateisystem ist einfach nicht so intelligent, wie Sie es gerne hätten. Der bashBefehl, der ausgeführt wird /bin/bash, ist und /binbefindet sich nicht in einem Dateisystem mit noexec. Es läuft also kein Problem. Dem System ist es egal, dass Bash (oder Python oder Perl oder was auch immer) ein Interpreter ist. Es wird nur der Befehl ausgeführt, den Sie ( /bin/bash) mit dem Argument gegeben haben, das zufällig eine Datei ist. Im Fall von Bash oder einer anderen Shell enthält diese Datei eine Liste der auszuführenden Befehle, aber jetzt haben wir alles "hinter uns", was die Dateiausführungsbits überprüfen soll. Diese Überprüfung ist nicht verantwortlich für das, was später passiert.

Betrachten Sie diesen Fall:

$ cat hello_world | /bin/bash

… Oder für diejenigen, die den sinnlosen Gebrauch von Katzen nicht mögen:

$ /bin/bash < hello_world

Die "shbang" #!-Sequenz am Anfang einer Datei ist nur eine nette Magie, um effektiv dasselbe zu tun, wenn Sie versuchen, die Datei als Befehl auszuführen. Dieser Artikel von LWN.net ist möglicherweise hilfreich: Wie Programme ausgeführt werden .

mattdm
quelle
Vielen Dank für Ihre ausführliche Erklärung und der Link zum Artikel von LWN.net ist sehr hilfreich!
Valentin Bajrami
1
Wenn meine Vermutung richtig ist, spielt auch eine Rolle, /bin/bashdie im Dateisystem ohne gespeichert wird noexec, während sich das OP-Skript im noexecDateisystem befindet.
Sergiy Kolodyazhnyy
@SergiyKolodyazhnyy Ja.
Mattdm
11

In früheren Antworten wird erläutert, warum die noexecEinstellung die Ausführung eines Skripts nicht verhindert, wenn der Interpreter (in Ihrem Fall /bin/bash) explizit über die Befehlszeile aufgerufen wird. Aber wenn das alles gewesen wäre, hätte dieser Befehl auch funktioniert:

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

Und wie Sie bemerkt haben, funktioniert das nicht. Das liegt daran, dass es auch noexeceinen anderen Effekt hat. Der Kernel lässt keine PROT_EXECaktivierten Speicherdateien von diesem Dateisystem zu, wenn diese aktiviert sind.

Speicherzuordnungsdateien werden in mehreren Szenarien verwendet. Die beiden häufigsten Szenarien betreffen ausführbare Dateien und Bibliotheken. Wenn ein Programm mit dem execveSystemaufruf gestartet wird, erstellt der Kernel intern Speicherzuordnungen für den Linker und die ausführbare Datei. Alle anderen benötigten Bibliotheken werden vom Linker über den mmapSystemaufruf mit PROT_EXECaktiviertem Speicher zugeordnet . Wenn Sie versuchen, eine Bibliothek aus einem Dateisystem mit noexecdem Kernel zu verwenden, wird der mmapAufruf abgelehnt .

Wenn Sie /lib64/ld-linux-x86-64.so.2 hello_worldden execveSystemaufruf aufrufen, wird nur eine Speicherzuordnung für den Linker erstellt, und der Linker öffnet die hello_worldausführbare Datei und versucht, eine Speicherzuordnung auf die gleiche Weise zu erstellen, wie dies für eine Bibliothek der Fall gewesen wäre. Und an diesem Punkt weigert sich der Kernel, den mmapAufruf auszuführen , und Sie erhalten den Fehler:

./hello_world: error while loading shared libraries: ./hello_world: failed to map segment from shared object: Operation not permitted

Die noexecEinstellung ermöglicht weiterhin Speicherzuordnungen ohne Ausführungsberechtigung (wie dies manchmal für Datendateien verwendet wird) und ermöglicht auch das normale Lesen von Dateien, weshalb dies bash hello_worldfür Sie funktioniert hat.

Kasperd
quelle
Faszinierend! Ich habe hier viel gelernt. Vielen Dank.
Bruno Bronosky
3

Befehl auf folgende Weise ausführen:

bash hello_world

Sie bashlesen aus einer Datei hello_world(was nicht verboten ist).

In anderen Fällen versucht das Betriebssystem, diese Datei auszuführen, hello_worldund schlägt aufgrund des noexecFlags fehl

Romeo Ninov
quelle
Nun, der Kernel wird den Shebang überprüfen, um zu entscheiden, welche Anweisungen zu erwarten / auszuführen sind. Laufen ./hello_world würde also das gleiche Recht bedeuten oder verpasse ich hier den Punkt?
Valentin Bajrami
@ ValentinBajrami, nein, sie führen auf unterschiedliche Weise aus. Denken Sie an kein Ausführungsflag in der Datei.
Romeo Ninov
2

Wenn Sie das Skript über Bash ausführen, liest es nur die Datei ein und interpretiert sie.

Wenn Sie jedoch den Namen an den Kernel übergeben, wird die Datei wirklich auf "#!" und lädt den gemäß der Kerneloption "CONFIG_BINFMT_SCRIPT" angegebenen Interpreter. Es sagt:

Sagen Sie hier Y, wenn Sie interpretierte Skripte ausführen möchten, die mit "#!" Beginnen. gefolgt vom Weg zu einem Dolmetscher.

Sie können diese Unterstützung als Modul erstellen. Bis dieses Modul geladen wird, können Sie jedoch keine Skripte ausführen. Wenn Sie dieses Modul aus einem Initramfs laden möchten, darf der Teil des Initramfs vor dem Laden dieses Moduls nur aus kompilierten Binärdateien bestehen.

Die meisten Systeme werden nicht gestartet, wenn Sie hier M oder N sagen. Wenn Sie sich nicht sicher sind, sagen Sie Y.

Das Obige ist der Hilfetext, der dieser Option zugeordnet ist. Für einen weiteren interessanten Unterschied. Ich habe mein Drehbuch geschrieben:

> cat myprog.sh
#!/bin/cat

echo "Hello World"
> chmod +x myprog.sh

Wenn Sie es mit bash ausführen, wird immer noch der Bash-Interpreter ausgeführt:

> bash myprog.sh
Hello World

Der Kernel tut jetzt jedoch:

> myprog.sh
#!/bin/cat

echo "Hello World"

Der Kernel druckte das Skript einschließlich der ersten Zeile aus, da es 'cat' hieß.

Im Fall des C-Programms rufen Sie keinen Interpreter auf, um die Binärdatei auszuführen. Der Kernel versucht es direkt auszuführen. Wenn Sie Ihre gesamte ausführbare Datei mit einigen Debuggern in den Speicher geladen haben, können Sie Ihr Programm dennoch "ausführen", während es über den Debugger geladen wird.

Die Option 'noexec' ist wie das Ausschalten des Ausführungsbits in Ihrer Binärdatei und verhindert, dass der Kernel die Binärdatei "nativ" ausführt.

Dies macht übrigens einen Unterschied, wenn für Ihr Programm ein SetUID-Bit im Programm gesetzt war. Wenn Sie es mit einem Interpreter laden, wird Ihre UID nicht festgelegt. Nur wenn der Kernel sie lädt, kann dieses Privileg aktiviert werden.

FWIW - Windows hat den gleichen Mechanismus.

Wenn Sie ".sh" als "ausführbares Suffix" wie ".exe" oder ".vbs" hinzufügen, führt Windows Ihre Datei automatisch aus, je nachdem, wie Sie die auszuführenden ".sh" -Dateien eingerichtet haben. Theoretisch könnten Sie ".txt" -Dateien so einrichten, dass sie automatisch eingegeben werden, wenn Sie ihren Namen in die Befehlszeile eingeben.

Ebenso können Sie ein Programm kurz aufrufen, um Textdateien auf dem Bildschirm auszudrucken. Dies ist ein Grund, sich nicht an einem öffentlichen Ort angemeldet zu lassen.

Astara
quelle
-1

Weil sich die ausführbare Bash-Datei nicht auf dem Dateisystem befindet.

basteln
quelle
2
Das ist wahr, aber das Ausführen ./hello_worldwürde auch uns, /bin/bashdie sich nicht auf einem Dateisystem mit noexecaktivierter Option befinden. Wenn das wahr ist c, würde das Programm auch korrekt ausgeführt werden?
Valentin Bajrami
1
Nee. So funktioniert das nicht.
Tink
3
@ValentinBajrami Die Shebang-Zeile im Skript wird erst gelesen, nachdem die Hürden noexec und execute-bit beseitigt wurden.
Blrfl