Как стать автором
Обновить

Комментарии 101

это бред какой-то, сначала сохранять файл, а потом проводить его валидацию, и удалять в случаи неуспеха
что это за движок такой?
Бред здесь не в сохранении файла — это необходимость из-за safe mode и open base dir, а в других деталях:
1. Сохранение файла в папку temp, которая почему-то доступна извне.
2. Проверка файла на mime-type — нигде не видела, чтобы так делали.
3. Сохранение под начальным названием файла, отсутствие проверки на расширение и возможность запуска под этим именем.

Но автору все равно респект, все дружно проверяем свои движки!
Помню приходилось делать register shutdown при загрузке mp3.
Бред здесь не в сохранении файла — это необходимость из-за safe mode и open base dir

можно в деталях, пожалуйста?
При загрузке файла PHP помещает его во временную папку со случайным названием файла. Если в настройках PHP задан параметр open_base_dir и временная директория не входит в предел видимости open_base_dir, то манипуляции с загруженным файлом будут запрещены.
Для работы с ним существует функция move_uploaded_file, которая сохраняет только загруженные файлы в другое место.

Название временного файла будет храниться в ключе tmp_name массива $_FILES['название_параметра_для_файла'].
При попытке, например, обратиться к нему getimagesite($_FILES['file']['tmp_name']) получим ошибку
Warning: getimagesize(): open_basedir restriction in effect. File(/tmp/phpncFYlY) is not within the allowed path(s):


Соответственно, файл нам сохранить все же придется.
Только сохранять его нужно вне DocumentRoot или как в вашем вебсервере это называется. Ну или хотя бы в директории, доступ к которой через веб закрыт. В худшем случае — в которой не выполняются скрипты.
Вообще-то манипуляции с загруженным файлом напрямую во временной директории — это уже звоночек, что вы делаете что-то не так. Вне зависимости от значения open_base_dir, с загруженными файлами лучше работать через move_uploaded_file, эта функция для этого и создавалась.
Я отвечала на комментарий beat о том, что сохранить как раз таки нужно. Только это пришло в голову, когда он написал:
это бред какой-то, сначала сохранять файл, а потом проводить его валидацию, и удалять в случаи неуспеха

Показалось, что он напрямую обращается к загруженному файлу (а может оно так и есть, знает только автор).
Ну я понял, что вы про загрузку как раз) Просто вначале вы странный кейс описали, не рекомендую никому так делать)
НЛО прилетело и опубликовало эту надпись здесь
Потому что для работы с загружеными файлами специально есть функция move_uploaded_file, которая делает это безопасно и является общепринятым способом. Не нужно самому копаться во временной директории, когда в языке есть функционал для нормальной работы в этом случае.
если нужно проанализировать содержимое файла ДО того, как он будет помещён в окончательную директорию, этот анализ лучше делать во временной

ну или придётся перемещать его в «другую временную», а потом опять, уже на окончательное место (ну или удалять)
Не вижу проблемы в том, что у приложения есть своя временная папка или в том чтобы удалить файл, если он не прошел проверку.
Не вижу проблемы в том, что у приложения есть своя временная папка или в том чтобы удалить файл, если он не прошел проверку.

Вы видно и статью-то по диагонали прочитали. Там именно происходит так: перемещают в свою папку, а потом проверяют, и если не прошёл проверку — удаляют. Точно так, как вы сказали.

Только вот в функции, которая делает проверку, нашли баг, и скрипт на этом этапе вылетает, не успев удалить файл. Упс…

А если бы мы проверяли в /tmp, а потом если надо перемещали — проблемы бы не возникло.

