Автореферат
Библиотека
Ссылки
Отчет о поиске
Роликовый спорт
Автобиография
Автореферат Библиотека Ссылки Отчет о поиске Роликовый спорт

Гошко Владислав Викторович

Донецкий национальный технический университет

Факультет: вычислительной техники и информатики

Специальность: экономическая кибернетика

Тема выпускной работы: Маркетинговый анализ средств защиты от спама

Руководитель: Костин Валерий Иванович

Гошко В.В.

Отправка электронных писем через анонимный SMTP сервер.


В данной статье мы рассмотрим методы отправки почты с анонимным адресом, ну и естественно и другими фальшивыми данными в заголовке письма. Но я не буду рассматривать старинный случай отправки почты через всем известную программу sendmail которая "зашита" во многих *nix системах.

Я рассмотрю посылку почты через анонимный smtp сервер. Для начала двавйте разберемся с Simple Mail Transfer Protocol. И так, обычно smtp сервис установлен на 25 порту.

Работа с ним не так уж сложна: после соединения с smtp сервером он посылает нам так называемый банер, в котором обычно содержится название сервера, версия и т.д. Далее идет работа с командами, давайте рассмотрим их все:

КомандаОбязательнаОписание
HELOXИдентифицирует модуль-передатчик для модуля-приемника (hello).
MAILXНачинает почтовую транзакцию, которая завершается передачей данных в один или несколько почтовых ящиков (mail).
RCPTXИдентифицирует получателя почтового сообщения (recipient).
DATA-Строки, следующие за этой командой, рассматриваются получателем как данные почтового сообщения. В случае SMTP, почтовое сообщение заканчивается комбинацией символов: CRLF-точка-CRLF.
RSET-Прерывает текущую почтовую транзакцию (reset).
NOOP-Требует от получателя не предпринимать никаких действий, а только выдать ответ ОК. Используется главным образом для тестирования.(No operation).
QUIT-Требует выдать ответ ОК и закрыть текущее соединение.
VRFY-Требует от приемника подтвердить, что ее аргумент является действительным именем пользователя.
SEND-Начинает почтовую транзакцию, доставляющую данные на один или несколько терминалов (а не в почтовый ящик).
SOML-Начинает транзакцию MAIL или SEND, доставляющую данные на один или несколько терминалов или в почтовые ящики.
SAML-Начинает транзакцию MAIL и SEND, доставляющие данные на один или несколько терминалов и в почтовые ящики.
EXPN-Команда SMTP-приемнику подтвердить, действительно ли аргумент является адресом почтовой рассылки и если да, вернуть адрес получателя сообщения (expand).
HELP-Команда SMTP-приемнику вернуть сообщение-справку о его командах.
TURN-Команда SMTP-приемнику либо сказать ОК и поменяться ролями, то есть стать STMP- передатчиком, либо послать сообщение-отказ и остаться в роли SMTP-приемника.

Не анонимный smtp сервер требует аутентификации от пользователя обычно этот метод - Basic. Рассмотрим пример работы с smtp сервером:

*** Connected to 127.0.0.1 ***
220 home-dugh2o5xj5 Microsoft ESMTP MAIL Service, Version: 6.0.2600.1 ready at Wed, 14 May 2003 15:49:44 +0300
helo xakep
250 home-dugh2o5xj5 Hello [127.0.0.1]
mail
530 5.7.3 Client was not authenticated
auth login
334 VXNlcm5hbWU6

Далее следует логин зашифрованный по основанию 64, и пароль, тоже зашифрованный. А вот анонимный сервер сразу принимает команду mail, но нам не анонимный сервер не нужен, поэтому рассмотрим транзакции с анонимным smtp сервером:

*** Connected to 127.0.0.1 ***
220 home-dugh2o5xj5 Microsoft ESMTP MAIL Service, Version: 6.0.2600.1 ready at Wed, 14 May 2003 15:57:31 +0300
helo xakep
250 home-dugh2o5xj5 Hello [127.0.0.1]
mail from: vasya@imhacker.ru
250 2.1.0 vasya@imhacker.ru....Sender OK
rcpt to: home-dugh2o5xj5
250 2.1.5 home-dugh2o5xj5@home
data
354 Start mail input; end with <CRLF>.<CRLF>
From: tester@mail.ru
To: neo@matr.ix
Subject: Wake up Neo
X-Priority: 1 (Highest)
X-Mailer: The Bat! (v1.51) Personal
Content-type: text/html

Wake up Neo, matrix has you !

.
250 2.6.0 Queued mail for delivery
quit
221 2.0.0 home-dugh2o5xj5 Service closing transmission channel
*** Closed connection with 127.0.0.1 ***

Рассмотрим все по порядку:

mail from: vasya@imhacker.ru

Ответ был:

250 2.1.0 vasya@imhacker.ru....Sender OK

Все ОК, сервер принял адрес, как вы уже успели заметить данный smtp сервер это ESMTP от Microsoft, в котором есть не особо новая ошибка, заключается она в том, что вместо адреса vasya@imhacker.ru подставляем <>, и сервер и это принимает :), этим мы воспользуемся в нашем будущем скрипте.

250 в начале строки это код ответа мы самые популярные рассмотрим чуть попозже.

Смотрим дальше:

rcpt to: home-dugh2o5xj5

Ответ был:

250 2.1.5 home-dugh2o5xj5@home

Вообщем-то home-dugh2o5xj5 - это неправильный адрес почты, но сервер дописал @home, т.е. данное письмо прийдет локально в mail drop. Команда rcpt to:, может быть использована много раз, т.е. получатель данного письма может быть не один.

Дальше идет команда data, собственно и есть передача данных, завершающаяся <CRLF>.<CRLF> В data передаем заголовок письма - это такие поля как from, to, subject, x-priority, x-mailer и content-type.

В поле from, я указал tester@mail.ru, т.е. когда вы будете смотреть это письмо Outlook'ом, или чем-нибудь еще вы увидите, что письмо пришло не от vasya@imhacker.ru, а от tester@mail.ru, точно также и поле to - тоже фальшивое. Поле Subject - это тема сообщения.

X-Priority - это приоритет сообщения он бывает таким:

1 (Highest)
2 (High)
3 (Normal)
4 (Low)
5 (Lowest)

Поле X-Mailer - это программа с помощью которой было отправлено письмо, для лучшей маскировки, я взял "The Bat! (v1.51) Personal".

Хотя это все конечно выглядит довольно правдиво, но разберающийся человек, просто возьмет и откроет детали письма, и вот там-то и будет vasya@imhacker.ru, home-dugh2o5xj5@home, и ip вот так-то.

Как вы успели заметить код ответа состоит из трех цифр, каждая из них имеет свой определенный смысл, первая - означает было ли выполнение команды:

2 - успешно
5 - неуспешно
3 - еще не закончилась
По RFC на основании первой цифры и должен работать smtp клиент. Вот примерный список кодов ответа:
КодЗначение
211Ответ о состоянии системы или помощь
214Сообщение-подсказка (помощь)
220<имя_домена> служба готова к работе
221<имя_домена> служба закрывает канал связи
250Запрошенное действие почтовой транзакции успешно завершилось
251Данный адресат не является местным; сообщение будет передано по маршруту
354Начинай передачу сообщения. Сообщение заканчивается комбинацией .
421<имя_домена> служба недоступна; соединение закрывается
450Запрошенная команда почтовой транзакции не выполнена, так как почтовый ящик недоступен
451Запрошенная команда не выполнена; произошла локальная ошибка при обработке сообщения
452Запрошенная команда не выполнена; системе не хватило ресурсов
500Синтаксическая ошибка в тексте команды; команда не опознана
501Синтаксическая ошибка в аргументах или параметрах команды
502Данная команда не реализована
503Неверная последовательность команд
504У данной команды не может быть аргументов
550Запрошенная команда не выполнена, так как почтовый ящик недоступен
551Данный адресат не является местным; попробуйте передать сообщение по маршруту
552Запрошенная команда почтовой транзакции прервана; дисковое пространство, доступное системе, переполнилось
553Запрошенная команда не выполнена; указано недопустимое имя почтового ящика
554Транзакция не выполнена

Ну вот вообщем немного с протоколом разобрались теперь напишем саму программу.

#!/usr/bin/perl

use Socket;

