Sicherheitsauswirkungen der Ausführung von Perl -ne '…' *

27

Anscheinend läuft:

perl -n -e 'some perl code' *

Oder

find . ... -exec perl -n -e '...' {} +

(gleich mit -pstatt -n)

Oder

perl -e 'some code using <>' *

Wird häufig in Einzeilern verwendet, die auf dieser Website veröffentlicht werden, hat dies Auswirkungen auf die Sicherheit. Was ist das Problem? Wie vermeide ich es?

Stéphane Chazelas
quelle

Antworten:

33

Was ist das Problem

Erstens, wie bei vielen Dienstprogrammen, treten Probleme mit Dateinamen auf, die mit beginnen -. Während in:

sh -c 'inline sh script here' other args

Die anderen Argumente werden an die weitergereicht inline sh script; mit dem perlÄquivalent,

perl -e 'inline perl script here' other args

Die anderen Argumente werden nach weiteren Optionen durchsucht, um zuerst Perl und nicht das Inline-Skript zu verwenden. Wenn sich also beispielsweise eine Datei -eBEGIN{do something evil}im aktuellen Verzeichnis befindet,

perl -ne 'inline perl script here;' *

(mit oder ohne -n) wird etwas Böses tun.

Wie bei anderen Dienstprogrammen besteht die Lösung darin, die Option zum Beenden der Optionen ( --) zu verwenden:

perl -ne 'inline perl script here;' -- *

Aber selbst dann ist es immer noch gefährlich und das liegt an dem <>Operator, der von -n/ verwendet wird -p.

Das Problem wird in der perldoc perlopDokumentation erläutert .

Dieser spezielle Operator wird verwendet, um eine Zeile (ein Datensatz, wobei die Datensätze standardmäßig Zeilen sind) der Eingabe zu lesen, wobei diese Eingabe von jedem der nacheinander übergebenen Argumente stammt @ARGV.

Im:

perl -pe '' a b

-pimpliziert eine while (<>)Schleife um den Code (hier leer).

<>wird zuerst geöffnet a, liest die Datensätze zeilenweise, bis die Datei erschöpft ist, und öffnet dann b...

Das Problem ist, dass zum Öffnen der Datei die erste unsichere Form verwendet wird open:

open ARGV, "the file as provided"

Mit diesem Formular, wenn das Argument ist

  • "> afile", es öffnet sich afileim Schreibmodus,
  • "cmd|", es läuft cmdund liest seine Ausgabe.
  • "|cmd", du hast einen Stream zum Schreiben für den Eingang von geöffnet cmd.

Also zum Beispiel:

perl -pe '' 'uname|'

Gibt nicht den Inhalt der aufgerufenen Datei aus uname|(übrigens einen vollkommen gültigen Dateinamen), sondern die Ausgabe des unameBefehls.

Wenn du rennst:

perl -ne 'something' -- *

