Продолжим кодинг своей собственной
системы. В прошлой
статье
я написал структуру самого
сервера (пусть примитивного). Но система,
состоящая лишь из одной части - не система.
Поэтому пришло время написать клиент,
который будет делать запрос на сервер и,
собственно, перебирать пароль.

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

Итак, что мы имеем? Сервер написан таким
образом, что при обращении к нему он выдает
всего одну строку - зашифрованный пароль (getpass.cgi).
Причем делает это рандомно и каждый раз
проверяет количество участников, которые
расшифровывают выданный пароль. Если число
превышает заданную в конфе переменную,
скрипт временно комментирует пароль (в
будущем планируется сделать суточную крон-проверку
по базе. Если пароль не расшифрован -
отменять комментарий). Успешно разгаданный
пароль клиент отдает серверу (putpass.cgi).
Скрипт проверяет его правильность и если
все ок - начисляет поинты участнику.

Наконец разобрались. Теперь поглядим
структуру базы паролей.

user:$2a$08$cpIrpG.tfe/LfMfHnvb42.FfouTtGNcJk/43M9K5UQOm5fEB1eA6:14:0

Что означают параметры? С первыми двумя все
ясно - это пользователь и пароль. Затем идет
идентификатор участника, который прислал
пароль в базу. Последний параметр - счетчик,
о котором говорилось выше.

После этой вводной части можно приступить к
дальнейшему кодингу клиента, который в
полной мере реализует вышеизложенный
алгоритм.

Я всегда использую отдельный конфиг для
крупных проектов, чтобы не заморачиваться
изменением переменных. Поэтому у клиента
будет также свой конф. Вот он (расположен в
папке etc/fclient.conf относительно скрипта client.pl):

## Config file for Fsystem

# Server of Fsystem
$server='www.remotesystem.org'; ##
Адрес
сервера, где будут крутиться скрипты
системы.

# Path to getpass.cgi and putpass.cgi scripts
$getpath='/system/getpass.cgi';
$putpath='/system/putpass.cgi'; ##
Пути
к getpass и putpass соответственно.

# Client id, which recieve you from FS
$cid=31337; ##
Id клиента,
который будет передаваться серверу (обязательный
параметр).

# Default type for salt (1 - DES, 2 - MD5)
$stype=1; ##
Тип
запрашиваемого пароля по умолчанию. Его
можно будет задать также в командной строке
клиента.

# Save file (Salt will dump in this file)
$savefile='salt.save'; ##
Файл,
куда будет записан хэш после скачивания.

# External program for crack passwords
$progname="/usr/bin/john"; ##
Путь
к программе John The Ripper.

# Wordlist (optional) for cracking passwords
$wordlist="etc/wl.fs"; ##
Путь
к вордлисту (если он будет использован).

# Enable rules - enable option '-rules' when cracking by wordlist
$enablerules=1; ##
Параметр -rules
Джоника может перебирать много вариантов
заданного словарного слова (актуален
только при использовании словаря).

Вот весь конф. Большинство переменных из
него могут быть изменены в самой командной
строке клиента (соответственно они и будут
иметь наивысший приоритет), который мы
сейчас и напишем. В принципе, клиент очень
простой и разобраться в коде может даже
начинающий Perl'овик, но я не поленился
указать комментарии в коде. Итак,
собственно, сам клиент.

#!/usr/bin/perl

## Client for Fsystem. Options

# -g get the password salt from remote server
# (if value 1 - crack process started automatically 0 - get only)
# -t type of the password salt (1 - DES, 2 - OpenBSD Blowfish). DES by default
# -i client id
# -c begin crack process (1 - new crack, 2 - restoring process)

## are avaliable in this client

## Небольшой хелп. Так
принято в крупных проектах ;). Как ты понял,
может быть изменен тип хеша и идентификатор
клиента. Параметр -g (get) запрашивает пароль у
сервера. Если его значение 1 - происходит
автоматическая расшифровка пароля, если 0 -
клиент лишь сохранит пароль в заданном (в
конфе) файле.

use Getopt::Std; ##
Используем
модуль для парсинга (не путать с пирсингом
;)) командной строки.

use IO::Socket; ##
А также
сокеты.
$|++; ## Выключаем
буферизацию.

$config = 'etc/fclient.conf'; ## Путь
к конфигу.

if (-e "$config") {
require "$config"
} else {
exit print "Config is missing. Maybe you create it?\n";
}

## Если конф не найден -
ругаемся ;). Иначе обрабатываем его как Perl-скрипт.

getopt("gtic"); ## Хватаем
из командной строки параметры -g -t -i и -c (они
будут автоматически преобразованы в
переменные $opt_параметр).

if (defined $opt_g && defined $opt_c) {
usage ("Options -g and -c are not compartible\n"); ##
Если
указаны параметры -g и -c ругаемся на их
несовместимость.

}
getsalt($opt_g)
if (defined $opt_g); ##
Отправляемся
делать запрос если указан параметр -g

cracksalt()
if (defined $opt_c); ##
Отправляемся
расшифровывать пароль при опции -c

usage("No parameters given\n"); ## В
противном случае грязно ругаемся и выходим
(выход в usage()).

sub usage {
my $error=shift; ##
Ошибка -
возможный параметр процедуры.

print $error
if ($error); ##
Если она есть -
печатаем ее на экран.

print "Usage: $0 <options>\nOptions are:
-g get the password salt from remote server
\t(if value 1 - crack process started automatically 0 - get only)
-t type of the password salt (1 - DES, 2 - OpenBSD Blowfish). DES by default
-i client id
-c begin crack process (0 - new crack, 1 - restoring process)\n";
exit 0; ##
Затем выводим
команды для использования и выходим.

}