# чтобы не использовать целый модуль MIME::Base64 используем только одну функцию взятую оттуда
# вот она
sub Encode_base64 {
my $res = "";
my $eol = $_[1];
$eol = "\n" unless defined $eol;
pos($_[0]) = 0;
while ($_[0] =~ /(.{1,45})/gs) {
$res .= substr(pack('u', $1), 1);
chop($res);}
$res =~ tr|` -_|AA-Za-z0-9+/|;
my $padding = (3 - length($_[0]) % 3) % 3;
$res =~ s/.{$padding}$/'=' x $padding/e if $padding;
if (length $eol) {
$res =~ s/(.{1,76})/$1$eol/g;
} $res; }

# создаем наш хеш для значений заголовка письма
%m = (
# маскируемся под Outlook
"X-Mailer" => "Microsoft Outlook Express 6.00.2600.0000",
# также взято из Outlook
"X-MSMail-Priority" => "High",
# и стандарое поле
"X-Priority" => "1"
);

# задаем массив приемников
@rcpts=("somebody@mail.com","vasya@mail.ru","test@mail.ru");

# исполняем функцию smtp, со всеми пречисленными аргументами
print smtp("127.0.0.1",\%m,"Wake Up Neo","neo@matr.ix",\@rcpts,"Wake up Neo, matrix has you twice, at 15 may 2003");

sub smtp{
# 1 аргумент сервер
# 2 входной хеш для доп. инфор. в хэдере письма
# 3 тема письма
# 4 список или скаляр отправител(я,ей)
# 5 само тело сообщения, в формате html и кодировкой win1251
# 6 (опциональный) логин
# 7 (опциональный) пароль

my($server,$g,$subject,$from,$to,$mdata,$user,$pass)=@_;
my($port,$iaddr,$paddr,$proto,%h);
local(*SOCK);$crlf="\r\n";
($server,$port)=split(/:/,$server,2);
# если порт не задан используем 25
$port = 25 if !$port;
# создаем сокет
$iaddr = gethostbyname($server) or die $!;
$paddr = sockaddr_in($port,$iaddr);
$proto = getprotobyname("tcp") or die $!;
socket(SOCK,PF_INET,SOCK_STREAM,$proto) or return "Cannot create socket: $!";
# соединяемся
connect(SOCK,$paddr) or return "Connect to $server failed: $!";
# получаем банер
chomp($banner = );
# если в банере встречается слово esmtp
# тогда используем вышеописанную ошибку и подставляем <>,
# запоминаем оригинальный адрес отправителя, для подставления в заголовок
$orig_from = $from;
if($banner =~ /esmtp/i){
$from="<>";
}
%h=(
"X-Mailer" => "The Bat! (v1.51) Personal",
"X-Priority" => "3 (Normal)",
);

%h = %$g if $g;
# идентифицируем себя
send(SOCK,"helo $server$crlf",0) or die $!;
chomp($data = );
if($data =~ /^2/){
# если задан логин и пароль шифруем их по осн. 64 и авторезируемся
if($user and $pass){
chomp($user=Encode_base64($user));
chomp($pass=Encode_base64($pass));
send(SOCK,"auth login$crlf",0);
chomp($data=);
# если метод auth login поддерживается тогда сервер
# будет ждать дальнейших действий и пошлет соответственный код
# иначе мы выйдем из функции и вернем то что "сказал" сервер
if($data =~ /^3/){
# если мы попали сюда значит все ОК
# посылаем логин
send(SOCK,"$user$crlf",0);
chomp($data=);
# посылаем пароль
send(SOCK,"$pass$crlf$crlf",0);
chomp($data=);
if($data =~ /^5/i){
# если авторизация не удалась вернули, закрыли сокет и
# вернули ответ сервера
close(SOCK);
return $data;
}
}else{
return $data;
}
}
# Здесь ситуация посложнее:
# Допустим, что у нас esmtp с вышеописанной ошибкой - это значит
# что мы заменили адрес отправителя на <>, но мы не учли что ошибка
# может быть уже исправлена, поэтому первый раз используем <>, а
# второй настоящий адрес отправителя...

# ставим лэйбл
MAIL:
send(SOCK,"mail from: $from$crlf",0);
chomp($data = );
if($data =~ /^5/i){
if($orig_from ne $from){
# если мы попали сюда, тогда ошибка была уже исправлена и
# мы ставим оригинальный адрес отправителя и возращаемся к лэйблу
# MAIL, чтобы заново послать команду mail from:
$from = $orig_from;
goto MAIL;
}
close(SOCK);
return $data;
}else{
# проверяем если переменная $to ссылка, (т.е. это должно быть массив)
# тогда по циклу вызываем эти команды последовательно
# если был сбой - возвращаем ответ сервера
if(ref $to){
for($i=0;$i<=$#$to;$i++){
send(SOCK,"rcpt to: $$to[$i]$crlf",0);
chomp($data=);
if($data =~ /^5/i){
close(SOCK);
return $data;
}
}
}else{
# если мы здесь значит переменная $to не ссылка, а просто переменная :)
send(SOCK,"rcpt to: $to$crlf",0);
chomp($data=);
if($data =~ /^5/i){
close(SOCK);
return $data;
}
}
$bak=$mdata;
$mdata="";
# идем по хешу и добавляем поля в переменную $mdata
foreach $val (sort keys %h){
$mdata.="$val: $h{$val}$crlf";
}
# здесь в поле стоит $orig_from для маскировки, т.е. даже если ошибка esmtp была
# все равно маскируемся
$mdata.="From: $orig_from$crlf";
$mdata.="To: ";
# если $to ссылка тогда соединяем через запятую в переменную $mdata
if(ref $to){
$mdata.=join(",",@$to);
}else{
$mdata.="<$to>";
}
$mdata.=$crlf;
# добавляем всякие поля...
$mdata.="Reply-To: $from$crlf";
$mdata.="Subject: $subject$crlf";
$mdata.="Content-Type: text/html; charset=\"windows-1251\"";
$mdata.=$crlf.$crlf.$bak;
# вызываем команду data
send(SOCK,"data$crlf",0);
chomp($data=);
# если она поддерживается код ответа сервера должен быть - продолжать (т.е. 3)
if($data =~ /^3/i){
# посылаем данные
send(SOCK,"$mdata$crlf",0);
# посылаем конец данных, т.е. .
send(SOCK,"$crlf.$crlf",0);
chomp($data=);
if($data !~ /^2/){
close(SOCK);
return $data;
}
# завершаем сессию и возвращаем Dropped
send(SOCK,"quit$crlf",0);
close(SOCK);
return "Dropped";
}else{
close(SOCK);
return $data;
}
}
}else{
close(SOCK);
return $data;
}
}

Ну вот вообщем-то и вся программа, вы можете ее переделать под web-интерфейс, или под STDIN - это уже ваше дело...

Вот и все, удачи в работе с протоколом простой почты!