Wie kann ich explizit und sicher die Verwendung eines eingebauten Befehls in bash erzwingen?

19

Es gibt eine ähnliche Frage , die sich mit dem 'Wrapping'-Szenario befasst, bei dem Sie beispielsweise cdeinen Befehl verwenden möchten , der das eingebaute Element aufruft cd.

Angesichts von Shellshock et al. Und der Tatsache, dass Bash Funktionen aus der Umgebung importiert, habe ich einige Tests durchgeführt und kann das Builtin nicht sicher cdaus meinem Skript aufrufen .

Bedenken Sie

cd() { echo "muahaha"; }
export -f cd

Alle Skripte, die in dieser Umgebung mit aufgerufen cdwerden, brechen ab (berücksichtigen Sie die Auswirkungen von etwas Ähnlichem cd dir && rm -rf .).

Es gibt Befehle zum Überprüfen des Typs eines Befehls (bequemer Aufruf type) und Befehle zum Ausführen der eingebauten Version anstelle einer Funktion ( builtinund command). Aber siehe da, diese können auch mit Funktionen überschrieben werden

builtin() { "$@"; }
command() { "$@"; }
type() { echo "$1 is a shell builtin"; }

Ergibt Folgendes:

$ type cd
cd is a shell builtin
$ cd x
muahaha
$ builtin cd x
muahaha
$ command cd x
muahaha

Gibt es eine Möglichkeit, bash sicher zur Verwendung des integrierten Befehls zu zwingen oder zumindest zu erkennen, dass ein Befehl kein integrierter Befehl ist, ohne die gesamte Umgebung zu löschen?

Mir ist klar, dass Sie wahrscheinlich sowieso bescheuert sind, wenn jemand Ihre Umgebung kontrolliert, aber zumindest für Aliase können Sie den Alias ​​nicht aufrufen, indem Sie ein \davor einfügen .

falstro
quelle
Sie können Ihr Skript zuvor mit dem folgenden envBefehl env -i <SCRIPT.sh>
ausführen
Nur wenn envnicht auch als Funktion neu definiert. Das ist erschreckend. Ich dachte zuerst, dass Sonderzeichen helfen würden - Aufrufen mit vollem Pfad, einschließlich der /Verwendung von .to source und so weiter. Diese können aber auch für Funktionsnamen verwendet werden! Sie können jede gewünschte Funktion neu definieren, aber es ist schwierig, zum Aufrufen des ursprünglichen Befehls zurückzukehren.
Orion
1
Stimmt, aber wenn Sie ein Skript auf einem kompromittierten Computer ausführen, sind Sie trotzdem durchgeknallt. Alternativ schreiben Sie Ihr Skript in, #/bin/shwenn dies nicht die standardmäßige interaktive Shell ist.
Arkadiusz Drabczyk

Antworten:

16

