В данной статье мы рассмотрим методы отправки почты с анонимным адресом, ну и,
естественно, другими фальшивыми данными в заголовке письма. Но я не буду
рассматривать старинный случай отправки почты через всем известную программу
sendmail, которая "зашита" во многих *nix системах. Я рассмотрю посылку почты
через анонимный smtp сервер. Для начала
давайте разберемся с Simple Mail Transfer Protocol. Итак, обычно smtp сервис установлен на 25 порту. Работа с ним не
так уж сложна: после соединения с smtp сервером он посылает нам так называемый
банер, в котором обычно содержится название сервера, версия и т.д. Далее идет
работа с командами, давайте рассмотрим их все:

HELO (обязательна) - Идентифицирует модуль-передатчик для модуля-приемника (hello).

MAIL (обязательна) - Начинает почтовую транзакцию, которая завершается передачей данных в один или несколько почтовых ящиков (mail).

RCPT (обязательна) - Идентифицирует получателя почтового сообщения (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 <HOME-DUGH2O5XJ5kEej00000001@home-dugh2o5xj5> 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, вот так-то.

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

По RFC на основании первой цифры и должен работать smtp клиент. Вот примерный список кодов ответа:

Код Значение

211 Ответ о состоянии системы или помощь
214 Сообщение-подсказка (помощь)
....
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 аргумент сервер <host[:port]>
#
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 = <SOCK>);
#
если в банере встречается слово 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 = <SOCK>);
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=<SOCK>);
#
если метод auth login поддерживается тогда сервер
#
будет ждать дальнейших действий и пошлет соответственный код
#
иначе мы выйдем из функции и вернем то что "сказал" сервер
if($data =~ /^3/){
#
если мы попали сюда значит все ОК
#
посылаем логин
send(SOCK,"$user$crlf",0);
chomp($data=<SOCK>);
#
посылаем пароль
send(SOCK,"$pass$crlf$crlf",0);
chomp($data=<SOCK>);
if($data =~ /^5/i){
#
если авторизация не удалась вернули, закрыли сокет и
#
вернули ответ сервера
close(SOCK);
return $data;
}
}else{
return $data;
}
}
#
Здесь ситуация посложнее:
#
Допустим, что у нас esmtp с вышеописанной ошибкой - это значит
#
что мы заменили адрес отправителя на <>, но мы не учли что ошибка
#
может быть уже исправлена, поэтому первый раз используем <>, а
#
второй настоящий адрес отправителя...

# ставим лэйбл
MAIL:
send(SOCK,"mail from: $from$crlf",0);
chomp($data = <SOCK>);
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=<SOCK>);
if($data =~ /^5/i){
close(SOCK);
return $data;
}
}
}else{
#
если мы здесь значит переменная $to не ссылка, а просто переменная 🙂
send(SOCK,"rcpt to: $to$crlf",0);
chomp($data=<SOCK>);
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=<SOCK>);
#
если она поддерживается код ответа сервера должен быть - продолжать (т.е. 3)
if($data =~ /^3/i){
#
посылаем данные
send(SOCK,"$mdata$crlf",0);
#
посылаем конец данных, т.е. <CRLF>.<CRLF>
send(SOCK,"$crlf.$crlf",0);
chomp($data=<SOCK>);
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 - это уже ваше дело... 

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

Оставить мнение