Zakład podatności na błąd PHP regex


Dziś mój kolega argumentował ze mną, że zna sposób, aby zapewnić specjalnie sformatowany ciąg, który może przekazać następującą kontrolę regex, a jednocześnie podaj nazwę pliku z rozszerzeniem .php lub
.jsp </Code> lub[code].asp
: [/code]
if (preg_match('/\.(jpeg|jpg|gif|png|bmp|jpe)$/i', $var) && preg_match('/\.(asp|jsp|php)$/i', $var) == false) 
{
echo "No way you have extension .php or .jsp or .asp after this check.";
}

Bez względu na to, jak bardzo się starałem i przeszukiwałem sieć, nie mogłem znaleźć ani jednej usterki, która by to umożliwiła. Może czegoś mi brakuje? Biorąc pod uwagę, że luka w zabezpieczeniach bajtu zerowego została naprawiona, co jeszcze może być problemem?
Uwaga: w żaden sposób nie sugeruję, że ten kod jest w pełni przetestowaną metodą sprawdzania rozszerzenia pliku, może występować defekt w funkcji
preg_match ()
lub zawartość pliku może być inny format, po prostu zadaję pytanie z kropki w odniesieniu do samej składni wyrażenia regularnego.

Kod edycji

:
if (isset($_FILES["image"]) && $_FILES["image"]["name"] && preg_match('/\.(jpeg|jpg|gif|png|bmp|jpe)$/i', $_FILES["image"]["name"]) && preg_match('/\.(asp|jsp|php)$/i', $_FILES["image"]["name"]) == false) {
$time = time();
$imgname = $time . "_" . $_FILES["image"]["name"];
$dest = "../uploads/images/"; if (file_exists($dest) == false) {
mkdir($dest);
} copy($_FILES['image']['tmp_name'], $dest . $imgname); }else{
echo "Invalid image file";
}

Wersja PHP: 5.3.29

EDYCJA: epilog

Okazało się, że „luka” występuje tylko w systemie Windows. Jednak zrobił dokładnie to, co powiedział mi mój kolega - przeszedł test regex i zapisał plik z rozszerzeniem wykonywalnym. Poniższe elementy zostały przetestowane pod kątem
WampServer 2.2
z
PHP 5.3.13
:
Przekazanie następującego wiersza do sprawdzenia wyrażenia regularnego powyżej
test.php: .jpg
(zwróć uwagę na dwukropek „:” na końcu żądanego rozszerzenia) spowoduje jego sprawdzenie, a
copy ()
function. wydaje się pomijać wszystko po znaku dwukropka, w tym sam znak.
Ponownie dotyczy to tylko okien. W systemie Linux plik zostanie zapisany pod dokładnie taką samą nazwą, jak ta przekazana funkcji. [/code]
Zaproszony:
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Nie ma jednego kroku ani pełnego prostego sposobu użycia kodu, ale oto kilka myśli.
W tym przykładzie przekazujesz go do
copy ()
, ale wspomniałeś, że od jakiegoś czasu używasz tej metody do sprawdzania pliku ext, więc zakładam, że miałeś inne przypadki, w których ta procedura mogła być używana wraz z innymi funkcjami w różnych wersjach PHP.
Potraktuj to jako procedurę testową (używając include, require):
$name = "test.php#.txt";
if (preg_match('/\.(xml|csv|txt)$/i', $name) && preg_match('/\.(asp|jsp|php)$/i', $name) == false) {
echo "in!!!!";
include $name;
} else {
echo "Invalid data file";
}

To zakończy się „in !!!!” a uruchamiając „test.php”, nawet jeśli jest załadowany, włączy go z folderu tmp - oczywiście w tym przypadku należysz już do atakującego, ale spójrzmy również na te opcje.
Nie jest to typowy skrypt procedury rozruchu, ale jest to koncepcja, której można użyć, łącząc kilka metod:
Przejdźmy dalej - jeśli wykonasz:
//$_FILES['image']['name'] === "test.php#.jpg";
$name = $_FILES['image']['name'];
if (preg_match('/\.(jpeg|jpg|gif|png|bmp|jpe)$/i', $name) && preg_match('/\.(asp|jsp|php)$/i', $name) == false) {
echo "in!!!!";
copy($_FILES['image']['tmp_name'], "../uploads/".$name);
} else {
echo "Invalid image file";
}

I znowu wszystko jest w porządku. Plik jest kopiowany do folderu „

uploads