Ваш подход аналогичен «разрешено всё, что не запрещено». Мой — «запрещено всё, что не разрешено». Второе правильнее.
Is_uploaded_file() вам в помощь.
НЛО прилетело и опубликовало эту надпись здесь
На самом деле настоящая причина как раз таки в безопасности. А конкретно в инкапсуляции.
Есть система, в ней есть системные папки (например, временная), которые доступны всем. Если дать к ней доступ PHP-скриптам — это огромная дыра в безопасности (можно подменить любые чужие временные файлы).
Дальше, если в PHP настраиваем upload_tmp_dir, то это «межпроектовая» дыра (то же самое, только не на уровне всей системы, а на уровне PHP-процессов.
по-моему временна директория как раз и предназначена для хранения всякого мусора, временных файлов, или что там можно найти интересного? даже не знаю, часть пользовательских сессий, пока за ними не пришел garbage collection?
Вы представляете, что будет при возможности изменения файлов сессий? Мусор, как вы говорите, на данный момент считается самой ценной целью взлома любой системы.
И еще раз, временная папка (если речь о ней) — она на всю систему, не только на PHP.
Так для чего вообще нужна системная временная директория, по-вашему?
Нужна для хранения временных файлов, но это не значит, что любому процессу можно ее отдавать.
Если вы говорите об этом примере, то это всего лишь пример.
И с open_base_dir без системной временной папки — это не прокатит.
Warning: tempnam(): open_basedir restriction in effect. File(/tmp) is not within the allowed path(s):


Если вам так нужна временная папка — ничего не мешает сделать ее самому на уровне конкретного web-ресурса.
Но зачем? У общесистемной есть отдельные плюсы, например:

* Устаревшие сессии PHP будет подчищать сам
* Файлы, не перемещённые оттуда с помощью move_uploaded_file, он будет подчищать сам

Кстати, последнее полностью нивелирует проблему, изложенную в статье (скрипт упал до того, как удалил файл).

То есть, если бы мы с самого начала не стали тянуть каку в рот (забирать оттуда непроверенный файл), даже после падения проверки он сам удалился бы. Так что надёжнее и безопаснее — проверить в /tmp и если не нужен — не брать (т.е. «брать только то, что нужно»), чем забрать к себе, проверять и если не нужен — удалить («брать всё и потом выбрасывать»).

Не понимаю причем тут сессии? Я не предлагаю для сессий создавать свою временную папку для каждого проекта.
Я предлагаю не давать к ней доступ из скриптов!
session_start и $_SESSION при этом будет работать.
Так скриптам тоже бывают нужны временные файлы. Например, только что загруженные и про которые ещё неизвестно, нужны ли они и хотим ли мы их хранить.
Вот скриптам и создайте временную ограниченную open_base_dir директорию.
А зачем, зачем создавать ещё одну, если такая уже есть? Пусть /tmp будет в open_basedir и всё. Зачем мне отдельная временная директория?
Расскажите, пожалуйста, в чем по вашему смысл директивы open_basedir, если она захватывает системные папки?
Вообще, по большому счёту, никакого смысла в open_basedir нет. Это костыль, как и safe mode.

Вон, в соседней ветке обсуждают Django — там нет никакого safe mode, нет никакого open_basedir, и никто не жалуется. Тем не менее, проблем безопасности типа RCE у них прилично так меньше, поскольку исполняемый скрипт недоступен через веб и указан в конфиге сервера приложений, а не как с PHP, любой файл с подходящей маской имени и находящийся внутри дерева документов является исполняемым скриптом.

Однако, а что такого священного в системных ресурсах? Куда важнее защитить данные, а не системные директории.
Вообще, по большому счёту, никакого смысла в open_basedir нет. Это костыль, как и safe mode.

Вот это и стоило обсуждать. Потому что весь диалог и шел в контексте open_basedir и ваше аргументы неверны в таком случае.

А про open_basedir, как и про любую другую особенность языка смысла спорить не вижу. Она есть потому, что разработчиков на PHP в тысячи раз больше и их уровень сильно отличается от разработчиков на других языках.
Но не соглашусь, что это какой-то костыль — это обычная фишка, коих хватает в каждом языке.

Тем не менее, проблем безопасности типа RCE у них прилично так меньше, поскольку исполняемый скрипт недоступен через веб и указан в конфиге сервера приложений, а не как с PHP, любой файл с подходящей маской имени и находящийся внутри дерева документов является исполняемым скриптом.

Спорить о достоинствах этого тоже считаю глупым холиваром.

Однако, а что такого священного в системных ресурсах? Куда важнее защитить данные, а не системные директории.

Священного ничего нет, как и в данных, мы же не о религии говорим вроде? А важного много чего может быть. Не хотите — не защищайте, лично я стараюсь ограничить исполняемые скрипты только необходимыми для их работы возможностями, а не гадать — есть ли что-то важное на жестком диске сервера.
по-моему взломщик уже получил доступ к файлам, от он и так уже получит и всю базу, и файлы, и скрипты
можно подменить любые чужие временные файлы

не знаю как у вас, а я создаю там временные файлы с правами 600
давайте, подмените
Я вроде не говорила, что нельзя защититься от этого? Я отвечала на вопрос — почему, по-умолчанию, рекомендуется этого не делать.
Предполагается, что люди, способные осознать всю глубину проблемы — не нуждаются в советах для широкой аудитории.

По поводу ваших прав на 600, а создаете их вы средствами чего? 600 — это права доступа владельца на чтение и запись.
Дайте угадаю, кто будет владельцем при, например, запуске apache+php в обоих случаях?
Я знал, что последует этот вопрос.

Если apache+mod_php, то mpm_itk и скрипты каждого виртуалхоста выполняются от отдельной учётной записи. Так что не угадаете.

Кроме того, если бы на шареде все PHP исполнялись от одного пользователя, я мог бы всё равно залезть в чужие сессии.
Т.е. возвращаемся к первоначальному ответу — если мы осознаем проблему, мы способны от нее защититься?
Я понял, что вы поняли. Я хотел подчеркнуть, что если «дай угадаю» и все PHP-скрипты всех пользователей сервера выполняются от одного и того же имени, просто перемещением сессий или чего угодно вовне /tmp вы проблему не решаете никак. Потому, что всё равно это всё будет доступно интерпретатору, и соответственно, любому пользователю.

То есть, без разницы, работать ли в /tmp или где-то ещё: если сервер настроен безопасно, и в /tmp будет безопасно; если он настроен криво, куда не положи — дыра будет. А поскольку /tmp специально предназначен именно для работы с временными файлами, для этого лучше его и использовать, не изобретая велосипеды и костыли.

Кстати, сам PHP по умолчанию сессии создаёт в /tmp и с правами 600, специально проверил.
Подождите, мы говорим о загруженных файлах и директиве open_base_dir так?
Никаких прав на доступ к /tmp с ней у нас нет. Если мы даем такой доступ, то нам приходится уже защищаться от другой проблемы — доступа к системной папке.
Далее, мы говорим о PHP, а не об остальных возможностях защитить наш сервер. PHP не знает, кто его запустит. Может быть разные пользователи, может быть один. Может быть безопасный http-сервер, а может быть и не очень.
PHP инкапсулированно в себе предлагает не создавать дыру в безопасности. Что тут нелогичного?
То, что подход, который вы предлагаете для «не создавания дыры в безопасности» не работает на небезопасном сервере, а на безопасном он просто не нужен.

/tmp — системная папка специально для временных файлов, в чём проблема с доступом к ней, если она специально задумана так, чтобы доступ был?
То, что подход, который вы предлагаете для «не создавания дыры в безопасности» не работает на небезопасном сервере, а на безопасном он просто не нужен.

Во-первых, я конкретный подход не предлагала, а описывала рекомендации и возможности PHP.
Во-вторых, почему это не работает? Вы так и не объяснили вашу позицию. Вот задан у нас open_base_dir и файлы грузятся через move_uploaded_file. При выполнении скриптов от одного пользователя — какие проблемы безопасности? Может быть, я что-то упускаю, конечно?

/tmp — системная папка специально для временных файлов, в чём проблема с доступом к ней, если она специально задумана так, чтобы доступ был?

Потому что есть разные уровни доступа. Например PHP был задуман чтобы автоматизировать часть процессов, но это не значит, что пользователю web-ресурса можно запускать произвольные скрипты (например, запустить на хабре php-скрипт-фильтр, который автоматом уберет из ленты комментарии с рейтингом -500). Точно так же есть уровни доступа и на сервере: для системных процессов — одни, для разработчиков — другие, для скриптов, выполнящихся http-серверов — третие, для скриптов из crontab — четвертые.
Откуда рекомендации? На php.net ничего такого нет. Я как раз пытаюсь показать, что подобные рекомендации лишены смысла, а то и вредны.

Напомню, в статье описан случай: перемещаем к себе, проверяем, если не нужен — удаляем. Но проверка падает, файл не удаляется. Если бы мы проверили до перемещения (да, доступ к /tmp должен быть разрешён, и я не понимаю, зачем его запрещать), даже при падении проверки файл был бы удалён. То есть, ваша рекомендация в данном случае принесла бы больше вреда, чем пользы.

Про уровни доступа не понял, вообще это к чему здесь? Есть некий системный ресурс, который задуман как «можно всем» (вне зависимости от уровня доступа). К чему возникла речь про выполнение произвольных скриптов и так далее — не улавливаю, это как-то за рамками темы.
Откуда рекомендации? На php.net ничего такого нет. Я как раз пытаюсь показать, что подобные рекомендации лишены смысла, а то и вредны.

www.php.net/manual/ru/function.move-uploaded-file.php
Эта функция проверяет, является ли файл filename загруженным на сервер (переданным по протоколу HTTP POST). Если файл действительно загружен на сервер, он будет перемещён в место, указанное в аргументе destination.

Такая проверка особенно важна в том случае, если существует шанс того, что какие-либо действия, производимые над загруженным файлом, могут открыть его содержимое пользователю или даже другим пользователям системы.


Это по поводу вообще проверки — наш ли это файл загружен. Вы скажете, что есть is_uploaded_file, но это возвращает нас к обсуждению смысла open_base_dir, которое вы избегаете.

Если бы мы проверили до перемещения (да, доступ к /tmp должен быть разрешён, и я не понимаю, зачем его запрещать), даже при падении проверки файл был бы удалён.


Возможно, вы удивитесь, но загружаемый файл удаляется
www.php.net/manual/ru/features.file-upload.post-method.php
По окончанию работы скрипта, в случае, если принятый файл не был переименован или перемещен, он будет автоматически удален из временной папки.


Так что, я не понимаю о каких вредных рекомендациях вы говорите, с учетом того что open_base_dir ни в коем случае не дал бы нам доступ из web-морды к upload_tmp_dir, в отличии от вашего примера.

«можно всем»

Ну это ваше личное видение.
Это по поводу вообще проверки — наш ли это файл загружен. Вы скажете, что есть is_uploaded_file

Ммм, то есть, по-вашему PHP настолько говно, что в моём массиве $_FILES может быть не мой загруженный файл?

Зачем ЭТО проверять? Всё, что в $_FILES — моё, файлы, переданные моему скрипту через POST. В is_uploaded_file тоже, таким образом, мало смысла.

Возможно, вы удивитесь, но загружаемый файл удаляется
www.php.net/manual/ru/features.file-upload.post-method.php

По окончанию работы скрипта, в случае, если принятый файл не был переименован или перемещен, он будет автоматически удален из временной папки.

Перечитайте, пожалуста, то, на что отвечали. ОБ ЭТОМ Я И ГОВОРЮ. Повторю: если я переместил файл (как вы предлагаете), а потом упала проверка, до удаления дело не дойдёт — я переместил его, и в лучшем случае он там останется лежать бесполезным мусором, а в худшем — будет ситуация как в статье. Поэтому нельзя пользоваться этим советом, совет — вредный.

Если бы я проверял файл там, куда его мне положили во временной директории, и проверка упала бы, то файл был бы автоматически удалён PHP после падения скрипта — т.к. я его не перемещал. Никакой проблемы с мусором нет. Поэтому верный совет — проверить, нужен ли нам вообще этот файл, и забирать его к себе move_uploded_file только если был нужен. И игнорировать, если не был нужен — удалится само.

Ну это ваше личное видение.

режим 0777, видно, один я вижу
Перечитайте, пожалуста, то, на что отвечали. ОБ ЭТОМ Я И ГОВОРЮ. Повторю: если я переместил файл (как вы предлагаете), а потом упала проверка, до удаления дело не дойдёт — я переместил его, и в лучшем случае он там останется лежать бесполезным мусором, а в худшем — будет ситуация как в статье. Поэтому нельзя пользоваться этим советом, совет — вредный.

Вы просто подгоняете ситуацию конкретно под этот пост. А мы говорим о совершенном разных вещах.
Я говорю о глобальной защите скриптов через open_basedir и, как следствие, необходимости переместить файл в место, где я смогу с ним работать (проверять и т.д.). Вы считаете, что open_basedir не нужно — это ваше право, но тема совершенно другая.
Я не предлагала переместить файл в папку доступную извне? Вроде, нет.
То, что у вас случайно получится избежать именно этой атаки ни о чем не говорит, да и получится ли?
Если бы я проверял файл там, куда его мне положили во временной директории, и проверка упала бы, то файл был бы автоматически удалён PHP после падения скрипта

Это если будет кому его еще удалять (живой GC), ведь так?
Вы просто подгоняете ситуацию конкретно под этот пост. А мы говорим о совершенном разных вещах.

Здрасте, я вроде бы пишу в комментарияк конкретно к этому посту. Не уходите в оффтопик ;)

