Отправка электронных писем через анонимный SMTP сервер.
В данной статье мы рассмотрим методы отправки почты с анонимным адресом, ну и естественно и другими фальшивыми данными в заголовке письма. Но я не буду рассматривать старинный случай отправки почты через всем известную программу sendmail которая "зашита" во многих *nix системах.
Я рассмотрю посылку почты через анонимный smtp сервер. Для начала двавйте разберемся с Simple Mail Transfer Protocol. И так, обычно smtp сервис установлен на 25 порту.
Работа с ним не так уж сложна: после соединения с smtp сервером он посылает нам так называемый банер, в котором обычно содержится название сервера, версия и т.д. Далее идет работа с командами, давайте рассмотрим их все:
Команда | Обязательна | Описание |
HELO | X | Идентифицирует модуль-передатчик для модуля-приемника (hello). |
X | Начинает почтовую транзакцию, которая завершается передачей данных в один или несколько почтовых ящиков (mail). | |
RCPT | X | Идентифицирует получателя почтового сообщения (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 - это уже ваше дело...
Вот и все, удачи в работе с протоколом простой почты!