Olivier D ist fast richtig, aber Sie müssen POSIXLY_CORRECT=1vor dem Laufen einstellen unset. POSIX kennt sich mit speziellen integrierten Funktionen aus , und bash unterstützt dies . unsetist eine solche eingebaut. Suchen nachSPECIAL_BUILTIN in builtins/*.cin der Bash - Quelle für eine Liste, enthält es set, unset, export, evalund source.

$ unset() { echo muahaha-unset; }
$ unset unset
muahaha-unset
$ POSIXLY_CORRECT=1
$ unset unset

Der Schurke unsetist nun aus der Umgebung entfernt, wenn Sie unset command, type, builtindann sollten Sie in der Lage sein, zu gehen , aber unset POSIXLY_CORRECTwenn Sie auf nicht-POSIX - Verhalten oder erweitern bash Funktionen angewiesen sind.

Hiermit werden jedoch keine Aliase behandelt. Sie müssen \unsetalso sicherstellen, dass es in der interaktiven Shell funktioniert (oder immer, falls dies tatsächlich der Fall expand_aliasesist).

Für die paranoid, dies sollte alles reparieren, denke ich:

POSIXLY_CORRECT=1
\unset -f help read unset
\unset POSIXLY_CORRECT
re='^([a-z:.\[]+):' # =~ is troublesome to escape
while \read cmd; do 
    [[ "$cmd" =~ $re ]] && \unset -f ${BASH_REMATCH[1]}; 
done < <( \help -s "*" )

( while, do,done Und [[sind reservierte Worte und keine Vorsichtsmaßnahmen müssen.) Hinweis verwenden wir unset -fauf ungesetzt Funktionen sicher sein, obwohl Variablen und Funktionen denselben Namespace ist es möglich , beide gleichzeitig zu existieren (dank Etan Reisner) , in dem Fall zweimaliges Einstellen würde ebenfalls ausreichen. Sie können eine Funktion schreibgeschützt markieren. Bash hindert Sie nicht daran, eine schreibgeschützte Funktion bis einschließlich Bash-4.2 zu deaktivieren. Bash-4.3 hindert Sie daran, berücksichtigt jedoch weiterhin die speziellen eingebauten Funktionen, wenn diese POSIXLY_CORRECTgesetzt sind.

Ein Readonly POSIXLY_CORRECT ist kein echtes Problem, dies ist kein Boolescher Wert oder ein Flag, dessen Vorhandensein den POSIX-Modus aktiviert. Wenn es also als Readonly existiert, können Sie sich auf POSIX-Funktionen verlassen, auch wenn der Wert leer oder 0 ist. Das müssen Sie einfach ungeklärte problematische Funktionen anders als oben, vielleicht mit etwas Ausschneiden und Einfügen:

\help -s "*" | while IFS=": " read cmd junk; do echo \\unset -f $cmd; done

(und ignorieren Sie alle Fehler) oder engagieren Sie sich in einem anderen Scriptobatics .


Weitere Hinweise:

  • functionIst ein reserviertes Wort, kann es mit einem Alias ​​versehen, aber nicht mit einer Funktion überschrieben werden. (Aliasing functionist leicht störend, weil\function es als Umgehungsmöglichkeit nicht akzeptabel ist.)
  • [[, ]]sind reservierte Wörter, sie können mit Alias ​​versehen werden (was ignoriert wird), aber nicht mit einer Funktion überschrieben werden (obwohl Funktionen so benannt werden können)
  • (( ist weder ein gültiger Name für eine Funktion noch ein Alias
mr.spuratic
quelle
Interessant, danke! Ich scheint mir seltsam , dass bash auf Standard würde nicht schützen unsetet al entfernt, überschrieben.
Falstro
Das braucht das -fArgument, unsetnicht wahr? Wenn andernfalls sowohl eine Variable als auch eine Funktion definiert sind, die den gleichen Namen wie eine eingebaute haben, unsetwird zuerst die Variable ausgewählt, die nicht gesetzt werden soll. Auch dies schlägt fehl, wenn eine der verdeckenden Funktionen eingestellt readonlyist, nicht wahr? (Obwohl dies erkennbar ist und zu einem schwerwiegenden Fehler führen kann.) Auch hier fehlt das [eingebaute System, wie es scheint.
Etan Reisner
1
Ich denke nicht, \unset -f unsetdass es Sinn macht, vorausgesetzt, es wird in der Leseschleife gemacht. Wenn unsetes sich um eine Funktion handelt, die Sie \unsetnormalerweise anstelle der eingebauten auflösen würden, können Sie sie \unsetsicher verwenden, solange der POSIX-Modus aktiv ist. Explizit Aufruf \unset -fauf [, .und :vielleicht eine gute Idee , wenn es , wie Ihr regex schließt sie. Ich würde auch -fdie \unsetin der Schleife hinzufügen , und würde \unset POSIXLY_CORRECTnach der Schleife statt vor. \unalias -a(after \unset -f unalias) erlaubt es einem auch, bei nachfolgenden Befehlen sicher auf die Flucht zu verzichten.
Adrian Günter
1
@ AdrianGünter Versuch: [[ ${POSIXLY_CORRECT?non-POSIX mode shell is untrusted}x ]]Dies führt dazu, dass ein nicht interaktives Skript mit dem angegebenen Fehler beendet wird, wenn die Variable nicht gesetzt ist, oder fortgesetzt wird, wenn sie gesetzt ist (leer oder ein beliebiger Wert).
mr.spuratic
1
"you must use \unset" ... Es sollte beachtet werden, dass das Zitieren eines oder mehrerer Zeichen funktionieren würde, um die Alias-Erweiterung zu vermeiden, nicht nur die erste. un"se"twürde genauso gut funktionieren, abeit weniger lesbar. Msgstr "Das erste Wort jedes einfachen Befehls wird, wenn es nicht in Anführungszeichen gesetzt ist, überprüft, um festzustellen, ob es einen Alias ​​hat." - Bash Ref
Robin A. Meade
6

Mir ist klar, dass Sie wahrscheinlich sowieso durcheinander sind, wenn jemand Ihre Umgebung kontrolliert

Ja das. Wenn Sie ein Skript in einer unbekannten Umgebung LD_PRELOADausführen , können alle möglichen Probleme auftreten, angefangen damit, dass der Shell-Prozess beliebigen Code ausführt, bevor er Ihr Skript überhaupt liest. Der Versuch, innerhalb des Skripts vor einer feindlichen Umgebung zu schützen, ist erfolglos.

Sudo hat die Umgebung desinfiziert, indem es seit über einem Jahrzehnt alles entfernt, was wie eine Bash-Funktionsdefinition aussieht. Seit Shellshock sind andere Umgebungen, in denen Shell-Skripts in einer nicht vollständig vertrauenswürdigen Umgebung ausgeführt werden, dem Beispiel gefolgt.

Sie können ein Skript nicht sicher in einer Umgebung ausführen, die von einer nicht vertrauenswürdigen Entität festgelegt wurde. Sich über Funktionsdefinitionen Gedanken zu machen, ist also nicht produktiv. Bereinigen Sie Ihre Umgebung und verwenden Sie dabei Variablen, die von bash als Funktionsdefinitionen interpretiert werden.

Gilles 'SO - hör auf böse zu sein'
quelle
1
Ich bin damit nicht einverstanden. Sicherheit ist keine binäre Angelegenheit, die sich um "saniert" oder "nicht saniert" dreht. Es geht darum, Möglichkeiten zur Ausbeutung zu beseitigen. Und leider bedeutet die Komplexität moderner Systeme, dass es viele Nutzungsmöglichkeiten gibt, die richtig abgeschlossen werden müssen. Viele Exploit-Möglichkeiten drehen sich um harmlos erscheinende Upstream-Angriffe wie das Ändern der PATH-Variablen, die Neudefinition von integrierten Funktionen usw. Bieten Sie einer einzelnen Person oder einem Programm die Möglichkeit, dies zu tun, und Ihr gesamtes System ist anfällig.
Dejay Clayton
@DejayClayton Mit Ausnahme des ersten Satzes stimme ich Ihrem Kommentar voll und ganz zu, da ich nicht sehe, wie er meiner Antwort in irgendeiner Weise widerspricht. Das Entfernen einer Ausnutzungsmöglichkeit hilft, aber nicht nur die Hälfte davon zu entfernen, wenn eine geringfügige Änderung des Angriffs es ihm ermöglicht, weiterzuarbeiten.
Gilles 'SO - hör auf böse zu sein'
Mein Punkt ist, dass Sie nicht einfach "Ihre Umgebung desinfizieren" und sich dann keine Gedanken mehr über verschiedene Angriffsmethoden machen können. Manchmal lohnt es sich, "innerhalb des Skripts vor einer feindlichen Umgebung zu schützen". Selbst wenn Sie das Problem mit der Funktionsdefinition lösen, müssen Sie sich immer noch Gedanken darüber machen, ob beispielsweise ein PATH in einem Verzeichnis ein benutzerdefiniertes Skript mit dem Namen "cat" oder "ls" und anschließend ein Skript mit dem Namen "cat" vorhanden ist "or" ls "anstelle von" / bin / cat "und" / bin / ls "führt effektiv einen Exploit aus.
Dejay Clayton
@DejayClayton Offensichtlich hilft die Bereinigung der Umgebung nicht bei Angriffsvektoren, die nicht mit der Umgebung zusammenhängen. (Zum Beispiel könnte ein Skript, das /bin/cateine Chroot aufruft, alles aufrufen.) Wenn Sie die Umgebung bereinigen, erhalten Sie einen gesunden Eindruck PATH(vorausgesetzt, Sie tun es natürlich richtig). Ich verstehe deinen Standpunkt immer noch nicht.
Gilles 'SO - hör auf böse zu sein'
In Anbetracht dessen, dass PATH eine der größten Möglichkeiten für Exploits ist und dass viele fragwürdige PATH-Praktiken als empfohlener Ratschlag auch in Sicherheitsblogs dokumentiert sind, und auch in Anbetracht der Tatsache, dass manchmal installierte Anwendungen PATH neu anordnen, um sicherzustellen, dass solche Apps funktionieren, kann ich nicht nachvollziehen, wie defensive Kodierung innerhalb eines Skripts wäre eine schlechte Idee. Was Sie für einen vernünftigen Pfad halten, kann tatsächlich anfällig für Exploits sein, auf die Sie nicht gestoßen sind.
Dejay Clayton
1

Sie können verwenden unset -f, um die Funktionen builtin, command und type zu entfernen.

odc
quelle
2
Entschuldigung, unset() { true; }kümmert sich darum.
Falstro
1
Verdammt! Die Auflistung definierter Funktionen hängt auch von einem eingebauten Code ab. Ich gebe auf!
odc