Даже если файл перемещается в «другую временную директорию», недоступную через веб, он может там так и остаться (ну, опять же, при падении во время проверки). То есть, будет хотя бы занимать место, а может быть и зацеплен через уязвимость типа file inclusion. Не хотелось бы иметь в системе непонятных файлов, где угодно. Либо полученные от пользователя файлы должны быть проверены и нужны, либо они не должны оставаться в системе вообще. И да, open_basedir не поможет в данном случае никак.

Это если будет кому его еще удалять (живой GC), ведь так?

Да. Надо понимать, что он выживает чаще, чем скрипт. Упавший GC в случае с mod_php примерно равносилен упавшему процессу вебсервера, в котором выполняется интерпретатор. А скрипт обычно и убивается самим интерпретатором при различных условиях, то есть, слишком много памяти запросил, слишком долго выполняется и так далее. GC интерпретатор
Поэтому на встроенный GC рассчитывать надёжнее, чем на то, что скрипт доработает до конца.
(… не дописал...) GC интерпретатор не будет убивать, убивая скрипт, а наоборот, вызовет, чтобы подчистить за ним мусор.
Здрасте, я вроде бы пишу в комментарияк конкретно к этому посту. Не уходите в оффтопик ;)

Да. Надо понимать, что он выживает чаще, чем скрипт. Упавший GC в случае с mod_php примерно равносилен упавшему процессу вебсервера, в котором выполняется интерпретатор.

