Schutz des Shell-Befehls mit String-Variable

9

Innerhalb einer Programmiersprache führe ich einen einfachen Shell-Befehl aus

cd var; echo > create_a_file_here

Dabei ist var eine Variable, die eine Zeichenfolge (hoffentlich) eines Verzeichnisses an der Stelle enthält, an der ich die Datei "create_a_file_here" erstellen möchte. Wenn jemand diese Codezeile sieht, kann er ausgenutzt werden, indem beispielsweise Folgendes zugewiesen wird:

var = "; rm -rf /"

Die Dinge können ziemlich hässlich werden. Eine Möglichkeit, den obigen Fall zu vermeiden, besteht darin, die Zeichenfolge in var nach Sonderzeichen wie ';' zu durchsuchen. vor dem Ausführen des Shell-Befehls, aber ich bezweifle, dass dies alle möglichen Exploits abdeckt.

Kennt jemand einen guten Weg, um sicherzustellen, dass "cd var" nur ein Verzeichnis ändert und sonst nichts?

ES___
quelle
4
Abhängig davon, wie Sie die Shell aufrufen können, können Sie sie auch varals Argument übergeben. ZB der Aufruf shmit Argumenten -c, 'cd "$1"; echo > create_a_file_here', 'sh', varArbeiten und benötigt keine Änderungen var. Das 'sh'Argument wird als übergeben $0.
IPSec
1
Welche Programmiersprache? Verwenden Sie POSIX shoder erstellen Sie eine eigene Programmiersprache mit einer ähnlichen Syntax, die jedoch erweitert wird, varanstatt dass Sie schreiben müssen cd "$var"? Oder ist das bashmit shopt -s cdable_vars? Oh, ich denke du meinst, dass ein anderes Programm eine Shell zwingt, um diese Befehle auszuführen. Also zitieren Sie einfach var, aber stellen Sie sicher, dass es kein Anführungszeichen selbst enthält ...
Peter Cordes
@PeterCordes Wenn Sie über Bash sprechen, ist eine Variable in doppelten Anführungszeichen, die ein doppeltes Anführungszeichen enthält, in Ordnung. zB s='"'; echo "$s"Drucke ".
Wjandrea
@WJAndrea: Ja, aber es gibt kein "Trumpfkarten" -Zitat, das nicht besiegt werden kann, wenn eine Variablenzuweisung aus nicht vertrauenswürdigen Eingaben erstellt wird. Oh, Lösung: Tun Sie dies var=untrusted stringim übergeordneten Programm, ebenso varwie eine Umgebungsvariable, die bereits beim Aufrufen festgelegt wurde sh. Dann müssen Sie es nur jedes Mal zitieren, wenn Sie es erweitern, was zuverlässig möglich ist. Ah, ich sehe , dass Idee ist bereits Teil von Stéphane Antwort> <.
Peter Cordes

Antworten:

9

Wenn ich richtig verstehe, varist eine Variable in Ihrer Programmiersprache.

In Ihrer Programmiersprache fordern Sie eine Shell auf, eine Zeichenfolge zu interpretieren, die die Verkettung "cd ", den Inhalt dieser Variablen und darstellt "; echo > create_a_file_here".

Wenn dann, ja, wenn der Inhalt von varnicht streng kontrolliert wird, handelt es sich um eine Sicherheitsanfälligkeit bezüglich Befehlsinjektion.

Sie können versuchen, den Inhalt der Variablen¹ in der Syntax der Shell korrekt in Anführungszeichen zu setzen, damit sie garantiert als einzelnes Argument an das integrierte cdElement übergeben wird.

Ein anderer Ansatz wäre, den Inhalt dieser Variablen auf eine andere Weise zu übergeben. Ein naheliegender Weg wäre, dies in einer Umgebungsvariablen zu übergeben. Zum Beispiel in C:

char *var =  "; rm -rf /";
setenv("DIR", var, 1);
system("CDPATH= cd -P -- \"$DIR\" && echo something > create_a_file_here");

Dieses Mal ist der Code, den Sie von der Shell zur Interpretation auffordern, behoben. Wir müssen ihn weiterhin ordnungsgemäß in die Syntax der Shell schreiben (hier wird angenommen, dass es sich um eine POSIX-kompatible Shell handelt):

  • Die Erweiterung der Shell-Variablen muss in Anführungszeichen gesetzt werden, um Split + Glob zu verhindern
  • Sie müssen -Pfür cdeine einfache tunchdir()
  • Sie müssen --das Ende der Optionen markieren, um Probleme beim varStarten mit -(oder +in einigen Shells) zu vermeiden.
  • Wir setzen CDPATHauf die leere Zeichenfolge, falls sie sich in der Umgebung befindet
  • Wir führen den echoBefehl nur aus, wenn er cderfolgreich war.

Es gibt (mindestens) ein verbleibendes Problem: Wenn dies der Fall varist -, wird das Verzeichnis nicht in das aufgerufene Verzeichnis, -sondern in das vorherige Verzeichnis (wie darin gespeichert $OLDPWD) verschoben und es OLDPWD=- CDPATH= cd -P -- "$DIR"wird nicht garantiert , dass es umgeht . Sie brauchen also etwas wie:

