Moin!
Im von Dir entfernten Abschnitt steht aber:
if ( isset($_GET['file']) && $_GET['file'] ) { $_GET['file'] = './' . $_GET['file']; $forbidden=array('../', '/..', '/.ht'); $_GET['file'] = str_replace($forbidden, '', $_GET['file']);
Danach beginnt der Inhalt von $_GET['file'] auf jeden Fall mit './' (dem aktuellen Verzeichnis) und ist um alle Zeichenfolgen bereinigt, mit denen auf ein tieferes Verzeichnis und die im Apache gesperrten '.ht-Dateien' zugegriffen werden kann.
Wie soll denn da eine Traversal-Lücke entstehen?
Wie ja schon experimentell bestätigt wurde, ist der Trick hier die Eigenschaft von str_replace, nicht rekursiv zu ersetzen. Sofern irgendwo die Zeichenkette "../" gefunden wird, wird sie durch einen Leerstring ersetzt. Danach geht es bei den rechts daneben befindlichen Zeichen weiter. Wenn durch die Ersetzung von links und rechts Zeichen, die vorher nicht findbar waren, zu einer "bösen" Zeichenkette zusammenwachsen, werden sie nicht noch einmal ersetzt.
Sprich: Im String nur "../" durch "" zu ersetzen kann umgangen werden durch "....//" - das mittlere "../" wird durch Leerstring ersetzt, der Rest davor (nur zwei Punkte "..") und danach (nur ein "/") passt nicht auf das Suchmuster. Das Resultat ist "../" und wird nicht weiter angetastet von der Ersetzung.
Wenn man, wie hier, mehrere Suchstrings hat, wird str_replace() diese nacheinander suchen und beim Finden ersetzen. Das bedeutet, ich muss meinen Zielstring rückwärts so um die entfernten Ersetzungsmuster anreichern, dass diese, von hinten nach vorn durch das Array gehend, mit Fake-Fundstellen aufgesplittet werden.
Grüße Sven