Und jemand hat rm -rf "$HOME"|im aktuellen Verzeichnis eine Datei mit dem Namen (wieder ein vollkommen gültiger Dateiname) erstellt (zum Beispiel, weil dieses Verzeichnis einmal von anderen geschrieben werden konnte oder weil Sie ein zweifelhaftes Archiv extrahiert oder einen zweifelhaften Befehl ausgeführt haben oder Eine andere Sicherheitslücke in einer anderen Software wurde ausgenutzt. Bereiche, in denen es wichtig ist, sich dieses Problems bewusst zu sein, sind Tools, die Dateien automatisch in öffentlichen Bereichen verarbeiten /tmp(oder Tools, die möglicherweise von solchen Tools aufgerufen werden).

Dateien genannt > foo, foo|, |foosind ein Problem. In geringerem Umfang < foound foomit führenden oder nachfolgenden ASCII-Abständen (einschließlich Leerzeichen, Tabulatoren, Zeilenumbrüchen, Zeilenumbrüchen usw.) bedeutet dies jedoch, dass diese Dateien nicht verarbeitet werden oder dass die falschen verarbeitet werden.

Beachten Sie auch, dass einige Zeichen in einigen ǖMehrbyte- Zeichensätzen (wie in BIG5-HKSCS) in Byte 0x7c enden, der Kodierung von |.

$ printf ǖ | iconv -t BIG5-HKSCS | od -tx1 -tc
0000000  88  7c
        210   |
0000002

Also in Gebietsschemas mit diesem Zeichensatz,

 perl -pe '' ./nǖ

Versuchen würde , den laufen ./n\x88Befehl als perlwürde nicht , dass die Dateinamen des Gebietsschema des Benutzers zu interpretieren versuchen!

Wie zu beheben / umgehen

AFAIK, Sie können nichts tun, um dieses unsichere Standardverhalten perlein für alle Mal systemweit zu ändern .

Erstens tritt das Problem nur bei Zeichen am Anfang und Ende des Dateinamens auf. Also, während perl -ne '' *oder perl -ne '' *.txtsind ein Problem,

perl -ne 'some code' ./*.txt

nicht , weil alle jetzt die Argumente mit Start ./und Ende in .txt(also nicht -, <, >, |, Raum ...). Allgemeiner gesagt , ist es eine gute Idee, Präfix Klackse mit ./. Das vermeidet auch Probleme mit Dateien, die aufgerufen werden -oder -mit vielen anderen Dienstprogrammen beginnen (und hier bedeutet dies, dass Sie den End-of-options ( --) -Marker nicht mehr benötigen ).

Das -TEinschalten des taintModus hilft in gewissem Maße. Der Befehl wird abgebrochen, wenn eine solche schädliche Datei gefunden wird (nur für die Fälle >und |, jedoch nicht für <Leerzeichen).

Dies ist nützlich, wenn Sie solche Befehle interaktiv verwenden, da Sie dadurch darauf hingewiesen werden, dass etwas Ungewöhnliches vor sich geht. Dies ist jedoch möglicherweise nicht wünschenswert, wenn eine automatische Verarbeitung durchgeführt wird, da dies bedeutet, dass eine andere Person diese Verarbeitung nur durch das Erstellen einer Datei zum Scheitern bringen kann.

Wenn Sie jede Datei unabhängig von ihrem Namen verarbeiten möchten, können Sie das ARGV::readonly perlModul auf CPAN verwenden (leider normalerweise nicht standardmäßig installiert). Das ist ein sehr kurzes Modul, das Folgendes bewirkt:

sub import{
   # Tom Christiansen in Message-ID: <24692.1217339882@chthon>
   # reccomends essentially the following:
   for (@ARGV){
       s/^(\s+)/.\/$1/;   # leading whitespace preserved
       s/^/< /;       # force open for input
       $_.=qq/\0/;    # trailing whitespace preserved & pipes forbidden
   };
};

Im Grunde desinfiziert es @ARGV, indem es sich " foo|"zum Beispiel in "< ./ foo|\0".

Sie können dasselbe in einer BEGINAnweisung in Ihrem perl -n/-pBefehl tun :

perl -pe 'BEGIN{$_.="\0" for @ARGV} your code here' ./*

Hier vereinfachen wir es unter der Annahme, ./dass verwendet wird.

Ein Nebeneffekt dieses (und ARGV::readonly) ist jedoch, dass $ARGVin your code heredieses nachgestellte NUL-Zeichen zeigt.

Update 03.06.2015

perlv5.21.5 und höher haben einen neuen <<>>Operator, der sich so verhält, <>außer dass er diese spezielle Verarbeitung nicht ausführt . Argumente werden nur als Dateinamen betrachtet. Mit diesen Versionen können Sie nun schreiben:

perl -e 'while(<<>>){ ...;}' -- *

(Vergiss das nicht --oder benutze es ./*), ohne befürchten zu müssen, dass es Dateien überschreibt oder unerwartete Befehle ausführt.

-n/ -pNoch die gefährliche verwenden <>Form though. Beachten Sie, dass Symlinks weiterhin befolgt werden, was nicht unbedingt bedeutet, dass die Verwendung in nicht vertrauenswürdigen Verzeichnissen sicher ist.

Stéphane Chazelas
quelle
2
Ich wette, Sie haben den ganzen Tag daran gearbeitet. gut gemacht.
mikeserv
2
schön Update auf Perl, aber es ist seltsam , dass die Perl Devs nicht hinzufügen , -P und -N Optionen Gebrauch zu machen (kann nicht das ändern vorhandene -p und -n , weil einige Skripte auf das unsichere Verhalten verlassen kann)
cas
9

Zusätzlich zur Antwort von @ Stéphane Chazelas müssen wir uns um dieses Problem keine Sorgen machen, wenn wir die -iBefehlszeilenoption verwenden:

$ perl -pe '' 'uname|'
Linux

$ perl -i -pe '' 'uname|'
Can't open uname|: No such file or directory.

Denn wenn Sie die -iOption verwenden, haben Sie perlmit stat den Dateistatus überprüft, bevor Sie ihn verarbeiten:

$ strace -fe trace=stat perl -pe '' 'uname|'
stat("/home/cuonglm/perl5/lib/perl5/5.20.1/x86_64-linux", 0x7fffd44dff90) = -1 ENOENT (No such file or directory)
stat("/home/cuonglm/perl5/lib/perl5/5.20.1", 0x7fffd44dff90) = -1 ENOENT (No such file or directory)
stat("/home/cuonglm/perl5/lib/perl5/x86_64-linux", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
Process 6106 attached
Linux
Process 6105 suspended
Process 6105 resumed
Process 6106 detached
--- SIGCHLD (Child exited) @ 0 (0) ---

$ strace -fe trace=stat perl -i -pe '' 'uname|'
stat("/home/cuonglm/perl5/lib/perl5/5.20.1/x86_64-linux", 0x7fffdbaf2e50) = -1 ENOENT (No such file or directory)
stat("/home/cuonglm/perl5/lib/perl5/5.20.1", 0x7fffdbaf2e50) = -1 ENOENT (No such file or directory)
stat("/home/cuonglm/perl5/lib/perl5/x86_64-linux", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("uname|", 0x785f40)                = -1 ENOENT (No such file or directory)
Can't open uname|: No such file or directory.
cuonglm
quelle
1
Gibt es nicht eine mögliche Racebedingung zwischen dem statCheck und der effektiven Perl-Verarbeitung, die unmittelbar danach stattfindet?
Totor
@Totor: Ich denke nein.
Donnerstag,
Es geht nicht darum stat. Es ist nur so, dass Dateien an Ort und Stelle -ibearbeitet werden. Daher ist es nicht sinnvoll, andere Argumente als die tatsächlichen Dateipfade zu akzeptieren, sodass keine -ispezielle Verarbeitung erfolgt.
Stéphane Chazelas