system(
  "case $DIR in\n"
  " (-) CDPATH= cd -P ./-;;\n"
  " (*) CDPATH= cd -P -- \"$DIR\";;\n"
  "esac && ....");

¹ Beachten Sie, dass system(concat("cd \"", var, "\"; echo..."));es nicht der richtige Weg ist , nur a zu tun. Sie würden das Problem nur verschieben.

Zum Beispiel var = "$(rm -rf /)"wäre a immer noch ein Problem.

Die einzige zuverlässige Möglichkeit, Text für Bourne-ähnliche Shells in Anführungszeichen zu setzen, besteht darin, einfache Anführungszeichen zu verwenden und sich auch um die einfachen Anführungszeichen zu kümmern, die in der Zeichenfolge auftreten können. Wenden Sie sich zum Beispiel char *var = "ab'cd"an a char *escaped_var = "'ab'\\''cd'". Das heißt, ersetzen Sie alle 'zu '\''und wickeln Sie das Ganze innen '...'.

Das setzt voraus , dass nach wie vor , dass Zeichenfolge in Anführungszeichen nicht innerhalb von Backticks verwendet wird, und Sie würden immer noch brauchen die --, -P, &&, CDPATH=...

Stéphane Chazelas
quelle
12

Einfache Lösung: Rufen Sie die Shell nicht aus Ihrem Programm auf. Überhaupt.

Ihr Beispiel hier ist trivial. Das Ändern des Verzeichnisses und das Erstellen von Dateien sollte in jeder Programmiersprache einfach sein. Aber selbst wenn Sie einen externen Befehl ausführen müssen, ist dies normalerweise nicht über die Shell erforderlich.

Also, zB in Python, anstatt zu laufen os.system("somecmd " + somearg), verwenden subprocess.run(["somecmd", somearg]). system()Verwenden Sie in C stattdessen fork()und exec()(oder suchen Sie eine Bibliothek, die dies tut).

Wenn Sie die Shell verwenden müssen, zitieren Sie die Befehlszeilenargumente oder übergeben Sie sie wie in Stéphanes Antwort an die Umgebung . Auch, wenn Sie finden sich Sorgen Sie sich über Sonderzeichen, die richtige Lösung ist, nicht zu versuchen , heraus zu filtern (schwarze Liste) potenziell gefährliche Zeichen, sondern nur zu halten bekannte Zeichen sicher zu sein (Whitelist).

Lassen Sie nur die Zeichen zu, deren Funktionen Sie kennen. Auf diese Weise besteht ein geringeres Risiko, dass etwas fehlt. Das Endergebnis könnte sein, dass Sie sich nur dafür entscheiden, es zuzulassen [a-zA-Z0-9_], aber das könnte gerade ausreichen, um die Arbeit zu erledigen. Möglicherweise möchten Sie auch überprüfen, ob Ihr Gebietsschema und Ihr Toolset keine Buchstaben mit Akzent wie äund ödarin enthalten. Sie werden wahrscheinlich von keiner Shell als besonders angesehen, aber auch hier ist es besser, sicher zu sein, ob sie passen oder nicht.

ilkkachu
quelle
1
Und stellen Sie sicher, dass alles, was Sie zum Abgleichen [a-zA-Z0-9]verwenden, keine Dinge enthält, àderen Codierung auch von einigen Shells (wie bash) in einigen Gebietsschemas falsch interpretiert werden könnte .
Stéphane Chazelas
@ StéphaneChazelas (aus Interesse :) sind sie (und knapp unter den betroffenen Orten?)
Wilf
10

Innerhalb einer Programmiersprache sollte es bessere Möglichkeiten geben, als Shell-Befehle auszuführen. Wenn Sie beispielsweise durch cd vardas Äquivalent Ihrer Programmiersprache ersetzen, chdir (var);sollte sichergestellt werden, dass jeder Trick mit dem Wert " varnur" zu einem Fehler "Verzeichnis nicht gefunden" führt und nicht zu unbeabsichtigten und möglicherweise böswilligen Aktionen.

Sie können auch absolute Pfade verwenden, anstatt Verzeichnisse zu ändern. Verketten Sie einfach den Verzeichnisnamen, den Schrägstrich und den Dateinamen, den Sie verwenden möchten.

In C könnte ich so etwas tun:

char filepath[PATH_MAX];  /* alternative constant: MAXPATHLEN */

/* Join directory name in var and the filename, guarding against exceeding PATH_MAX */
snprintf (filepath, PATH_MAX, "%s/%s", var, "create_a_file_here");

/* create an empty file/truncate an existing one */
fclose (fopen (filepath, "w") );

Sicher kann Ihre Programmiersprache etwas Ähnliches tun?

telcoM
quelle
Vielen Dank für Ihre Antwort, aber leider muss ich die Problemumgehung mit den Bash-Befehlen verwenden. Ich habe getestet, die Variable zu zitieren - wie von Muru vorgeschlagen - und es scheint zu funktionieren!
ES___