Вы пишите про вредность совета для всех случаев или конкретно как обойти эту уязвимость?
В этом посте не упадет, а в следующем может и упадет. Мы говорим об уязвимостях. Именно поэтому вы подгоняете ситуацию под этот пост. Конкретно, эту уязвимость ваш способ решит (оставим открытым кучу дыр, но ладно), а вот остальные нет.
Но слава богу у вас GC жив. Тогда можете указать upload_tmp_dir в директорию с open_basedir и будем вам счастье. Помогу, с конфигом для apache
php_admin_value open_basedir /var/www/sites/site.ru
php_admin_value upload_tmp_dir /var/www/sites/site.ru/upload
Если некий совет претендует на общность и хотя бы в одном случае он вредный, то — он вредный в целом, потому, что не определены границы применимости и условия. То есть, кто-нибудь непременно последует ему бездумно как раз в той ситуации, где надо было делать по-другому. Не надо таких советов.

Впрочем, если вы находите open_basedir полезным, то вариант с переназначением upload_tmp_dir действительно лучше, чем поход «скопировать и потом по результатам анализа удалять». То есть, я допустил бы такое решение, но потребовал бы аргументировать, чем именно не устраивает стандартная директория /tmp и разделение прав средствами unix. Мало ли, какие дополнительно ограничения у меня есть на /tmp, типа nodev nosuid noexec и мандатного контроля прав, которым я как администратор доверяю больше, чем php open_basedir.
1. С какой целью мне создавать себе дополнительную кучу забот, проверяя нет ли у меня дыр во всем серваке, везде ли правильные права и т.д. Это я бы попросила вас аргументировать бессмысленное увеличение прав скрипта. И про недоверие туда же — зачем мне одна степень безопасности вместо двух, если я ничего не теряю при этом.
2. Расскажите подробнее как вы откроете доступ к /tmp при помощи open_basedir и где при этом у вас лежат файлы проекта.
его запрещают для того чтоб не могли сделать LFI в tmp. Вкратце о реализации, отправляют на phpinfo evil файл с заранее увеличенным размером пока сервак ждёт файл до конца, получают имя файла в tmp, юзают LFI баг, исполняют код.
Манипуляции можно проводить там же — во временной папке, но после move_uploaded_file. После чего уже класть в рабочее место чистенький и обработанный файлик (само собой удаляя его из /tmp).
Ну а чем это безопаснее манипуляций ДО move_uploaded_file?
Потому что, ДО move_uploaded_file манипуляции сделать невозможно, нет? При правильно настроенном сервере.
Более того (могу ошибаться, но мне всегда казалочь, что это так) move_uploaded_file может выполнить только тот же процесс apache/php, кто его принял и положил на диск
А почему это невозможно? Возможно. При правильно настроенном сервере.

