В данной статье мы рассмотрим методы отправки почты с анонимным адресом, ну и,
естественно, другими фальшивыми данными в заголовке письма. Но я не буду
рассматривать старинный случай отправки почты через всем известную программу
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 - это уже ваше дело...
Вот и все, удачи в работе с протоколом простой почты !