"- Nie możesz uzyskać bezpośredniego dostępu do niego (ponieważ serwer WWW będzie szablon prawej strony #), ale wprowadziłeś plik, a atakujący może znaleźć sposób lub innego słabego miejsca, aby zadzwonić później.
Przykład takiego skryptu wykonawczego jest powszechny w witrynach udostępnianych i hostujących, w których pliki są obsługiwane przez skrypt PHP, który (w niektórych niezabezpieczonych przypadkach) może załadować plik, dołączając do niego niewłaściwy typ funkcji, na przykład
require
,
include
,
file_get_contents
, które są podatne na ataki i mogą wykonać plik. [/code]
Null Bayt.

ataki zerobajtowe były poważną słabością w php & < 5.3, ale zostały ponownie wprowadzone przez regresję w wersji 5.4+ w niektórych funkcjach, w tym we wszystkich funkcjach związanych z plikami i wielu innych w rozszerzeniach. Zostało to naprawione kilka razy, ale nadal istnieje i wiele starszych wersji jest nadal w użyciu. Jeśli pracujesz ze starszą wersją php, na pewno jesteś narażony na:
//$_FILES['image']['name'] === "test.php\0.jpg";
$name = $_FILES['image']['name'];
if (preg_match('/\.(jpeg|jpg|gif|png|bmp|jpe)$/i', $name) && preg_match('/\.(asp|jsp|php)$/i', $name) == false) {
echo "in!!!!";
copy($_FILES['image']['tmp_name'], "../uploads/".$name);
} else {
echo "Invalid image file";
}

Zostanie wydrukowany „w !!!!” i skopiuje twój plik o nazwie „test.php”.
Sposób php naprawił to, sprawdzając długość ciągu przed i po przekazaniu go do głębszej procedury C, która tworzy rzeczywistą tablicę znaków, a zatem jeśli ciąg jest obcięty do bajtu zerowego (co wskazuje koniec ciągu w C ) długość nie będzie pasować.

więcej szczegółów
http://www.madirish.net/401
Co dziwne, nawet w poprawionych i nowoczesnych wydaniach PHP, nadal istnieje:
$input = "foo.php\0.gif";
include ($input);// Will load foo.php :)


Mój wniosek:

Twoja metoda sprawdzania rozszerzeń plików mogłaby zostać znacznie ulepszona - Twój kod pozwala plikowi PHP o nazwie
test.php # .jpg
przejść przez niego, podczas gdy nie powinien. Skuteczne ataki są zasadniczo przeprowadzane poprzez połączenie wielu luk, nawet tych mniejszych - każdy nieoczekiwany wynik i zachowanie należy traktować jako jeden.

Uwaga
: wciąż jest wiele problemów z nazwami plików i obrazami, ponieważ są one później umieszczane wielokrotnie na stronach i jeśli nie są prawidłowo filtrowane i bezpiecznie dołączane, narażasz się na wiele innych rzeczy związanych z XSS, ale to jest poza tematem.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Wypróbuj ten kod.
$allowedExtension = array('jpeg','png','bmp');// make list of all allowed extensionif(isset($_FILES["image"]["name"])){
$filenameArray = explode('.',$_FILES["image"]["name"]);
$extension = end($filenameArray);
if(in_array($extension,$allowedExtension)){
echo "allowed extension";
}else{
echo "not allowed extension";
}
}
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

preg_match()
http://php.net/manual/en/function.preg-match.php
zwraca 1, jeśli wzorzec pasuje do podanego tematu, 0, jeśli nie pasuje, lub FALSE, jeśli wystąpił błąd.
$var = "test.php";
if (preg_match('/\.(jpeg|jpg|gif|png|bmp|jpe)$/i', $var) === 1
&& preg_match('/\.(asp|jsp|php)$/i', $var) !== 1)
{
echo "No way you have extension .php or .jsp or .asp after this check.";
} else{
echo "Invalid file";
}

Więc kiedy zamierzasz przetestować swój kod, użyj
=== 1
.

Idealnie powinieneś użyć.
>
function isImageFile($file) {
$info = pathinfo($file);
return in_array(strtolower($info['extension']),
array("jpg", "jpeg", "gif", "png", "bmp"));
}
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Pamiętam to w wersji pewnej

PHP < 5.3.X

PHP pozwala na to, aby linie zawierały
0x00
, ten znak jest uważany za koniec linii

Na przykład, jeśli twój ciąg zawiera:

myfile.exe\0.jpg

wtedy
preg_match ()
będzie pasować

jpg

ale inni


Funkcje PHP zatrzymają się na

myfile.exe,

np. funkcje
include ()
lub
copy ()

Aby odpowiedzieć na pytania, Zaloguj się lub Zarejestruj się