Ни для кого не секрет, что технология RemoteApp внедрённая в Windows 2008 это ответ Microsoft технологиям доступа к приложениям компании Citrix. Всё бы ничего, но для использования данной технологии в повседневной жизни, без наличия RD Gateway, нужен открытый наружу RDP Port tcp/3389. Наблюдая за своими терминальными серверами, я обнаружил что сервера постоянно подвергаются brute-force атакам подбора паролей различных пользователей.
Дабы не испытывать судьбу я немного модифицировал схему доступа к RDP.
Так как сервера в большинстве случаев находятся за роутером с которого пробрасывается порт на терминальный сервер, сделаем ход конём и будем динамически открывать порт 3389 для тех, кто корректно авторизовался.
Для этого правим файл WindowsWebRDWebPagesDefault.aspx, добавляя ему внутрь некоторый новый функционал.
void goToFolder(string getLangVal)
{
Response.Redirect(getLangVal + "/Default.aspx" + Request.Url.Query,true);
}
private float getInternetExplorerVersion()
{
// Returns the version of Internet Explorer or a -1
// (indicating the use of another browser).
float rv = -1;
System.Web.HttpBrowserCapabilities browser = Request.Browser;
if (browser.Browser == "IE")
rv = (float)(browser.MajorVersion + browser.MinorVersion);
return rv;
}
void Page_Load(Object sender, EventArgs e)
{
string UserIPAddress = Request.ServerVariables["REMOTE_ADDR"];
string UserName = Request.ServerVariables["AUTH_USER"];
string safeString = System.Security.SecurityElement.Escape(UserName);
string url = "http://myrouter/cgi-bin/open.cgi?ip=" + UserIPAddress + "&user=" + safeString ;
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
request.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
request.AllowAutoRedirect = false;
request.KeepAlive = true;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
double ver = getInternetExplorerVersion();
if (ver > 0.0)
{
if (ver < 7.0)
Response.Redirect("TSWeb/Default.asp",true);
}
else
{
Response.Redirect("Unsupported/Default.asp",true);
}
string langCode = null;
System.Globalization.CultureInfo culture;
// For each request initialize the culture values with
// the user language as specified by the browser.
При загрузке страницы портала со списком Remote Application используется внутренняя аутентификация iis. Вышеуказанный скрипт выполнится только в случае корректной авторизации.
На роутере устанавливаем «net-mgmt/pftabled».
создаём ключ
dd if=/dev/random bs=20 count=1 | md5 | cut -c 1-19 > /etc/pftabled.key
и активируем сервис в rc.conf
pftabled_enable="YES"
pftabled_flags="-d -k /etc/pftabled.key -t 432000"
Далее поднимаем thttpd и создаём в cgi-bin скрипт open.cgi
#!/usr/bin/perl
use strict;
use warnings;
use CGI qw/:standard/;
use IO::Socket;
use Digest::HMAC_SHA1 qw(hmac_sha1);
use Net::SMTP;
use vars qw/%macs $pftabled $key %mac_ip/;
use constant PFTBLPORT => 56789;
use constant pfip => "127.0.0.1";
use constant PFTBLVERSION => 2;
use constant PFTABLED_CMD_ADD => 1;
use constant PFTABLED_CMD_DEL => 2;
use constant PFTABLED_CMD_FLUSH => 3;
use constant PFTBLCOMMAND => 1;
use constant PFTBLMASK => 32;
use constant SHA1_DIGEST_LENGTH => 20;
use constant PFTBLNAME => "RDP";
my $keyfile = "/etc/pftabled.key";
if (! -r $keyfile) {
print STDERR "Cannot Read KeyFile $keyfilen";
exit 1;
}
open(KEY, "<$keyfile");
sysread KEY, $key, SHA1_DIGEST_LENGTH;
close KEY;
$pftabled = IO::Socket::INET->new(Proto => 'udp',
PeerPort => PFTBLPORT,
PeerAddr => pfip)
or die "Creating socket: $!n";
#prepare struct for pftabled
my $command = '1';
my $iparray = param("ip");
#print @iparray;
my $addr = inet_aton($iparray);
my $time = time();
my $block = pack("C1 S1 C1",PFTBLVERSION,$command,PFTBLMASK).$addr.pack("a32 N*",PFTBLNAME,$time);
my $digest = hmac_sha1($block, $key);
$block .= $digest;
$pftabled->send($block);
print header();
my $smtp = Net::SMTP->new('mysmtpserver.mydomain.ru');
$smtp->mail('terminal_guard@mydomain.ru');
$smtp->to('account_admin@mydomain.ru');
$smtp->data();
$smtp->datasend("To: account_admin@mydomain.run");
$smtp->datasend("Subject: Terminal server logon detectedn");
$smtp->datasend("n");
$smtp->datasend("User ".param("user")." logged on from ".param("ip")."n");
$smtp->dataend();
$smtp->quit;
exit(0);
Теперь можно поправить конфигурацию pf.
external_addr="1.1.1.1"
terminal_server_addr="192.168.1.1"
table <RDP> persist { }
rdr on $ext_if proto tcp from <RDP> to $external_addr port 3389 -> $terminal_server_addr port 3389 # terminal
pass in on $ext_if proto tcp from <RDP> to { $external_addr, $terminal_server_addr } port { 3389 } flags S/SA keep state
При удачной аутентификации скрипту передаются имя зашедшего пользователя и IP адрес с которого осуществлялся запрос. Информация о IP адресе помещается в таблицу RDP и пользователь получает доступ к RDP подключению.
Включить windows аутентификацию на RDP сервере можно в файле C:WindowsWebRDWebPagesWeb.config
При желании можно задействовать custom errorpages и таким же образом блокировать адреса злоумышленников при нескольких неудачных попытках ввода логина пароля. Правда могут быть FP. Но без этого никуда не денешься.
© Aborche 2013
Автор: aborche