Лень проверять, но если память не изменяет, к этому файлу доступ может получить кто угодно, но на нём режимы 0600 и он существует до перемещения move_uploaded_file или до завершения скрипта (удаляется по завершении, если не был перемещён).
Смотря какие манипуляции :-) Удалить можно. А вот изменить размер по-моему нельзя
Ах. Если файл нужен не весь, а будет подвергаться обработке на лету, то я не стал бы это делать in place. Вынул бы всё, что мне из него нужно, а сам он пусть удаляется.
1. Apache/PHP кладет файл в /tmp (понятно что она не должна быть доступна web-серверу, который может что-то запускать)
2. Делаете move_uploaded_file опять же в /tmp с каким-то именем (алгоритм которого знаете только вы — повышаем безопасность)
3. Обрабатываете как надо файл.
4. Shutdown function на всякий случай для удаления (GC по сути)
5. Перемещаете в нужное место (как вариант доступное из web-сервера, хотя в настоящее время для картинок обычно это уже не apache/php — что тоже повышает безопасность)
честно говоря, у меня файлы даже не загружаются, если /tmp не прописан в open_basedir, сейчас вот при загрузке получаю [error] => 6 (что есть UPLOAD_ERR_NO_TMP_DIR), честно говоря никогда не задавался вопросом совместной работы директив open_basedir и upload_tmp_dir, потестирую на досуге.
Доверять MIME не стоит, но все же если хочется проверить, то он приходит в массиве $_FILES, и необходимости в getimagesize() нет