sub getsalt { ## Процедура
запроса пароля у сервера.

my $flag=shift; ##
Хватаем
значение параметра -g

my($socket,@answer,$answer); ##
Локализуем
переменные процедуры

$socket=IO::Socket::INET->new("$server:80") || die print "Cant
connect to Fsystem.
Check your Internet Connection\n"; ##
Создаем
сокет с сервером, либо ругаемся на
отсутствие соединения.

$socket->autoflush(1); ##
Выключаем
буферизацию у сокета.

print $socket "GET $getpath?id=$cid&type=$stype HTTP/1.0\n"; 
print $socket "Server: $server\n\n"; ##
Пишем
в сокет запрос на выдачу пароля (обязательные
переменные id и type

while(<$socket>) {
$socket->read($answer,1024);
@answer=split("\n",$answer);
} ##
Считываем ответ,
который разбиваем построчно в массив для
удобного анализа.

$answer=$answer[scalar @answer-1]; ##
Ответ
(пароль) - предпоследняя строка (последняя
будет символом перевода строки).

open(SF,">$savefile");
print SF "$answer";
close(SF); ##
Дампим ответ
сервера в файл $savefile (ты уже забыл конфиг? ;)).

$flag == 1 ? cracksalt() : exit print "Salt was dumped\n"; ##
Если
значение 0 - выходим. Иначе переходим к
перебору.

}

sub cracksalt { ## Процедура
перебора пароля.

my($rules,$pwd,$binstring,@res); ##
Локализуем
переменные

if ($wordlist) { ##
Если
используем вордлист

$rules == 0 ? $rules='' : $rules='-rules'; ##
Проверяем
$rules

$binstring="$progname -w:$wordlist $rules $savefile 1>fs.tmp 2>/dev/null";
##
Формируем запрос (без
вывода на экран, сохранение результата в
файл).

print "Starting process...\n"; ##
Напоминаем
юзеру о том, что мы еще живы.

`$binstring`; ##
Выполняем
программу john.

$pwd=checkres(); ##
Когда
программа завершается - переходим на
проверку пароля.

putsalt("$pwd")
if (defined $pwd); ##
Если
пароль обнаружен - засылаем его серваку!

}
if ($opt_c eq 1) {
$binstring="$progname -i:all $savefile 1>fs.tmp 2>/dev/null"; ##
Если параметр crack равен
1 - запускаем john в режиме all.

} else {
unless(-f "~/.john/restore") {
system("cp restore ~/.john/");
} ##
Иначе ресторим
процесс. При отсутствии restore - копируем
бекап.

$binstring="$progname -restore $savefile 1>fs.tmp 2>/dev/null";
##
И формируем запрос с -restore.
}
## Some signal stuff...
$SIG{INT}=\&sigterm; ##
Ставим
обработчик на ctrl+c.

`$binstring`; ##
Запускаем john.
$pwd=checkres();
putsalt("$pwd")
if (defined $pwd);
exit ##
Повтор операции (что
и со словарем).

}

sub checkres { ## Процедура
проверки результата взлома

my(@res,$pwd); ##
Локализация
переменных

open(TMP,"fs.tmp");
@res=<TMP>;
if(scalar @res eq 1) {
print "No resuilts... Switch to ALL mode.\n";
return; ##
Если результатов
нет.. - переключаемся в режим ALL

} else {
($pwd,undef)=split(' ',$res[1]);
print "Done. Your pwd is $pwd\n"; ##
Иначе
пишем пароль на экран.

return $pwd;
}
close(TMP);
unlink("fs.tmp"); ##
Удалим
временный файл.

}

sub sigterm {
print "Exiting. You must use -c 2 for restoring session\n";
system("copy ~/.john/restore .");
unlink("fs.tmp"); ##
Предупреждение
о выходе и бекап restore-файла.

exit; ## Выход :).
}

sub putsalt { ## Самое
интересное - отправка результата.

my $pwd = shift;
my($salt,$socket,@answer,$answer); ##

Локализуем переменные и параметры

open(SF,"salt.save");
chomp($salt=<SF>);
close(SF); ##
Хватаем хэш+параметры
user и id.

$socket=IO::Socket::INET->new("$server:80"); ##
Создаем
сокет с сервером.

$socket->autoflush(1);
print $socket "GET $putpath?pwd=$pwd&salt=$salt&id=$cid
HTTP/1.0\n";
print $socket "Server: $server\n\n"; ##
Посылаем
запрос.

$socket->recv($answer,1024);
@answer=split("\n",$answer);
$answer=$answer[scalar @answer-1];
close($socket);
print "Salt was transferred to Fserver. Res is $answer\n"; ##
И
оповещаем возврат сервера (1 все в порядке, 0
- пароль неправильно расшифрован).

exit; ## Выходим.
}

Вот и весь клиент. Модульный кодинг придает
ему неопределенную красоту.

Что же, проверим его в действии ;). Пароль
будет выдан 1111, чтобы Джоник быстро
перебрал его по словарю.

[root@forbik client]# perl client.pl -g 1 -t 1
Starting process...
Done. Your pwd is 1111
Salt was transferred to Fserver. Res is 1

Как видим - все работает. Сервер обслуживает
клиента на все 100%. Теперь попробуем так:

[root@forbik client]# perl client.pl -c 1
Starting process...
Done. Your pwd is 1111
Salt was transferred to Fserver. Res is 1

Опция -c также действует. Все идет по плану ;).

Таким образом, система готова. Остается
лишь намутить статистику и начисление
поинтов за результаты. Об этом мы поговорим
в следующей тематической статье. А пока -
ставь клиент-сервер и обкатывай систему на
себе ;). Удачи!

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии