Extra Systems Ban Software (ESBANS)

ES-RDP

Модуль rdp-log

Модуль rdp-log нашей оригинальной системы защиты удаленного рабочего стола Windows состоит из трех файлов - двух основных скриптов для PowerShell rdp-log.ps1 и для PHP rdp-log.php (которые последовательно выполняют всю необходимую работу по вторичному выявлению и блокировке хакеров-рецидивистов и блоков вредоносных адресов) и дополнительного файла rdp-log.bat (который вызывается из Windows Task Scheduler каждый час и при этом запускает на выполнение основные файлы rdp-log.ps1 и rdp-log.php).

Файл rdp-log.bat имеет в нашей системе такой вид:

powershell.exe -File "C:\Scripts\rdp_log.ps1" 2>nul
"C:\Program Files (x86)\PHP\php.exe" C:\Scripts\rdp_log.php 2>nul

Само собой понятно, что вы у себя должны здесь будете заменить "C:\Scripts" на тот адрес, по которому у вас расположена копия нашей системы, а также, в случае необходимости, подправить путь к файлу php.exe. Главный файл этого модуля rdp-log.ps1 имеет следующий вид:

# Подключаем общие переменные (пути к mysql, логин, пароль и т.д.)
. "$PSScriptRoot\common.ps1"

# 1. ОЧИСТКА СТАРЬЯ
foreach ($Key in $BanTypes.Keys) {
    # 1. Настройка переменных для текущего типа
    $FirewallPrefix = $FirewallPrefixes[$Key]
    $BanType        = $BanTypes[$Key]
    $BanTime        = $BanTimes[$Key]
    $ExpirationDate = (Get-Date).AddDays(-$BanTime)

    # 2. Поиск всех правил этого типа
    $AllRules = Get-NetFirewallRule -DisplayName "$FirewallPrefix*" -ErrorAction SilentlyContinue

    # 3. Отбор и удаление старых правил
    $OldRules = $AllRules | Where-Object {
        if ($_.DisplayName -match "(\d{4}-\d{2}-\d{2})") {
            [DateTime]$Matches[1] -lt $ExpirationDate
        } else { $false }
    }

    if ($OldRules) {
        $OldRules | Remove-NetFirewallRule
    }

}

# чистим логи
if ((Get-Date).Hour -lt 3) {
	$db_max_days_size = 7
	$Query = "delete from ban_stats where ban_date < CURDATE() - INTERVAL $db_max_days_size DAY;"
	& $mysql_path --user=$mysql_user --password=$mysql_password --database=$mysql_dbName --execute="$Query" 2>$null

	$db_max_days_size = 48
	$Query = "delete from days_data where ban_date < CURDATE() - INTERVAL $db_max_days_size DAY;"
	& $mysql_path --user=$mysql_user --password=$mysql_password --database=$mysql_dbName --execute="$Query" 2>$null

	$db_max_days_size = $LongSlice
	if ($NetSlice -gt $db_max_days_size) { $db_max_days_size = $NetSlice }
	$db_max_days_size = $db_max_days_size * 2
	$Query = "delete from ban_log where ban_time < CURDATE() - INTERVAL $db_max_days_size DAY;"
	& $mysql_path --user=$mysql_user --password=$mysql_password --database=$mysql_dbName --execute="$Query" 2>$null

	$db_max_days_size = $BotSlice * 2
	$Query = "delete from net_log where ban_time < CURDATE() - INTERVAL $db_max_days_size DAY;"
	& $mysql_path --user=$mysql_user --password=$mysql_password --database=$mysql_dbName --execute="$Query" 2>$null
}

# документируем количество банов каждой категории
foreach ($Key in $BanTypes.Keys) {

    # 1. Получаем префикс и BanType для текущей итерации
    $CurrentPrefix = $FirewallPrefixes[$Key]
    $BanType       = $BanTypes[$Key]

    # 2. Считаем количество правил для конкретного типа
    $RulesCount = @(Get-NetFirewallRule -DisplayName "$CurrentPrefix*" -ErrorAction SilentlyContinue).Count

    # 3. Формируем SQL-запрос
    $Query = "INSERT INTO ban_stats (ban_count, ban_type) VALUES ($RulesCount, $BanType);"

    # 4. Выполняем
    & $mysql_path --user=$mysql_user --password=$mysql_password --database=$mysql_dbName --execute="$Query" 2>$null
    
}

# на основе первичных банов формируем баны следующих уровней

$CurDate = Get-Date -Format "yyyy-MM-dd"

# Запрос на рецидивистов
$LongQuery = "SELECT INET_NTOA(ban_addr), COUNT(*) as cnt FROM ban_log WHERE ban_time > NOW() - INTERVAL $LongSlice DAY GROUP BY ban_addr HAVING cnt >= $LongLimit;"
$Recidivists = @(& $mysql_path --user=$mysql_user --password=$mysql_password --database=$mysql_dbName --execute="$LongQuery" --skip-column-names)
$FirewallPrefix = $FirewallPrefixes['long']
foreach ($Row in $Recidivists) {
	$Columns = $Row -split "\t"
	$IP = $Columns[0].Trim()
	$TechnicalName = $FirewallPrefix + $IP.Replace('.', '_')
        if (-not (Get-NetFirewallRule -Name $TechnicalName -ErrorAction SilentlyContinue)) {
		$Description = "Type: recidive. Created: $(Get-Date)"
		New-NetFirewallRule -Name $TechnicalName -DisplayName "$FirewallPrefix$CurDate`_$IP" -Direction Inbound -Action Block -RemoteAddress $IP -Description $Description | Out-Null
	}
}