P.S. Но все же, сохранения файла, и последующая его валидация (в данном случаи самая простая, на тип файла), не должна быть реализованная так как в испытуемому движке
У вас проблема не в open_base_dir, а в правах записи на папку tmp.
на винде? )
если /tmp не прописан в open_basedir

Подумалось, что это вряд ли «винда». А по вашему «на винде» нет прав доступа записи на папку? Или production у вас тоже на «винде» (код то работать везде должен)? Тогда, извиняюсь, не знаю нюансов работы upload_file php в windows, но подозреваю, что что-то тут не чисто.
в win с правами как-то попроще, да и все работало, пока не прописал только одну open_basedir
при чем здесь production?
В никсах также, если папка не входит в open_basedir то ни временные файлы ни сессии php сохранять в нее не будет.
«Chroot» процесса php происходит перед запуском сессий и всего прочего, так что доступа в недозволенные папки уже не оказывается к моменту старта сессии или сохранения временного файла. sferrka что-то путает.
В никсах также, если папка не входит в open_basedir то ни временные файлы ни сессии php сохранять в нее не будет.

Description: Ubuntu 12.04.4 LTS
PHP Version => 5.5.12-2+deb.sury.org~precise+1

Apache conf
php_admin_value open_basedir /var/www/sites/

	session_start();
	echo $_SESSION['ddas'];
	var_dump($_SESSION);
	$_SESSION['ddas']=1;
var_dump(session_save_path());
file_put_contents('/tmp/1.txt','sdfds');

1array(1) { [«ddas»]=> int(1) } string(13) "/var/lib/php5"
Warning: file_put_contents(): open_basedir restriction in effect. File(/tmp/1.txt) is not within the allowed path(s): (/var/www/sites/)


Не знаю, что у вас не сохраняет.
вы уверены что в данном случае php_admin_value open_basedir /var/www/sites/ обрабатывается? Попытка открыть файл вне этой директории заканчивается неудачей? Хотя возможно, в 5.5 версии изменили это поведение и ваш пример действительно работает.
Я изменила комментарий, с примером про сохранение и нет, я думаю так это работало всегда. У вас есть доказательства обратного?
А вот тут вы ошибаетесь, приходящий mime в массиве $_FILES содержит лишь заголовок, предоставленный браузером и не проверяется на корректность. С помощью данной уязвимости, в бородатые годы «куллхацкеры» натворили достаточно дел, если вы понимаете о чем я.
да, по этому я писал что доверять mime не стоит, но файлу можно можно и заголовок подделать
Что значит «заголовок подделать»?
Браузер при загрузке передает Content-Type файла, который затем попадает в $_FILES.
Ясно. В общем, в $_FILES есть два значения, которым можно доверять, не опасаясь — размер файла и временное имя. Остальные (в том числе — имя контрола, в котором был загруженный файл) — прилетают из сети и к ним нужно относиться с подозрением.
Совершенно верно.

Вообще, первое правило защиты — не доверять ничему, что как-либо соприкасалось с данными извне. В Ruby есть очень интересная концепция tainted, когда виртуальная машина будет автоматически помечать все переменные, которые «взаимодействовали» с пользовательскими данными, как «ненадёжные» и в таких функциях, как exec, можно затем заблокировать выполнение, получив tainted-переменную.
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
А еще добавили примеси, пространства имен, генераторы, имена классов по статическому псевдо-свойству «class» и многое другое. Не знаю какие вы видите скриншоты, но по работе сталкиваюсь с разными версиями PHP.
Движок писался до того, как 5.3 стала мейнстримом, а safe mode выпилили только в 5.4, то есть спустя лет так 5-7.
НЛО прилетело и опубликовало эту надпись здесь
Такое ощущение что функция загрузки специально придумана под уязвимость
Как мне кажется — уже каждый джуниор знает что проверять надо расширение, а не mime тип, и уж точно не сохранять файл куда либо до проверки…
 $info = getimagesize('temp/'.$filename.'.'.$ext);
  
 $tmp_ext = str_replace('image/', '', $info['mime']);
 if ($ext != $tmp_ext) {
      ...
 }

здесь тоже гениально, для JPEG файлов mime type всегда будет image/jpeg, а расширение файлов обычно .jpg, соответственно jpg файлы всегда будут переименовываться в .jpeg, зачем?
*.jpeg эквивалентен *.jpg, nginx и другие сервера его отлично распознают. Другой вопрос — зачем проверять на 'jpg' в коде, если там всегда будет 'jpeg'? Авторы перестраховались.
разницы между .jpg и .jpeg нет, зачем тогда делать лишнюю работу по переименованию файлов, по моему авторы ступили, а не перестраховались
Основная дыра — в запуске php.
Если её закрыть, ничего не будет.

