Коллектив авторов - Защита от хакеров корпоративных сетей
Основываясь на форматах параметров authkey и uname, естественно было предположить, что значение authkey относится к файловой системе. Прежде всего потому, что значение параметра authkey в формате username.999999. temp не похоже на информацию, которую хранят в базе данных. Вполне возможно, что приложение разделяло параметр authkey на три части, используя для запроса к базе данных имя пользователя и случайное число. При этом отпадала необходимость в передаче через значение параметра uname имени пользователя, а постоянное окончание «.temp» становилось бесполезным и бессмысленным. Следуя интуиции и зная, что формат представления параметра authkey похож на задание имени файла, было высказано предположение, что значение параметра authkey определяет имя файла, как в итоге и оказалось.
Конечно, PacketStorm была поставлена в известность, и ошибка была исправлена. Выбранное ими решение лаконично, но давайте рассмотрим другой вариант исправления ошибки. Пусть программа была бы исправлена следующим образом:if (-e “ authkey_directory/$authkey” && $authkey=~/^$uname/) {
print “Welcome $uname!”;
# do stuff as $uname
} else {
print “Error: not authenticated”;
}Хотя это решение и выглядит правильным (в фрагменте кода выполняется проверка того, что значение параметра authkey начинается со строки символов, совпадающих со значением параметра uname), но оно ошибочно. В фрагменте кода проверяется только то, что параметр authkey начинается со значения параметра uname. Это значит, что если значение authkey было бы «rfp.234623.temp», то можно было бы использовать в качестве значения параметра uname символ «r», так как строка «rfp» начинается с символа «r». Ошибка исправляется заменой $authkey=~/^$uname/ íà $authkey=~/^$uname./. В результате выполняется проверка на совпадении всего значения первой части параметра authkey с uname. PacketStorm решил использовать вариант, похожий на следующий:
@authkey_parts = split(“.”, $authkey);
if ($authkey_parts[0] eq $uname && -e «authkey_directory/
$authkey»){ …,Приведенный вариант – всего лишь иной способ убедиться в совпадении значения первой части параметра authkey со значением uname. Но все равно в демонстрационном коде присутствуют погрешности. Зачем дублировать и сравнивать имя пользователя параметра authkey со значением uname? Оно всегда должно быть одним и тем же. Храня его в двух местах, всегда существует вероятность ошибок. Корректнее использовать следующий код:
if (-e «authkey_directory/$uname.$authkey.temp»){...
В этом случае следует лишь переслать URL:
authkey=234562&uname=rfp.
В новом варианте программы имя файла «rfp.234562.temp» собирается из двух половинок. Тем самым гарантируется, что в приложении будет использовано одно и то же имя пользователя, совпадающее со значением параметра uname, и что атакующий может ссылаться только на файлы с расширением имени «.temp», поскольку так определено в программе. Тем не менее возможны и другие трюки злоумышленника. Например, передача символа NULL в конце значения параметра authkey вынудит систему проигнорировать добавление строки «.temp». Этого можно избежать, убрав все NULL из переданной строки. Но злоумышленник может использовать для аутентификации любой известный. temp-файл с помощью символов «../» в совокупности с другими хитростями. Поэтому лучше убедиться, что параметр $uname содержит только допустимые символы (предпочтительно лишь буквы), а $authkey – только числа. В общем случае для аутентификации лучше использовать запросы SQL к базе данных пользователей и паролей. Например, следующий запрос:
SELECT * FROM Users WHERE Username=’$name’ AND Password=’$pass’,
где $name и $pass – параметры имя пользователя и его пароль. В результате выполнения запроса будут найдены все записи в базе данных, у которых имя пользователя и пароль совпадают со значениями параметров, введенными пользователем. Полученный результат может быть обработан следующим образом:
if ( number_of_return_records > 0) {
# username and password were found; do stuff
} else {
# not found, return error
}Приведенный фрагмент программы обрабатывает результат запроса. Если в результате запроса были найдены записи, то введенная пользователем комбинация из имени пользователя и его пароля действительна. Тем не менее это образец небрежного программирования, основанного на ошибочном предположении. Представьте, что злоумышленник передаст в качестве параметра $pass следующее значение:
boguspassword OR TRUE
В результате злоумышленник будет аутентифицирован как законный пользователь, потому что в ответ на запрос будут найдены все записи базы данных, а согласно логике приложения при нахождении хотя бы одной записи, удовлетворяющей запросу, полномочия пользователя считаются подтвержденными.
Ошибка заключается в логическом условии (при number_of_return_records > 0). Это условие предполагает наличие ситуации, при которой одной комбинации имени пользователя и его пароля соответствуют многочисленные записи в базе данных. В правильно разработанном приложении подобная логическая ошибка должна быть исправлена. Исправленное логическое условие должно быть таким: (number_of_return_records == 1). Потому, что если число записей равно нулю, то в результате запроса не было найдено ни одной записи с заданной комбинацией имени пользователя и пароля, что свидетельствует о неуспешном завершении процедуры аутентификации. Одна найденная запись говорит об успешном завершении процедуры аутентификации, а более одной – о наличии проблемы из-за ошибки работы с базой данных или атаки злоумышленника.
Конечно, из-за взятого в кавычки параметра $pass описанная только что ситуация может не произойти. Результат непосредственной подстановки значения параметра в запрос будет выглядеть так:… AND Password=’boguspassword OR TRUE’
В итоге часть запроса OR TRUE не будет интерпретироваться как команда. Можно расставить собственные кавычки (boguspassword' OR TRUE) и преодолеть это ограничение. При этом запрос примет вид:
… AND Password=’boguspassword’ OR TRUE’
Обычно это приводит к выдаче диагностического сообщения о том, что кавычка непарная. Для того чтобы избавиться от сообщения, можно использовать символ комментария за ключевым словом TRUE или использовать в запросе замыкающую кавычку. При присвоении параметру $pass значения
boguspassword’ OR NOT Password=’otherboguspassword
запрос станет следующим:
… AND Password=’boguspassword’ OR NOT Password=’otherboguspassword’
и замыкающая кавычка окажется на своем месте. Естественно, что правильно выполненная в программе проверка корректности данных и расстановки кавычек не даст такому запросу выполниться. Именно так и реализована аутентификация в пакете wwwthreads (www.wwwthreads.com). Запрос, включенный в их демоверсию, выглядит следующим образом:
my $query = qq!
SELECT *
FROM Users
WHERE Username = $Username_q
!;А этому фрагменту кода предшествуют следующие строчки:
my $Username_q = $dbh->quote($Username); my $Password_q = $dbh->quote($Password);
В этих строчках проводимые проверки гарантируют, что значение параметра $Username правильно взято в кавычки. Благодаря этому все, что говорилось по поводу расстановки кавычек, работать не будет. Несмотря на это, еще раз рассмотрим запрос. Видно, что описанная проверка анализирует только имя пользователя. Это означает, что если кто-то использует правильное имя пользователя, то результат запроса заставит поверить пакет wwwthreads в то, что пользователь успешно прошел процедуру аутентификации. Исправленный запрос выглядит следующим образом:
my $query = qq!
SELECT *
FROM Users
WHERE Username = $Username_q
AND Password = $Password_q
!;О найденной ошибке была оповещена служба поддержки пакета wwwthreads, и проблема была решена.
Маскировка непредвиденных данных
Многие люди часто не замечают разновидности атак злоумышленника, основанных на маскировке характерных признаков (сигнатуры) непредвиденных разработчиком входных данных программы. На проверке сигнатуры данных основана работа некоторых приложений: сканеров вирусов и систем обнаружения вторжений. Цель маскировки сигнатуры данных состоит в преобразовании злонамеренной сигнатуры (сигнатуры вируса или атаки) таким образом, чтобы приложение не смогло ее распознать. Более подробно системы обнаружения вторжений (IDS) описаны в главе 16.
В основе работы всех сетевых систем обнаружения вторжений, основанных на проверке сигнатуры, лежит список различных величин и ситуаций, которые они ищут в сети. Системы обнаружения вторжений оповещают об опасности при нахождении совпадающих характеристик сети с данными из списка. Обычно системы обнаружения вторжений используются для предупреждения об атаках и нарушений установленных правил поведения в сети (например, политики безопасности).
В качестве примера рассмотрим Web-запрос. Пусть система обнаружения вторжений настроена на подачу сигнала тревоги при появлении любого запроса, содержащего строку /cgi-bin/phf. Предполагается, что в рассматриваемом случае давно известная уязвимость, основанная на вызове программы phf интерфейса CGI, будет оформлена по стандартным правилам протокола HTTP, и поэтому ее будет легко обнаружить. Но опытный злоумышленник, используя тонкие моменты в работе протокола HTTP и атакуемого Web-сервера, может исказить сигнатуру строки /cgi-bin/phf.