безопасность


Генерируем безопасность

Уже настали времена, когда пользователи интернета знают, что нельзя верить каждому электронному письму, что нельзя лазить по adult-сайтам без антивируса и что пароли должны быть длинными и непонятными. И если первые два требования выполняются довольно просто, то с паролями возникают вполне определенные проблемы. Действительно, запомнить что-то вроде «dsƶSD!HK» довольно проблематично, а записывание на бумажку нередко становится такой же брешью в безопасности, как и простой пароль.

Для таких случаев давно придумали программы типа Roboform (или даже сразу встроили возможность запоминания паролей в браузер, как в Opera и FireFox), поэтому эту тему мы оставим в покое. Лучше упомянем другой факт: нередко успешный взлом серверов крупных компаний (не говоря уже о любительских сайтах) возможен из-за того, что администратор ставит одинаковые пароли и на root-аккаунт, и на базу данных, и вообще на все что только можно. Что это: банальная лень, неверие в возможность взлома или просто недогляд — сказать сложно. Поэтому мы говорить об этом не будем. Лучше напишем программу, которая будет выдавать нам прорву паролей наивысшей сложности, дабы нам не приходилось мучаться, выдумывая очередную хитрую комбинацию. Такое приложения пригодится не только одиночным пользователям, но и администраторам сайтов/форумов/FTP-серверов, которым необходимо «придумывать» пароли для пользователей своих сервисов. Прежде чем писать статью, я провел некоторое время наедине с Гуглом. Из беседы с ним я вынес две вещи:
1) большинство генераторов паролей — онлайновые;
2) они не предназначены для генерации очень большого числа паролей.

Немного рекламы: программа, которую мы напишем, на моей машине (Core 2 Duo 3,0 Гц, 2 Гб оперативки) выдавала полмиллиона 15-значных паролей за 0,3 (да, именно ноль целых три десятых) секунды, 5 млн — за 28 секунд. Когда я попросил один из сайтов (где количество паролей вводилось с клавиатуры, а не выбиралось из списка, и не было оговорено максимальное количество) сделать мне полмиллиона 15-значных паролей, он на меня обиделся и сказал, что «сервер не отвечает». Делаем выводы. Итак, нам понадобится Microsoft Visual Studio и немного знаний в области программирования на С++ и ассемблере (дабы выжать из программы все возможное). Интерфейс программы будет на приплюснутом Си, сама генерация — на асме. Для начала продумаем концепцию. Пусть у нас будет консольное приложение с тремя параметрами запуска: длина паролей, файл вывода паролей и количество паролей. Алфавит для генерации будет жестко прописан в программе (добавление выбора символов — несложная задача, поэтому оставлю ее читателю). Ну и для пущей важности будем выводить время, за которое генерируются пароли. Похоже, что все. Переходим непосредственно к кодингу. Создаем новое консольное приложение и сразу подключаем две дополнительные библиотеки:

#include <stdlib.h>
#include "windows.h"


Первая понадобится для конвертирования параметров программы в числа, вторая — чтобы рассчитать время. Сразу после инклудов вводим алфавит:
Static char szAlphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz1234567890-
=!@#$%^&*()_+";

Теперь переходим к написанию Самой Главной Функции (то бишь main):

if (argc < 4) //проверяем количество параметров, если их меньше трех (не считая имени файла), то ругаемся
{
printf("Using: passgen.exe length filename count\n");
printf("Press any key to quit...
getchar();
return 0;
}
int start = GetTickCount();//функция GetTickCount() возвращает количество миллисекунд, прошедшее с момента запуска системы; это значение мы будем использовать для вычисления времени
static int charcount = atoi(argv[1]); //первый параметр — количество символов в каждом пароле
char* sFileName = argv[2]; //второй — имя файла, куда будут записываться пароли
int passcount = atoi(argv[3]); //третий — количество паролей
FILE *stream = fopen(sFileName, "a"); //открываем файл для дозаписи, если он есть, и создаем, если его нет

Файл открыт и готов принимать свеженькие пароли. Теперь напишем функцию, которая будет эти самые пароли выдавать:

char* GetPassword(int charcount)
{
static char szPassword[256]; //буфер, в который мы будем писать пароль
ZeroMemory(szPassword, sizeof(szPassword)); //очищаем его
int alphalen = strlen(szAlphabet); //длина алфавита — пригодится для генерации
int mask_rnd = GetTickCount();//число, на основе которого будут браться случайные числа для генерации паролей
}

Приготовления закончены, теперь будем непосредственно генерировать:

__asm
{
Pushad //сохраняем все регистры
mov edi,offset szPassword //сюда будем записывать символы
mov ebx,offset szAlphabet //а отсюда брать
mov ecx,charcount //количество символов и одновременно счетчик цикла
L1: mov eax, alphalen //цикл выбора символов
call Random //получаем случайное число в диапазоне [0; alphalen-1]
//вообще, следующие три команды в большинстве случаев можно заменить одной
//mov [edi+ecx-1],byte ptr [ebx+eax], но компилятор Студии не поддерживает такой синтаксис :(
xor edx,edx //обнуляем edx
mov dl,byte ptr [ebx+eax] //берем из алфавита символ со случайным номером
mov [edi+ecx-1], dl //записываем его в строку с паролем (запись идет от конца к началу)
loop L1 //зацикливаемся
jmp L5 //если с паролем закончено — выходим

//получение случайного целого числа (функцию придумал не я — да простит меня ее автор, т.к. вторично разыскать ее для уточнения авторства я не смог)
// на входе:
// eax — диапазон получаемых значений
// на выходе:
// eax — случайное число из диапазона [0..Range-1]
Random:
push edx
imul edx,[mask_rnd],08088405h
inc edx
mov [mask_rnd],edx
mul edx
mov eax,edx
pop edx
ret
L5:
Popad //восстанавливаем регистры
}
return &szPassword[0]; //возвращаем результат


Теперь мы можем вызывать функцию столько раз, сколько нам надо, а результат записывать в файл (продолжаем писать функцию main):

for (int i=0; i < passcount;i++) //столько нам надо паролей
{
fwrite(strcat(GetPassword(charcount), "\n"), 1, charcount+1, stream); //пишем очередной пароль в файл
}
fclose(stream); //закрываем файл
float finish = GetTickCount(); //снова получаем время, прошедшее с момента запуска системы
finish = (finish — start) / 1000; //вычисляем разницу, т.е. сколько времени выполнялась генерация, и переводим ее в секунды
//откровенно говоря, такой способ подсчета времени выполнения кода нельзя назвать 100% верным,
//т.к. во время его выполнения управление может (и будет) передаваться другим потокам (это и есть псевдомногозадачность)
//т.е. за время (finish — start) будет выполняться не только наш код. 
Но нам большая точность и не нужна
printf("Passwords were successfully generated for %.3f seconds\n", finish); //пишем уведомление об окончании работы
printf("Press any key to quit..."); //и выходим
getchar();
return 0;
}

Теперь можно скомпилировать и запустить программу:

passgen.exe 15 C:\pass.txt 500000

и наслаждаться плодами своего труда.



PainKiller, q@sa-sec.org SASecurity gr.

© компьютерная газета