Закрывается, например, так:

<Files ~ "\..+$">
    Deny from all
</Files>

php_flag engine 0
RemoveHandler .phtml .php .php3 .php4 .php5 .php6 .phps .cgi .exe .pl .asp .aspx .shtml .shtm .fcgi .fpl .jsp .htm .html .wml
AddType application/x-httpd-php-source .phtml .php .php3 .php4 .php5 .php6 .phps .cgi .exe .pl .asp .aspx .shtml .shtm .fcgi .fpl .jsp .htm .html .wml
AddType "text/html" .php .cgi .pl .fcgi .fpl .phtml .shtml .php2 .php3 .php4 .php5 .asp .jsp


Согласен, оптимальный вариант — выносить все скрипты за DocumentRoot, а для корня сайта отключать их выполнение. Либо если так не получается — отключать выполнение скриптов для пользовательских папок. Но увы, до сих пор многие движки грешат наследием и позволяют запускать всё и отовсюду. В этой связи особенно интересны ошибки в настройке nginx+fcgi, когда запустить на выполнение можно любой скрипт вне зависимости от его расширения (ссылка есть в статье).
Но как узнать хеш, в который переименовался файл?
Так ведь он вычисляется по содержимому файла, который вы загрузили.
Точно же. Я совсем забыл, что можно хеш создавать без соли.
Хеш — детерминированноя функция, где на входе содержимое нашего файла.
Даже не пирожное
Если я правильно понял, вы о CakePHP? Тогда целиком поддерживаю, ибо он просто из багов состоит, удивительно, что приведенный автором пример — не из него)
Видимо имелось в виду, что статья якобы вообще не торт (на самом деле это не так).
> Публиковать ли ещё статьи об уязвимостях?
Конечно публиковать, вы ещё спрашивате?

Я правильно понимаю, что генератор «бомбы» просто внедряет phpinfo() в файл из сжатых нулей?
«Бомба» прерывает «естественный» ход событий и файл bomb.php остается в каталоге для временных файлов…
Да, как уже сказано в топике, надо сделать две вещи:
— залить файл,
— заставить сервер выполнить залитый файл.

Для выполнения второй задачи, есть промежуточная задача: нужно знать куда именно и под каким именем залился наш файл.
В топике указан способ решения первой и промежуточной задач.
Решение второй задачи здесь не приведено, так как это, как ни странно, совсем другая задача :)
Тьфу ты блин, раньше времени отправилось и не поправилось.
Промежуточная задача здесь «решена» в большей части благодаря «ошибкам» в конфигурации.
> Публиковать ли ещё статьи об уязвимостях?
Да, только избавьтесь от «ложили».
этим способом ложили


Возможно это сленг, как у каменщиков.
Предположительно, слово «ложить» вышло из литературного языка, когда в моде был французский. Потому что прямой перевод этого слова — coucher — имеет сексуальный подтекст (в постель ложить). Тогда оно в тему: я ваш сервер того… поимел ложил :)
Лично мне статья понравилась, с удовольствием почитаю ещё.

Вспомнилось, как N лет назад прикалывались над друзьями и отправляли по почте файл в 1-2 Мб.
И называли файл «Фотки.rar» или «CS_1.6.zip».
А человек его распаковать не мог, потому что места на диске не хватало.

Кстати, может кому пригодится…
>> $ext = strtolower($ext[count($ext)-1]);
$ext = strtolower(end($ext))

Что касается проверки, то она какая-то странная…
Сначала происходят операции с файлом, а потом только проверяется у него расширение.
По логике, сначала должна происходить проверка входных данных, и только затем выполняться какие-то действия.
Кстати, может кому пригодится…
$ext = strtolower(end($ext))

еще лучше
pathinfo($filename, PATHINFO_EXTENSION);
Ну, я обычно пишу вообще: strrchr($fn, '.');
За шутку «640 петабайт хватит для всех» отдельное спасибо! ;)
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории