Читаем Атака на Internet полностью

Нынешние кракеры, наверное, кусают себе локти, что родились не 10 лет назад, – ведь тогда хакером мог прослыть тот, кто умел методично перебирать адреса компьютеров и вводить в качестве имени и пароля что-нибудь типа guest/guest [10]. Видимо, большинство хостов (в том числе и военных – в те времена возможность проникновения в секретные хосты еще не являлась мифом) вскрывались именно так. Были известны стандартные входные имена, присутствующие в операционной системе при ее установке на компьютер (см. табл. 9.1). Особо продвинутые кракеры, скорее всего, догадывались вводить в качестве паролей наиболее распространенные имена, жаргонные словечки и т. п.

Интересно заметить, что большинство средств защиты многих современных ОС успешно борется именно с таким примитивным классом атак, называя его intruder detection (обнаружение нарушителей). В ОС после набора неправильного пароля обычно приняты задержка в несколько секунд, а также ограничение максимального числа неправильно набранных паролей подряд. Эти меры не позволяют взломщику удаленно перебирать пароли. (Естественно, что сегодня, если хакер и будет заниматься перебором, то не в реальном времени.) Но, видимо, в те далекие годы не было даже таких мер.

Таблица 9.1. Примеры имен и паролей по умолчанию в различных ОС

Технология переполнения буфера

Примерно в это же время хакерами был придуман способ передачи управления чужеродному коду, не только ставший классическим, но и по сей день являющийся основным методом эксплуатации многих уязвимостей как удаленно, так и локально. Он с успехом применялся и применяется в большинстве операционных систем. Первое нашумевшее его применение было в вирусе Морриса (см. раздел «Червь»), хотя наверняка и до этого способ открывался и переоткрывался (а может быть, и использовался) несколько раз.

Итак, одной из основных проблем, стоящей перед кракером, является необходимость исполнения написанного им (то есть вредного) кода на машине, которую он атакует. Иначе говоря, он должен указать компьютеру, с какого адреса размещается этот код, то есть занести в указатель команд (обычно он называется instruction pointer – IP) нужный ему адрес. Это, безусловно, может быть сделано штатными средствами операционной системы – путем запуска соответствующей программы. Но тут у кракера возникает две проблемы:

1. У него может не быть доступа на атакуемый компьютер, то есть возможности исполнения программ.

2. Даже если доступ (login) у него есть, то привилегий, данных ему, может оказаться недостаточно для выполнения некоторых функций того вредного кода, который он написал. Обычная цель кракера – получить полный контроль над машиной, что ему, естественно, просто так никто не даст.

Для решения этих проблем приходит в голову следующее: передать некоторому привилегированному процессу такие данные, которые интерпретировались бы им как код. При этом отсутствие доступа на компьютер решается передачей удаленных данных через демоны (сценарий 1 – любой пользователь Internet имеет такую возможность). Для выбора локальных привилегированных процессов (то есть при наличии доступа) также хорошо подходят демоны, если они запущены от имени суперпользователя или SUID root-программы (сценарий 3).

Итак, задача кракера уточнилась: ему необходима привилегированная программа, которая получает какие-то входные данные от непривилегированных пользователей. И дело за малым – осталось заставить программу исполнить эти данные как код. Как следует из названия раздела, такой прием получил название buffer overflow (в переводе «переполнение буфера», хотя более точно сказать «переполнение буфера в стеке»).

Рассмотрим его. Весьма часто в процедурах программист отводит для своих нужд некоторый локальный буфер, имеющий фиксированный размер. Этот размер обычно устанавливается исходя из здравого (или не очень здравого) смысла. Например, если читается строка с экрана, то программист может ограничить размер буфера 80 символами, имя файла на NTFS не должно содержать более 255 символов – именно такой буфер может быть отведен в этом случае и т. п.

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

process_data (char *data)

{

char buf[FIXED];

...

strcpy (buf, data);

<необходимая обработка данных в буфере>

...

return;

}

Перейти на страницу:

Похожие книги