由于工作原因,偶尔需要在家里远程连接到公司计算机上处理一些工作事宜(犹记得2021年上海lockdown期间,远程办公了近2个月,就是以这种方式进行的😂)。因为公司的电脑是在一个局域网内的,并没有对外暴露IP地址,所以一般的做法就是通过第三方软件比如TeamView、向日葵这类软件来提供支持,但这些第三方软件经常会爆发一些安全事件,且把电脑交给第三方软件其实也存在安全隐患。
不使用第三方软件,通用的做法就是在路由器上将外网IP地址和特定的端口转发到特定内网IP地址的机器和端口上。这样通过外网IP地址和端口,就可以使用远程桌面的方式访问到内网的计算机,但在外网暴露特定端口是有风险的,尤其是对于远程桌面3389这样常用的端口( 虽然可以修改默认的3389端口,但作用不大),因为任何人都可以通过一些端口扫描工具进行连接,进而通过暴力的方式尝试登录(正规的做法是通过VPN的方式,首先登录一台堡垒机,然后再访问目标机器)。之前在记一次.NET程序内存暴涨分析这篇文章中就遇到过RDP攻击,今天又遇到了一个远程连接攻击的例子。
本文首先回顾了一次RDP攻击,并从Windows日志上查看RDP攻击留下来的信息,接下来通过新建防火墙入站规则,阻止特定的疑似攻击的IP地址对本机3389端口的访问。最后通过程序的方式自动监控这种RDP失败的登录,并在登录失败超过一定次数后,自动新建防火墙入站规则,并将疑似IP地址添加到规则中,从而防止来自这类IP地址的RDP攻击的进一步尝试。
缘由
为了防止登录密码被暴力破解,所以我留了一手,设置了登录失败一定次数之后,账户会锁定一段时间。这样,即使周末有人通过暴力破解也能拖到工作日来处理。设置锁定策略的步骤如下:
- 在命令行中运行gpedit.msc
- 在弹出的组策略里面,打开[计算机配置] - [Windows设置] - [安全设置] - [帐户策略] - [帐户锁定策略] 项,设置如下。
这个策略表示,如果5次登录失败,则锁定账户10分钟,然后等待5分钟之后重置登录失败计数。即5次错误后锁定15分钟。相当于一天最多的错误密码尝试次数为((24*60))/15)*5=480次。把登录密码长度设置长一点,这样用户名和密码这两项,要全部在480*2=960次之内尝试成功,概率是很小的,足以应付周末被攻击的情况。
果不其然,在某个周末我在家里想远程连接到公司电脑处理事情,结果远程连接失败弹出以下提示:
看到这个界面,就表示有人在尝试远程登录,因为用户名或密码错误超过次数后,电脑把账户锁定了,这种情况,只有等工作日上班解决。
蛛丝马迹
到公司后,第一件事就是把网线拔掉,然后重启计算机。然后查看Window相关日志信息,发现确实有很多RDP连接请求。比如在:"应用程序和服务日志"->"Microsoft"->"Windows"->"TerminalServices-RemoteConnectionManager"->"Operational"下面有很多“RDP-TCP”的连接尝试。
在“Windows日志”->"安全"下面,过滤所有表示为Logon的事件ID为4625的日志,注意,4625这个事件表示登录失败,它包含远程桌面登录失败和本地登录(127.0.0.1)失败:
上面这个日志里面记录了尝试登录但失败的日志信息,包括源网络地址,登录的时候用的用户名等信息。
由于此时人已经在电脑前面,所以我又重新连上网络,发现这种尝试的连接仍然存在,通过netstat也能看到,有些IP地址还非常顽固,会不断地尝试:
C:\Users\xxxxxx>netstat -ano | findstr "3389"
TCP 0.0.0.0:3389 0.0.0.0:0 LISTENING 15884
TCP 192.168.6.200:3389 80.234.104.149:51703 ESTABLISHED 15884
TCP 192.168.6.200:3389 80.234.104.149:52890 ESTABLISHED 15884
TCP 192.168.6.200:3389 80.234.104.149:58331 ESTABLISHED 15884
TCP [::]:3389 [::]:0 LISTENING 15884
UDP 0.0.0.0:3389 *:* 15884
UDP [::]:3389 *:* 15884
比如上面这个 80.234.104.149 ,这个IP地址会一直尝试连接,而且还是通过机器上不同的端口发起的连接请求:
当把远程桌面关掉之后,这一切又都回归平静。
解决方案
最简单的方法就是将netstat中查询到的那些可疑的IP地址添加到防火墙中,阻止这些IP地址到本机的连接。具体操作为“Windows Defender防火墙”->“高级设置”->“入站规则”下面新建规则:
▲ 规则类型选择端口
▲ RDP是通过TCP连接的,端口类型可以选择所有端口,也可以选择特定端口,这里选择特定的3389端口
▲ 这里选择阻止连接
▲ 给起个名字和描述
现在,这个规则因为没有添加任何需要阻止的IP地址,所以默认全部禁止访问3389端口。需要右键单击添加一个IP地址:
▲在“作用域”中,远程IP地址里,添加之前异常的IP地址
再次强调一点,因为创建规则是禁止访问端口,如果远程IP地址里面没有内容,则默认禁止所有的IP访问3389端口,这就不符合我们的要求了。所以这里面一定要添加一个IP地址,哪怕是虚拟的也可以。
更进一步
上面这个方法还是略显被动。如果能通过程序自动判断异常的登录,然后将这些异常的IP地址自动加入到防火墙里面就好了。幸运的是,Windows提供了操作防火墙设置的相关API,可以很方便的创建规则。现在就剩下如何判断异常的IP地址了,一种思路是根据上面的日志分析,只需要判断ID为4625的事件,统计这个事件出现的频次,达到一定阈值,就把这个事件里面的IP地址加入到防火墙里面。
恰好Github上BruteForceBuster这个项目就是这样实现的。它的逻辑也非常简单,主要就是分两步:
首先监听4625的事件:
public static void EventLogSubscription()
{
try
{
string eventQueryString = @"<QueryList>
<Query Id=""0"" Path=""Security"">
<Select Path=""Security"">*[System[(EventID=4625)]]</Select>
</Query>
</QueryList>";
EventLogQuery eventQuery = new EventLogQuery("Security", PathType.LogName, eventQueryString);
using EventLogWatcher watcher = new EventLogWatcher(eventQuery);
watcher.EventRecordWritten += Watcher_EventRecordWritten;
watcher.Enabled = true;
Console.ReadLine();
}
catch (EventLogReadingException e)
{
Console.WriteLine(e.Message);
}
Console.ReadLine();
}
这样,当有新的ID为4625事件发生时,就会回调Watcher_EventRecordWritten方法:
private static void Watcher_EventRecordWritten(object sender, EventRecordWrittenEventArgs e)
{
if (e.EventRecord != null)
{
string accountName = e.EventRecord.Properties[5].Value.ToString();
string ip = e.EventRecord.Properties[19].Value.ToString();
string port = e.EventRecord.Properties[20].Value.ToString();
string type = port == "0" ? "RDP" : $"SMB source port : {port}";
IPDict.TryGetValue(ip, out int count);
IPDict[ip] = ++count;
Console.WriteLine($"Account Name : {accountName}, Source Network Address : {ip}, Count : {count}, Time : {DateTime.Now}, Type : {type}");
if (count >= BlockThreshold)
BlockIP(ip);
}
}
在这个方法里,会提取IP地址,端口号,登录的用户名等一系列信息,这里要做的就是统计这个IP地址的出错频率,如果大于设定的阈值BlockThreshold,则执行第二步,将该IP地址添加到防火墙的规则中,禁止该IP地址的连接。BlockIP方法如下:
public static void BlockIP(params string[] remoteAddresses)
{
string ruleName = "Block3389";
Type typeFWPolicy2 = Type.GetTypeFromProgID("HNetCfg.FwPolicy2");
Type typeFWRule = Type.GetTypeFromProgID("HNetCfg.FwRule");
try
{
INetFwPolicy2 fwPolicy2 = (INetFwPolicy2)Activator.CreateInstance(typeFWPolicy2);
var rules = fwPolicy2.Rules.Cast<INetFwRule>();
var rule = rules.FirstOrDefault(x => x.Name == ruleName);
string newAddresses = string.Join(',', remoteAddresses);
if (rule is null)
{
INetFwRule newRule = (INetFwRule)Activator.CreateInstance(typeFWRule);
newRule.Name = ruleName;
newRule.Description = "Block inbound traffic over TCP port 3389";
newRule.Protocol = (int)NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP;
newRule.LocalPorts = "3389,445,135,139";
newRule.RemoteAddresses = newAddresses;
newRule.Direction = NET_FW_RULE_DIRECTION_.NET_FW_RULE_DIR_IN;
newRule.Enabled = true;
newRule.Grouping = "@firewallapi.dll,-23255";
newRule.Profiles = fwPolicy2.CurrentProfileTypes;
newRule.Action = NET_FW_ACTION_.NET_FW_ACTION_BLOCK;
fwPolicy2.Rules.Add(newRule);
}
else
{
rule.RemoteAddresses += $",{newAddresses}";
}
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"Blocked! IP : {newAddresses}");
Console.ResetColor();
}
catch (COMException ex)
{
Console.WriteLine(ex);
}
}
这段代码首先查找有没有名为“Block3389"的规则,如果没有,则新建该规则,该规则的Action是Block,端口是"3389,445,135,139",RemoteAddress就是日志里面提取的地址,需要注意的是因为Action是Block,所以这里的创建规则的时候,地址必须要填,否则就是禁止所有的远程IP地址连接了。如果之前新建有”Block3389“规则,则直接将IP地址添加到RemoteAddresses后面。
运行之后可以看到,非常完美的解决了这些恶意访问本机3389端口的IP地址。
如上图,可以看到“81.17.20.98”这个IP地址尝试RDP登录了3次,均失败了,第1次和第3次用的用户名是“ADMINISTRATOR”,第2次用的实“ADMIN”。程序里设置的是如果登录失败3次,这将这个IP地址添加到防火墙规则中。这里也可以给个提醒,就是我们最好禁用"ADMINISTRATOR"这样的管理员账户。
上面这个规则比较简单粗暴,只是按照出错的次数来限制,这里可以做的更复杂一些,比如使用类似熔断器之类的策略,限定允许错误的频次,设置屏蔽的时长等等,这里不展开,因为单纯的使用累计出错的次数已经满足要求。
在防火墙设置中,可以看到程序创建了一个名为“Block3389”的进站规则:
这个规则与我们之前手动创建的规则是一样的。
在“作用域”的远程IP地址中可以看到程序监测到的“81.17.20.98”这个异常的IP地址。
参考:
- https://help.aliyun.com/zh/ecs/support/for-safety-consideration-have-lock-the-user-account-the-reason-is-that-login-attempt-or-password-change-try-too-much
- https://blog.csdn.net/IDC02_Fang/article/details/131491012
- https://blog.csdn.net/xh_w20/article/details/134695906
- https://github.com/johnseed/BruteForceBuster/tree/master
- https://blog.csdn.net/qq_39065491/article/details/132986556
- https://blog.csdn.net/spidernet/article/details/136157997
非常实用!!这才是Windows应该预装的功能啊!一个IP反复输错RDP密码就立刻封禁。多基础的功能!你这个博客完美的解决了这个需求!
我改了端口。会消停一点时间。但过几天又会被暴力测试帐户。我就奇怪,端口他们是怎么知道的?