В большинстве языков есть команды вызова системных команд. Наиболее популярной такой командой в PHP является функция system(). Если эта функция (и другие функции обращения к системе) не нужна, то интерпретатор переводят в защищенный режим, дабы не спровоцировать себя и хакера на легкий метод создания заднего прохода. Нет, "задний проход" – это не то, что ты подумал. Об этом будешь думать у проктолога. Я имел ввиду back door.
Но что, если обращение к системе необходимо, потому что возможностей языка не хватает? Давай сначала посмотрим, чем это пахнет, а потом разберемся, как можно с этим бороться. А пахнет это очень даже не приятно.
Для начала я написал простейший PHP-сценарий, содержащий уязвимость.
<form name="syst" action="sys.php" method="get"> Command: <input name="command" size=50> <input type="submit" value="Find"> </form> <? if (isset($_GET['command'])) { $command = $_GET['command']; print("Executed command: <b>$command<P>"); print(system($command)); } ?>
Этот код отображает форму для ввода команды, которая передается функции system(). Результат выполнения отображается на Web-странице.
Запусти пример, введи в поле ввода команду ОС, на которой установлен Web-сервер и нажмите кнопку Find. Если сервер установлен на Linux или другую UNIX-подобную ОС, то в качестве примера можно ввести команду ls. В результате ты должен увидеть на экране содержимое текущей директории.
Выполнение системных команд в сценарии всегда связано с повышенным риском, потому что тут управлять правами доступа и фильтровать параметры чаще всего намного сложнее.
Конечно, такой идеальный вариант, как в данном примере встретить очень сложно, но все же, бывают случаи, когда подобные сценарии администраторы создают для удобства управления Web-сервером и надеются, что хакер о них никогда не узнает или не получит к ним доступа (например, если данный код расположен в панели администрирования). Намного чаще можно встретить вызов определенной системной команды, а через переменную передается дополнительный параметр. Например, если нужно пингануть машину, то в коде сценария можно будет встретить следующую строку:
print(system("ping –c 5 ".$command));
В данном случае выполняется системная Linux команда ping с параметром –c 5, которая проверяет связь с указанным компьютером. Параметр –c используется для указания количества попыток или посылаемых пакетов, в других ОС он может быть другим, например, в Windows для этой цели используется -n. В данном случае мы ограничиваемся пятью пакетами. А вот адрес компьютера указывается через переменную $command, которая содержит переданные пользователем сценарию данные. Пользователь должен ввести в форму адрес компьютера, с которым необходимо проверить связь и в результате мы увидим результат.
На первый взгляд все хорошо, и безопасно. Но это только если пользователь будет указывать IP-адрес или имя компьютера и ничего другого. А что другое может передать злой хакер? А он может передать другую команду. Дело в том, что ОС Linux может выполнять несколько команд подряд. Одной строкой можно передать две и более команды, поставив между ними в качестве разделителя точку с запятой. Например, таким образом можно выполнить две команды — ping и просмотр текущей директории:
ping –c 5 microsoft.com; ls –al
Эта строка будет разбита на две команды: ping –c 5 microsoft.com и ls –al. И обе эти команды будут выполнены системой, а результат мы увидим на экране.
Если у тебя есть Web-сервер, на котором можно протестировать PHP-сценарии, то загрузи туда файл с кодом, показанным выше, и попробуй передать через поля ввода строку "microsoft.com; ls –al". Будут выполнены обе команды, и результат обеих будет отображаться на Web-странице.
А если передать строку "microsoft.com; cat /etc/passwd", то в результате мы увидим на экране содержимое файла со списком пользователей, конечно, если у тебя будет достаточно прав на доступ к этому файлу.
Получается, что такая безобидная ситуация может привести к серьезным проблемам. Ведь с помощью команд ОС хакер легко может выполнить такие операции, как просмотр системных файлов, копирование, удаление, короче говоря, все, что только позволят права, под которыми работает WEB сервер, а это уже не мало.
Обращения к системе опасно вне зависимости от используемого языка программирования. Как и с SQL-инъекцией, при вызове системных команд, хакер использует систему, а не возможности языка программирования, на котором написаны сценарии. Программист должен обеспечить проверку получаемых от пользователя данных и обезопасить систему от вторжения. Не забывай, что из-за одного сценария может пострадать не только определенный Web-сайт, но и все Web-сайты, расположенные на том же сервере, если они крутятся в одном корне и к ним будет доступ.
В PHP есть одна интересная функция, которая упрощает жизнь в нашей борьбе за безопасный код: EscapeShellCmd. Она удаляет из переменной любые символы, которые могут быть использованы в командном интерпретаторе как произвольные команды. Это значит, что если необходимо передать переменную в функцию exec, system и ей подобную, то предварительно необходимо проверить эту переменную с помощью EscapeShellCmd. Например:
$cmd = EscapeShellCmd($cmd); system($cmd);
В первой строке мы убираем из переменной $cmd все, что может представлять опасность, а после этого уже переменная передается в функцию system.
Да, функция EscapeShellCmd — хороший инструмент, но полностью полагаться на нее не стоит. Я рекомендую выполнять дополнительную проверку с помощью регулярных выражений.
В большинстве книг можно встретить предложения использовать регулярные выражения для проверки того, что переданные данные корректны. Отличное решение, я сам использую этот метод. Но хочется предупредить о следующих нюансах:
- при написании регулярных выражений очень легко ошибиться, потому что их написание — занятие не из простых. По регулярным выражениям можно писать целые книги и тут столько нюансов, что малейшая ошибка может привести к тому, что шаблон будет работать не корректно;
- проверка данных на соответствие шаблонам — хорошо, но далеко не всегда гарантирует, что данные будут соответствовать шаблону, потому что может быть лазейка, благодаря которой регулярное выражение можно будет обмануть. Допустим, шаблон проверяет, чтобы передаваемое имя файла имело формат fileXXX.txt, где XXX — имя файла. Это очень легко обходиться с помощью нулевого байта. Достаточно передать имя: /etc/passwd%00fileXXX.txt. Выражение будет соответствовать шаблону, прописанному в сценарии, но указывает на имя файла /etc/passwd.
Дабы уменьшить вероятность ошибки, лучше использовать максимально простые и максимально жесткие шаблоны. Для этого нужно получать от пользователя только простые данные. Например, если пользователь должен передавать данные только из чисел, то нет смысла выдумывать регулярное выражение, которое опишет шаблон. Просто вырезаем все, кроме цифр и можем быть уверенными, что ни каких опасных символов в переменной не осталось.
Вот так вот мы быстренько, но подробно пробежались по проблеме выполнения системных команд. Надеюсь, что ты подчеркнул для себя что-то новое.
Внимание!!! Если ты копируешь эту статью себе на сайт, то оставляй ссылку непосредственно на эту страницу. Спасибо за понимание
Паника, что-то случилось!!! Ничего не найдено в комментариях. Срочно нужно что-то добавить, чтобы это место не оставалось пустым.
Добавить Комментарий