Κυλλήνη

Верификация e-mail: сложный путь. Brain dump

Иногда приспичивает сделать что-то сложно и мучительно, just for fun, так вот один из тех случаев.

Скачать SMTP honeypot: smtphoneypot.zip

Скрипт ковырялся давно, подробностей не помню, так что коротко. Ситуация примерно следующая: имеется некий хост, на котором поднят веб-сервер. Провайдер предоставляет динамический IP-адрес и категорически запрещает трафик на порты 80, 25, 22 и некоторые другие. Также пользуемся услугами DynDNS чтобы попадать на этот хост по адресу, например, что-то.gotdns.org и центром CAcert для подписи наших сертификатов, при этом очень хочется генерить и подписывать сертификаты на имя что-то.gotdns.org, а злобному касерту подавай проверку с помощью электронной почты, что данный домен принадлежит нам.

Финт ушами заключается в том, что вместо того, чтобы потратить 20 минут на настройку какого-нибудь доступного существующего почтового сервака, мне припёрло потратить пару часов на ковыряние с перловым скриптом :-D Зато теперь при случае можно словить почту на любой адрес на некотором домене, не разворачивая многотонный почтовик, если в том нет необходимости.

Общение с ханипотом выглядит примерно так со стороны клиента:

$ telnet localhost 25
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 mail.example.com Sendmail 8.14.0 Mon May 16 18:48:37 2011
HELO local.domain.name
250 mail.example.com Hello local.domain.name, pleased to meet you.
MAIL FROM: mail@domain.ext
250 2.1.0 mail@domain.ext... Sender ok
RCPT TO: mail@otherdomain.ext
501 5.5.2 Syntax error in parameters scanning "TO:"
250 2.1.5 mail@otherdomain.ext... Recipient ok
DATA
354 Enter mail, end with "." on a line by itself
Subject:-type subject here-
Message here
.
250 2.0.0 g8684xUD014698 Message accepted for delivery

И со стороны сервера:

# ./smtphoney.pl
Mon May 16 18:48:11 2011: 8113: SMTP server started on port 25
Mon May 16 18:48:37 2011: 8113: SRV: incoming connection from: 127.0.0.1
Mon May 16 18:48:37 2011: 8113: SRV: 220  mail.example.comSendmail 8.14.0
Mon May 16 18:48:41 2011: 8113: CLN: HELO local.domain.name
Mon May 16 18:48:41 2011: 8113: SRV: 250 mail.example.com Hello local.domain.name, pleased to meet you.
Mon May 16 18:48:48 2011: 8113: CLN: MAIL FROM: mail@domain.ext
Mon May 16 18:48:48 2011: 8113: SRV: 250 2.1.0 mail@domain.ext... Sender ok
Mon May 16 18:48:54 2011: 8113: CLN: RCPT TO: mail@otherdomain.ext
Mon May 16 18:48:54 2011: 8113: SRV: 501 5.5.2 Syntax error in parameters scanning "TO:"
Mon May 16 18:48:54 2011: 8113: SRV: 250 2.1.5 mail@otherdomain.ext... Recipient ok
Mon May 16 18:49:03 2011: 8113: CLN: DATA
Mon May 16 18:49:03 2011: 8113: SRV: 354 Enter mail, end with "." on a line by itself
Subject:-type subject here-
Message here
.
Mon May 16 18:49:18 2011: 8113: SRV: 250 2.0.0 g8684xUD014698 Message accepted for delivery

В настройках нашего хоста что-то.gotdns.org в аккаунте dyndns нужно временно включить опцию «I have mail server with another name and would like to add MX hostname…» и указать там адрес нашего «ответственного почтового сервера», например mail.example.com. На этом самом сервере запустить honeypot, после чего в учётке на cacert можно застолбить наш домен с динамическим именем, а перловый скрипт словит письмо с проверочным кодом от касерта и тот удовлетворится. Динамический домен станет Verified для CAcert, и можно будет спокойно подписывать сертификаты для него.

#!/usr/bin/perl
#!c:\perl\bin\perl.exe

# SMTP Honeypot
# (c) 2007 - admin at abuse.ch
# Version 1.2 - DATE: FEB 9th 2007
# mod by hermes - DATE: NOV 27th 2010

use Socket;
use strict;

# Port on which the service will be run
my $port = 25;

# The name of the hostname you want to simulate eg. mail.example.com
my $host = "YOURHOST.dyndns.com";

# SMTP-Daemon Version
my $smtpver = "Sendmail 8.14.0";

# Get the protocol nr. of TCP
my $tcp = getprotobyname('tcp');

# Path to Logfile
my $logfile = './honeypot.log';

# Path to Blacklist
my $blacklist = './honeyblacklist.txt';

# Define variables
my $lastline;
my $ipaddress;

# Create a socket at $port with protocol $tcp
socket(Server, PF_INET, SOCK_STREAM, $tcp) or die "socket: $!";
setsockopt(Server, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) or die "setsockopt: $!";
bind(Server, sockaddr_in($port, INADDR_ANY)) or die "bind: $!";
listen(Server,SOMAXCONN) or die "listen: $!";
print STDERR ("\n");

# Display a message that the server is now running
logmsg ("SMTP server started on port ", $port, "\n\n");

my ( $addr, @inetaddr );

my $old_handle = select Client;

$| =1;

select $old_handle;

*STDOUT = *Client;
*STDIN = *Client;

while (1)
{

	$addr = accept(Client,Server);

	my(undef, undef, $inetaddr) = unpack('S n a4 x8', $addr);
	@inetaddr = unpack('C4', $inetaddr);

	print STDERR ("\n\n");

	logmsg ("SRV: incoming connection from: ", join(".", @inetaddr));

	&READ();
}

close Client;

sub logmsg
{
	print STDERR "\n", (scalar (localtime), ": ", $$, ": ", @_);
	# Save to Logfile START
	open (DATA, "+>>$logfile") or die "can't open $logfile $!";
	print DATA "\n", (scalar (localtime), ": ", $$, ": ", @_);
	close (DATA);
	# Save to Logfile END
}

sub READ
{

	my $saidhelo = 0;
	my $saidmail = 0;
	my $maildata;

	print ("220 ", $host, ' ', $smtpver, ' ', scalar (localtime), "\r\n");
	logmsg("SRV: 220 ", join($host, ' ', $smtpver));

	while (1)
	{
		my $commands = <stdin>;

		if (!defined ($commands))
		{
			return;
		}

		$commands =~ s/[\r\n]+|\s+$//g;
		my @commands = split (/\s+/, $commands);

		logmsg ("CLN: ", $commands, "\n");

		if (!defined $commands[1])
		{
			$commands[1] = '';
		}

		if (!defined $commands[2])
		{
			$commands[2] = '';
		}

		my %smtphash = (
			AUTH => "503 AUTH mechanism not available.\x0d\x0a",
			BADRCPT => "503 5.0.0 Need MAIL before RCPT\x0d\x0a",
			BADHELO => "503 5.0.0 Polite people say HELO first\x0d\x0a",
			DATA => "354 Enter mail, end with \"\.\" on a line by itself\x0d\x0a",
			DATAerr => "503 5.0.0 Need MAIL command\x0d\x0a",
			DATAsent => "250 2.0.0 g8684xUD014698 Message accepted for delivery\x0d\x0a",
			EHLOOUT => "501 5.0.0 HELO requires domain address.\x0d\x0a",
			ERR => "500 5.5.1 Command unrecognized: $commands\x0d\x0a",
			ETRN => "500 5.5.2 Parameter required\x0d\x0a",
			EXPN => "502 5.7.0 Sorry, we do not allow this operation.\x0d\x0a",
			HELOERR => "501 5.0.0 Invalid domain name\x0d\x0a",
			HELOOUT => "501 5.0.0 HELO requires domain address.\x0d\x0a",
			HELOIN => "250 $host Hello $commands[1], pleased to meet you.\x0d\x0a",
			MAILFROM => "553 5.5.4 MAILDATA... Domain name required for sender address MAILDATA\x0d\x0a",
			MAILTO => "250 2.1.0 MAILDATA... Sender ok\x0d\x0a",
			MAIL => "501 5.5.2 Syntax error in parameters scanning \"$commands[1]\"\x0d\x0a",
			NOOP => "250 2.0.0 OK.\x0d\x0a",
			QUIT => "221 2.0.0 $host closing connection..\x0d\x0a",
			RCPTTO => "250 2.1.5 MAILDATA... Recipient ok\x0d\x0a",
			RESET => "250 2.0.0 Reset state.\x0d\x0a",
			RSET => "250 2.0.0 Reset state\x0d\x0a",
			STARTTLS => "454 4.3.3 TLS not available after start...\x0d\x0a",
			VRFY => "252 2.5.2 Cannot VRFY user; try RCPT to attempt delivery (or try finger).\x0d\x0a",
			HELP => "214-2.0.0 Sendmail v8.4.3\x0d\x0a214-2.0.0 Topics:\x0d\x0a214-2.0.0 HELO EHLO MAIL RCPT DATA\x0d\x0a214-2.0.0 RSET NOOP QUIT HELP VRFY\x0d\x0a214-2.0.0 EXPN VERB ETRN DSN AUTH\x0d\x0a214-2.0.0 STARTTLS\x0d\x0a214-2.0.0 For more info use \"HELP \".\x0d\x0a214-2.0.0 For local information send email to Postmaster at your site.\x0d\x0a214 2.0.0 End of HELP info.\x0d\x0a",
			EHLO => "250-ENHANCEDSTATUSCODES\x0d\x0a250-8BITMIME\x0d\x0a250-SIZE\x0d\x0a250-DSN\x0d\x0a250-ONEX\x0d\x0a250-ETRN\x0d\x0a250-XUSR\x0d\x0a250 HELP\x0d\x0a",

		);

		if (!defined ($commands[0]) or $commands[0] eq '')
		{
			next;
		}
		elsif ($commands[0] =~ /^HELO$/i)
		{
			if($commands[1] eq '')
			{
				print ($smtphash{HELOOUT});
				logmsg("SRV: ", $smtphash{HELOOUT});
			}
			elsif ($commands[1] =~ /[\!\@\#\$\%\^&\*\(\)\|\\,>?\/\"\':;\{\}]/)
			{
				print $smtphash{HELOERR};
				logmsg("SRV: ", $smtphash{HELOERR});
			}
			else
			{
				print $smtphash{HELOIN};
				logmsg("SRV: ", $smtphash{HELOIN});
				$saidhelo = 1;
			}
		}
#		elsif ($commands[0] =~ /^HELP$|^RESET$|^NOOP$|^AUTH$|^STARTTLS$|^VRFY$|^EXPN$|^ETRN$|^RSET$/i)
		elsif ($commands[0] =~ /^HELP$|^RESET$|^NOOP$|^AUTH$|^STARTTLS$|^VRFY$|^EXPN$|^ETRN$|^RSET$/)
		{
			# Kinda dummy commands
			print ($smtphash{$commands[0]});
			logmsg("SRV: ", $smtphash{$commands[0]});
			sleep 1;
		}
		elsif ($commands[0] =~ /^EHLO$/i)
		{
			if($commands[1] eq '')
			{
				#print ($smtphash{EHLOOUT});
				logmsg("SRV: ", $smtphash{EHLOOUT});
			}
			else
			{
				print ($smtphash{HELOIN});
				logmsg("SRV: ", $smtphash{HELOIN});
				#print ($smtphash{EHLO});
				logmsg("SRV: ", $smtphash{EHLO});
				$saidhelo = 1;
			}
		}
		elsif ($commands[0] =~ /^QUIT$/i)
		{
			print $smtphash{QUIT};
			logmsg("SRV: ", $smtphash{QUIT});
			return;
		}
		elsif ($commands[0] =~ /^MAIL$/i)
		{
			$maildata = $commands[2];
			if ($commands[2] eq '')
			{
				$maildata = $commands[1];
				if ($commands[1] =~ m@.*from:< (.*)>@i)
				{
					$maildata = $1;
				}
			}

			if ($saidhelo == 0)
			{
				print $smtphash{BADHELO};
				logmsg("SRV: ", $smtphash{BADHELO});
				
				# Is there a space after from:
			}
			elsif ($commands[1] =~ /from:/i)
			{
				if ($commands[2] =~ /\@/ || $commands[1] =~ /\@/)
				{
					$saidmail = 1;
					$smtphash{MAILTO} =~ s/MAILDATA/$maildata/g;
					print ($smtphash{MAILTO});
					logmsg("SRV: ", $smtphash{MAILTO});
				}
				else
				{
					$smtphash{MAILFROM} =~ s/MAILDATA/$maildata/g;
					print ($smtphash{MAILFROM});
					logmsg("SRV: ", $smtphash{MAILFROM});
				}
			}
			elsif ($saidhelo == 1)
			{
				#print ("250 2.1.5 $maildata... Recipient ok\x0d\x0a");
				print ($smtphash{MAIL});
				logmsg("SRV: ", $smtphash{MAIL});
			}
		}
		elsif ($commands[0] =~ /^RCPT$/i)
		{
			$maildata = $commands[2];
			#if ($commands[1] =~ m@.*to:< (.*)>@i || $commands[2] =~ m@< (.*)>@ )
			if ($commands[1] =~ /to:< (.*?)>/i || $commands[2] =~ /< (.*?)>/ )
			{
				$maildata = $1;
			}
			elsif ($commands[1] =~ /to: < (.*?)>/i || $commands[1] =~ /< (.*?)>/ )
			{
				$maildata = $2;
			}
			else
			{
				print ($smtphash{MAIL});
				logmsg("SRV: ", $smtphash{MAIL});
			}

			$smtphash{RCPTTO} =~ s/MAILDATA/$maildata/g;
			print($smtphash{RCPTTO});
			logmsg("SRV: ", $smtphash{RCPTTO});
		}
		elsif ($commands[0] =~ /^DATA$/i)
		{
				if ($saidmail == 1)
				{
					print ($smtphash{DATA});
					logmsg("SRV: ", $smtphash{DATA});
					
					my $z = 0;
					while (defined(my $lastline = </stdin><stdin>))
					{
						print STDERR ($lastline);
						last if $lastline =~ /^\.\x0d\x0a$/;
					}
					print ($smtphash{DATAsent});
					logmsg("SRV: ", $smtphash{DATAsent});
				}
				else
				{
					print ($smtphash{DATAerr});
					logmsg("SRV: ", $smtphash{DATAerr});
				}

		}
		else
		{
			print ($smtphash{ERR});
			logmsg("SRV: ", $smtphash{ERR}, ": ERROR cmd1: " , $commands[1], " cmd2: " , $commands[2]);
		}
	}
}
</stdin>

Оригинальный код тут: http://www.abuse.ch/?p=15