MailBomber - программа для отправки огромного
количества сообщений на указанный e-mail.
Очень неприятно обнаружить в собственном
почтовом ящике 300-400 сообщений от разных
адресатов. Еще неприятней знать, что среди
них какое-то количество тех, которые нужно
принять. В этой статье я расскажу о
некоторых способах защиты от такого
вандализма.
1. В диспетчере сообщений (есть
практически во всех почтовых клиентах)
принять только заголовки писем. Посмотреть
заголовок хотя бы одного не нужного письма.
Запомнить IP-адрес. Отметить все на удаление.
Потом просмотреть весь список, выбрать
нужные, отметить на получение и получить их.
Потом через whois узнать провайдера и
написать ему. Если не поможет - написать
матом со всяческими угрозами :-). В принципе
не долго, но геморно. Особенно пункт "просмотреть
весь список и выбрать нужные сообщения".
Провайдеры обычно из-за пары баксов
удавятся. Поэтому отправителю ничего не
будет (скорее всего). Письма с угрозами и
предупреждениями можно не посылать.
Есть несколько "НО". Хорошие MailBomber-ы
не только ставят в поле обратного адреса
разные адреса, но и умеют подделывать
заголовок, прикидываясь пересылочным
почтовым сервером. Мало того, что обратный
адрес определить очень тяжело, так еще и
разные темы, размеры и аттачи сообщений.
Есть еще и SOCK-5 прокси. Или, допустим, у меня
есть на странице форма для отправки
сообщения. Написать многопоточный флудер с
использованием этой формы и простого http-прокси
не так и сложно. Запустить у себя или с шелла
и все. Запросы идут через прокси, а в
обратном адресе стоит адрес sendmail хостера.
2. Идея реализации защиты в том, чтобы
получать только заголовки сообщений и
заданное количество строк. Потом искать
вхождение определенной строки (в любом
случае какая-то одинаковая строка для всех
сообщений будет) и автоматически удалять
сообщения на сервере. Поговорим о способах
реализации.
2.1. Способ для юзера. Поставить The Bat!. В
последних версиях есть создание правил для
приема сообщений. Прописать правило.
Инициализировать диспетчер сообщений.
Плюсы: просто, как у ребенка леденец
отобрать (так говорят те, кто никогда этого
не пробовал). Минусы: каждый раз нужно
создавать новое правило, искать где оно там
в меню и The Bat! все-таки платный. Хотя
программа настолько хороша, что можно и не
пожлобиться.
2.2. Способ для хакера. Написать скрипт.
Скрипт будет получать часть сообщений,
искать вхождение искомой строки и удалять,
удалять, удалять. Сунуть туда, где он
исполнится. Плюсы: не юзает твой траффик.
Минусы: надо иметь место, где он исполнится.
Вот самый простейший пример на Perl (необходим
модуль Mail::POP3Client), который просматривает
сообщения и удаляет все, в которых
содержится "stroka":
#!/usr/bin/perl
use Mail::POP3Client;
$user = "user";
$password = "password";
$host = "pop3.domain.com";
$stroka = "127.0.0.1"
$pop = new Mail::POP3Client( USER => $user, PASSWORD => $password, HOST
=> $host );
for( $i = 1; $i <= $pop->Count(); $i++ )
{
foreach( $pop->HeadAndBody( $i ) )
{
chomp $stroka;
if (/$stroka/)
{
$pop->Delete( $i );
}
}
}
$pop->Close();
Можно чуть усложнить: сделать вывод
сообщений, дописать получение $user, $password, $host,
$stroka из формы и т.д. Но все равно надо
получить почтовым клиентом или telnet-ом
парочку сообщений для определения строки
вхождения.
2.3. Настоящие герои всегда идут в обход.
Может под рукой не будет Perl или места, где
вышеописанный скрипт надо будет запустить.
Попробуем самим написать программу с
использованием неблокирующего сокета. Тем
более, что рабочие исходники простейшего
клиента, работающего на TCP/IP, всегда должны
быть под рукой. Почему на асинхронном
сокете? Плюсы: не надо покупать The Bat!, не
нужен шелл, Perl и т.п., к тому же разминка в
кодинге никогда не помешает. Минусы: надо
писать :-), использовать немного своего
траффика и Delphi (который стоит чуть-чуть
дороже The Bat! :-)). На самом деле Delphi покупать
не обязательно. Функции работы с сокетами
практически одинаковы на всех языках и
системах. Переписать нижеследующий код на
разновидность C или асма достаточно просто.
Итак, создаем новый проект с одним окном и
кидаем на форму 4 Edit-а (закладка Standart; юзер,
пароль, POP3-сервер и строка вхождения), 2 SpinEdit-а
(закладка Samples; тайм-аут и количество
получаемых строк кроме заголовка), 1 Button (старт/стоп)
и несколько Label-ов (чтоб потом ничего не
напутать). Нужные нам команды POP3-сервера:
USER - юзер;
PASS - пароль;
STAT - информация откуда получим кол-во
сообщений;
TOP - получение заголовка и указанных строк
выбранного сообщения.
Вот исходники "Unit1.pas" (Delphi32):
var
Form1: TForm1;
StartD : Boolean = false;
addr : TSockAddrIn;
IPaddr : Cardinal;
s : TSocket;
WSAData : TWSAData;
Buf : array [1..4096] of Char;
BoolS : Byte;
sock_on, sock_off : Integer;
implementation
{$R *.DFM}
// функция отправки
почтовому серверу строки str
function postsend(str:string) : Boolean;
var
I1 :integer;
begin
Result:=false;
for I1:=1 to Length(str) do if send(s,str[I1],1,0)=SOCKET_ERROR then exit;
Result:=true;
end;
//Start/Stop
procedure TForm1.Button1Click(Sender: TObject);
var
DopS : String;
I1, DopI, DopI1 : Integer;
label l3, l2, l1;
//Перевод адреса в DWORD
function dns_addr(IPaddr : String) : Cardinal;
var
pa: PChar;
sa: TInAddr;
Host: PHostEnt;
begin
Result:=inet_addr(PChar(IPaddr));
if Result=INADDR_NONE then begin
host:=GetHostByName(PChar(IPaddr));
if Host = nil then exit
else begin
pa := Host^.h_addr_list^;
sa.S_un_b.s_b1 := pa[0];
sa.S_un_b.s_b2 := pa[1];
sa.S_un_b.s_b3 := pa[2];
sa.S_un_b.s_b4 := pa[3];
with TInAddr(sa).S_un_b do Result:= inet_addr(PChar(IntToStr(Ord(s_b1))+'.'+IntToStr(Ord(s_b2))+'.'
+IntToStr(Ord(s_b3))+'.'+ IntToStr(Ord(s_b4))));
end;
end;
end;
//select на сокет и тайм-аут
function postselect(s1 : TSocket; TimeS1 : Integer) : Boolean;
var
tvS : TTimeVal;
rfdsS : Tfdset;
begin
Result:=true;
// Ждем пока не сможем
принять или тайм-аут
while true do begin
// Если отмена - выход
if not StartD then begin
Result:=false;
exit;
end;
Sleep(100);
// Обнуляем дескриптор
сокетов
FD_ZERO(rfdsS);
FD_SET(s1,rfdsS);
tvS.tv_sec:=0;
tvS.tv_usec:=100;
// select-ируем сокет
select(0,@rfdsS,nil,nil,@tvS);
TimeS1:=TimeS1-100;
// Если можно читать -
выход и читаем
if FD_ISSET(s1,rfdsS) then exit;
// Если тайм-аут - выход
с false
if TimeS1<=0 then begin
Result:=false;
exit;
end;
end;
end;
begin
if StartD then StartD:=false else begin
// Начали
StartD:=true;
Form1.Button1.Caption:='Stop';
// Создание сокета
sock_on:=1;
sock_off:=0;
if WSAStartUp(257, WSAData) <> 0 then goto l1;
s := socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
if s = INVALID_SOCKET then goto l1;
if dns_addr(Form1.Edit1.Text)<=0 then goto l1;
// Переход в
неблокирующий режим
ioctlsocket(s,FIONBIO,sock_on);
addr.sin_family := AF_INET;
addr.sin_port := htons(110);
addr.sin_addr.S_addr:=dns_addr(Form1.Edit1.Text);
// Соединение
connect(s,addr,sizeof(addr));
if not postselect(s,Form1.SpinEdit2.Value) then goto l2;
// Получим ответ
сервера
FillChar(Buf,4096,0);
recv(s,buf,sizeof(Buf),0);
// USER
if not postsend('user '+Form1.Edit2.Text+#13+#10) then goto l2;
if not postselect(s,Form1.SpinEdit2.Value) then goto l2;
FillChar(Buf,4096,0);
I1:=recv(s,buf,sizeof(Buf),0);
if (I1>0)and(buf[1]<>'+') then goto l2;
// PASSWORD
if not postsend('pass '+Form1.Edit3.Text+#13+#10) then goto l2;
if not postselect(s,Form1.SpinEdit2.Value) then goto l2;
FillChar(Buf,4096,0);
I1:=recv(s,buf,sizeof(Buf),0);
if (I1>0)and(buf[1]<>'+') then goto l2;
// Количество сообщений
(STAT)
if not postsend('stat'+#13+#10) then goto l2;
if not postselect(s,Form1.SpinEdit2.Value) then goto l2;
FillChar(Buf,4096,0);
I1:=recv(s,buf,sizeof(Buf),0);
if (I1>0)and(buf[1]<>'+') then goto l2;
DopS:=trim(Buf);
DopS:=Copy(Buf,5,length(DopS)-5);
if Pos(' ',DopS)>0 then DopS:=copy(DopS,1,Pos(' ',DopS)-1);
DopI:=StrToInt(DopS);
// DopI = кол-во сообщений.
Запускаем цикл если > 0
if DopI>0 then begin
for DopI1:=1 to DopI do begin
// Получим заголовок и
указанное кол-во строк сообщения
if not postsend('top '+IntToStr(DopI1)+' '+IntToStr( Form1.SpinEdit1.Value)+#13+#10)
then goto l3;
if postselect(s,Form1.SpinEdit2.Value) then begin
FillChar(Buf,4096,0);
recv(s,buf,sizeof(Buf),0);
// Если встречаются
вхождения - удалим
if Pos(Form1.Edit4.Text,Buf)>0 then begin
postsend('dele '+IntToStr(DopIl)+#13+#10);
if not postselect(s,Form1.SpinEdit2.Value) then goto l3;
FillChar(buf,4096,0);
I1:=recv(s,buf,sizeof(Buf),0);
if (I1>0)and(buf[1]<>'+') then goto l3;
break;
end;
end;
// Следующее сообщение
l3:
dec(DopI);
end;
postsend('quit'+#13+#10);
end else ShowMessage('0 message');
end;
l2:
ioctlsocket(s,FIONBIO,sock_off);
closesocket(s);
l1:
StartD:=false;
Form1.Button1.Caption:='Start';
end;
end.
Ради уменьшения размера исходника, здесь
нет обработки ошибок, не совсем корректно
сделан "Stop" и прием ограничен 4096 байт,
но всё более или менее работает. Дописать не
проблема. Теперь как человек ни будет
изменять настройки MailBomber-а, писать сам
скрипты и программы, потеть и тратить свой
траффик или шелл на то, чтобы тебя зафлудить
- защита займет всего ~ 1-2 kB входящих данных
на каждое сообщение (при способе 2.3) и
несколько минут на настройки. Естественно,
способ 2.3 подходит для сравнительно
небольшого количества сообщений. Если же
атака осуществляется с шелла или
многопоточным флудером, то писем может быть
несколько тысяч. Тогда предпочтительней
способ 2.2.
Если же у человека есть список из
нескольких сот работающих прокси-серверов,
несколько шеллов, серверов с sendmail и формами
для отправки сообщений, мощная выделенка и
большой список с разными сообщениями,
темами, обратными адресами и т.д., то так
просто ты не отделаешься. Мыло тебе вряд ли
удастся сохранить. Можно конечно стукнуть в
CyberPolice, но.... Не красиво это. Хотя, можно всем
кто пишет сказать, чтобы вставляли в первую
строчку какую-то определенную фразу и
удалять все сообщения без этой фразы. В
общем, варианты есть.