# Запрос на поиск враждебных сетей
$NetQuery = "SELECT INET_NTOA(ban_subnet), COUNT(DISTINCT ban_addr) as unique_ips FROM ban_log WHERE ban_time > NOW() - INTERVAL $NetSlice DAY GROUP BY ban_subnet HAVING unique_ips >= $NetLimit;"
$HostileNets = @(& $mysql_path --user=$mysql_user --password=$mysql_password --database=$mysql_dbName --execute="$NetQuery" --skip-column-names)
$FirewallPrefix = $FirewallPrefixes['net']
foreach ($Row in $HostileNets) {
	$Subnet = ($Row -split "\t")[0].Trim()
	$TechnicalName = $FirewallPrefix + $Subnet.Replace('.', '_')
        if (-not (Get-NetFirewallRule -Name $TechnicalName -ErrorAction SilentlyContinue)) {
		$Description = "Type: botnet. Created: $(Get-Date)"
		$FullSubnet = "$Subnet/24"
		New-NetFirewallRule -Name $TechnicalName -DisplayName "$FirewallPrefix$CurDate`_$Subnet" -Direction Inbound -Action Block -RemoteAddress $FullSubnet -Description $Description | Out-Null

		# фиксируем событие в логе для поиска рецидивов
		$LogQuery = "INSERT INTO net_log (net_addr) VALUES (INET_ATON('$Subnet'));"
		& $mysql_path --user=$mysql_user --password=$mysql_password --database=$mysql_dbName --execute="$LogQuery" 2>$null
	}
}

# Запрос на поиск сетей-рецидивистов (тех, что постоянно попадают в net_log)
$BotQuery = "SELECT INET_NTOA(net_addr), COUNT(*) as cnt FROM net_log WHERE ban_time > NOW() - INTERVAL $BotSlice DAY GROUP BY net_addr HAVING cnt >= $BotLimit;"
$BotNets = @(& $mysql_path --user=$mysql_user --password=$mysql_password --database=$mysql_dbName --execute="$BotQuery" --skip-column-names)
$FirewallPrefix = $FirewallPrefixes['bot']
foreach ($Row in $BotNets) {
    $Columns = $Row -split "\t"
    $Subnet = $Columns[0].Trim()
    $TechnicalName = $FirewallPrefix + $Subnet.Replace('.', '_')
    if (-not (Get-NetFirewallRule -Name $TechnicalName -ErrorAction SilentlyContinue)) {
        $Description = "Type: hell. Created: $(Get-Date)"
        $FullSubnet = "$Subnet/24"
        New-NetFirewallRule -Name $TechnicalName -DisplayName "$FirewallPrefix$CurDate`_$Subnet" -Direction Inbound -Action Block -RemoteAddress $FullSubnet -Description $Description | Out-Null
    }
}

Как видно из этого кода, данный скрипт использует общие настройки системы из файла common.ps1, а также документирует свою деятельность в таблице ban_log (это необходимо для работы других модулей, задачей которых является вторичный бан - рецидивисты, блоки адресов и т.п.).

Файл rdp-log.php имеет в нашей системе такой вид:

<?php
	$dbUser     = "*******";
	$dbPassword = "*******";
	$dbName     = "rdp_ban";
	$conn = mysqli_connect("localhost","$dbUser","$dbPassword", "$dbName");
	$mysql_datetime = date("Y-m-d");
	$ban_types['short'] = 1;
	$ban_types['long']  = 2;
	$ban_types['net']   = 3;
	$ban_types['bot']   = 4;
	foreach ($ban_types as $ban_type)
	{
		$step_result = mysqli_query($conn, "select AVG(ban_count) as ban_avg from ban_stats where ban_type = $ban_type and ban_date > '$mysql_datetime'");
		$step_row = mysqli_fetch_assoc($step_result);
		$ban_avg = intval($step_row['ban_avg']);
		$step_result = mysqli_query($conn, "select count(*) as data_count from days_data where ban_type = $ban_type and ban_date='$mysql_datetime'");
		$step_row = mysqli_fetch_assoc($step_result);
		$data_count = $step_row['data_count'];
		if ($data_count == 0)
		{
			mysqli_query($conn, "insert into days_data(ban_date, ban_count, ban_type) values('$mysql_datetime', $ban_avg, $ban_type)");
		} else {
			mysqli_query($conn, "update days_data set ban_count=$ban_avg where ban_type = $ban_type and ban_date='$mysql_datetime'");
		}
	}
?>

Естественно, в начале этого файла, вместо звездочек, вы должны поставить свой логин и пароль для доступа к базе данных.


© Extra Systems, 2026 Extra Web Top