Wie im Shellcode Guide 2321e8d6b3c3 beschrieben gilt die Regel "Traue keinem Input"
Gerade bei der Übergabe von Dateipfaden kann dies besonders kritisch sein.
Wenn du generell keine Dateipfade brauchst, filterst du bei der Variablenübergabe bereits die Zeichen / \ % heraus.
tr -d "%/\\"
Wenn du jedoch darauf angewiesen bist, auch Pfade übergeben zu müssen, und daher Slashes nicht wegfiltern kannst, existieren die kombinierbaren kritischen Zeichen . und /
Mit diesen Zeichen wird es möglich, relative Pfade zu bilden in der Form ../../illegitimes_ziel
Nun genügt es aber nicht, wenn du einfach "../" wegfilterst und durch "" ersetzt!
Betrachte diese Testfälle:
....//....//irgendwas/geheimes.txt
Wenn du nur die Sequenz "../" durch eine leere Zeichenfolge ersetzt, bleibt das Problem also bestehen.
Eine einfache Möglichkeit besteht darin, einfach aufeinanderfolgende Punkte zu filtern. Ohne den Slash!
Vermeide: tr -d "%" | sed "s|\.\./||g" (gefährlich!)
Nutze: tr -d "%" | sed "s|\.\.||g"
Für die sedline ist es wichtig, den dot mit backslash \ explizit anzugeben, sonst gilt er als "universelles" Zeichen.
Beachte aber, dass du damit auch potentiell gültige und unkritische Datei- und Ordnernamen nicht mehr korrekt auswerten kannst:
erbsen..linsen..bohnen.txt
Wäre an sich harmlos, wird damit aber zu einem ungültigen Namen, weil du auch dort die aufeinanderfolgenden Punkte entfernst.
Für derartige Szenarien stünde diese universelle aber hässlich notierte Variante zur Verfügung:
tr -d "%" | sed "s|.\.\/||g;s|\.\./||g"
Diese führt einfach einen erneuten replace nach dem replace durch. Hier wird der Slash also auch wieder explizit genannt. Alternativ geht natürlich auch, die etwas mehr Ressourcen verbrauchende Variante:
tr -d "%" | sed "s|.\.\/||g" | sed "s|\.\./||g"
Beachte aber, dass diese Variante dann nicht für backslash gilt. Die zuerst genannte Variante des Herausfilterns von aufeinanderfolgenden Punkten gilt hingegen gleichermaßen für Slash und Backslash.
Willst du auch hier die mehrfache Punkte in Datei- oder Ordnernamen korrekt erhalten, bleibt das noch hässlichere Konstrukt:
tr -d "%" | sed "s|.\.\/||g" | sed "s|\.\./||g" | sed "s|\.\.\\\||g" | sed "s|\.\.\\\||g"
Oder die kombinierte Variante davon:
tr -d "%" | sed "s|.\.\/||g;s|\.\./||g;s|\.\.\\\\||g;s|\.\.\\\\||g"
Encoding und Double Encoding sind hier ein Problem. Siehe diese Beispiele:
%2e%2e%2f steht für ../
%2e%2e/ steht für ../
..%2f steht für ../
%2e%2e%5c steht für ..\
%2e%2e\ steht für ..\
..%5c steht für ..\
%252e%252e%255c steht für ..\
..%255c steht für ..\
Du könntest also die hexadezimale ASCII Codierung des Zeichens Slash oder Punkt verwenden und damit deinen Replace austricksen. Damit das nicht mehr geht, werfen wir das % Zeichen weg, was damit auch kein Bestandteil eines Ordner- oder Dateinamens sein kann. Beachte auch, dass je nach Implementierung der Backslash \ verwendet werden kann, um die Octal oder x\ um die Hexadezimalnotation zu übergeben. Der Backslash ist hier also auch ein potentiell gefährliches Zeichen.
Auch URL-Encoding (Prozent-Encoding) des Slashes oder Backslashes ist ein Problem. Es wird z.B. beim URL Encoding verwendet und daher u.a. auch in der Webentwicklung relevant:
..%c0%af steht für ../
..%c1%9c steht für ..\