一,项目背景;
暴露在公网云上的服务器,会存在大量的暴力破解;
解决办法:
- 更换端口;
- 买云防护,waf
暴力破解的自动阻断;
使用python中的re模块和subprocess模块
二,使用到python中的模块
1,re模块
re模块是Python中用于处理正则表达式的模块。正则表达式是一种用于匹配字符串模式的强大工具,可以用于搜索、替换和分割字符串。
re模块提供了一系列函数,可以用来对字符串进行正则表达式的操作,包括匹配、搜索、替换等功能。一些常用的函数包括:
1. re.match(pattern, string):尝试从字符串的起始位置匹配一个模式,如果匹配成功返回一个匹配对象,否则返回None。
2. re.search(pattern, string):在字符串中搜索匹配模式的第一个位置,如果匹配成功返回一个匹配对象,否则返回None。
3. re.findall(pattern, string):在字符串中搜索匹配模式的所有位置,并返回所有匹配的字符串组成的列表。
4. re.sub(pattern, repl, string):用指定的替换字符串替换字符串中所有匹配模式的子串。
5. re.split(pattern, string):根据匹配模式分割字符串,并返回分割后的子串列表。
使用re模块需要先导入re模块,例如:import re。
然后可以通过re.compile()函数编译正则表达式模式,然后使用上述函数进行字符串操作。
(1),findall() 函数
找到匹配到的所有子串,并返回一个列表,如果没有匹配到就返回空;
>>> import re
>>> s = 'hello 520'
>>> re.findall('hello',s)
['hello']
>>> re.findall('520',s)
['520']
>>> re.findall('l',s)
['l', 'l']
(2),search() 函数
扫描整个字符串,并且返回第一个,如果没有成功匹配就返回空;
>>> re.search('hello',s)
<re.Match object; span=(0, 5), match='hello'>
>>> m = re.search('hello',s)
>>> m.group()
'hello'
>>> m.span()
(0, 5)
>>> n = re.search('l',s)
>>> n.group()
'l'
(3),匹配元字符
\d 匹配所有数字0-9
\D 匹配非数字
\w 匹配所有单词字符,包括大小写 数字 下划线 中文
\W 匹配剩下的,空格 换行符 特殊字符
(4),匹配字符集
字符的集合,用[]表示
字符集内用“^”表示“非”
\d=[0-9]
\D=[^0-9]
\w!=[a-zA-Z_0-9] 还有中文
(5),匹配标定的字符数量
{}内部表示长度
import re
s = 'https www baidu com'
re.findall('[a-z]{3}',s)
['htt', 'www', 'bai', 'com']
2,subprocess模块
需求:执行命令并且将输出劫持实现日志的监控;
用于启动新的进程的模块,它可以用于执行外部命令,获取进程的输出,多进程的协同;
多进程协同在python中有三种方式:
- os.system函数;阻塞式
- multiprocessing模块;密集型的计算
- subprocess模块
subprocess模块是Python中用于创建和管理子进程的模块。通过subprocess模块,可以在Python程序中启动新的进程,与新的进程进行交互,并获取新进程的输出。
subprocess模块提供了多个函数,用于执行外部命令,例如:
(1),subprocess.Popen
subprocess.Popen(args, bufsize=-1, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0, restore_signals=True, start_new_session=False, pass_fds=(), *, encoding=None, errors=None)
启动一个新的进程,并返回一个Popen对象,可以通过该对象与新进程进行交互。
(2),subprocess.run
subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False)
运行指定的命令,等待命令完成,并返回一个CompletedProcess对象。
(3),subprocess.check_output
subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False)
运行指定的命令,并返回命令的输出结果。
(4),subprocess.call
subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False)
运行指定的命令,等待命令完成,并返回命令的返回值。
使用subprocess模块可以执行系统命令、调用外部程序,并获取输出结果。需要注意的是,在执行外部命令时,应该谨慎处理输入参数,以防止命令注入等安全问题。
三,Linux的日志
1,常见日志的目录
- /var/log------日志任务
- /var/log/cron------记录系统定时任务
- /var/log/cups------打印信息的日志
- /var/log/message------记录系统重要信息的日志
- /var/log/btmp------登录失败
- /var/log/lastlog------最后一次登录
- /var/log/wtmp------登录成功的日志
- /var/log/secure------登录日志
- /var/log/utmp------目前登录用户的信息
- /etc/hosts.deny------hosts黑名单
- /etc/hosts.allow------hosts白名单
2,日志分析
常用到的Linux命令;
- find
- grep
- awk
- sed
- cat
- tail
- head
3,黑白名单的设置
/etc/hosts.deny------hosts黑名单
/etc/hosts.allow------hosts白名单
配置的格式:
服务:地址:允许/封禁
服务:ssh,ftp,telnet,http,https
如:all:192.168.xxx.xxx:deny 全部封禁
all:192.168.xxx.xxx:allow 白名单
地址:
192.168.1.10
192.168.1.10/24 或者 192.168.1.* 封掉整个C段
四,逻辑结构
实现暴力破解的自动阻断;
步骤:
- 第一步:打开安全日志;
- 第二步:对安全日志进行实时的监控;
- 第三步:解析日志的内容,找出正在爆破的IP;
- 第四步:设置一个阀值,超过阀值之后直接对IP进行封禁;
五,项目实现
源代码:
import re
import subprocess
import time
# 打开安全日志
logFile = '/var/log/secure'
# 设置黑名单
hostDeny = '/etc/hosts.deny'
# 封禁阀值
password_wrong_num = 5
# 获取已经加入黑名单的IP,转换为字典
def getDenies():
deniedDict = {}
list = open(hostDeny).readlines()
for ip in list:
group = re.search(r'(\d+\.\d+\.\d+\.\d+)',ip)
if group:
deniedDict[group[1]] = '1'
return deniedDict
# 监控方法
def monitorLog(Logfile):
# 统计错误的次数
tempIP = {}
# 已经拉黑的IP
deniedDict = getDenies()
# 读取安全日志
popen = subprocess.Popen('tail -f'+logFile,stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True)
# 开始监控
while True:
time.sleep(0.1)
line = popen.stdout.readline().strip()
if line:
group = re.search('Invalid user \w+ from (\d+\.\d+\.\d+\.\d+)',str(line))
# 用户不存在直接封
if group and not deniedDict.get(group[1]):
subprocess.getoutput('echo \'sshd:{} >> {}'.format(group[1]),hostDeny)
deniedDict[group[1]] = '1'
time_str = time.strftime('%Y-%m-%d %H:%M:%s',time.localtime(time.time()))
print('{} ---add ip :{} to hosts.deny for invalid user'.format(time_str,group[1]))
continue
# 用户名存在,密码错误
group = re.search('Failed password for \w+ from(\d+\.\d+\.\d+\.\d+)',str(line))
if group:
ip = group[1]
# 统计IP错误的次数
if not tempIP.get(ip):
tempIP[ip] = 1
else:
tempIP[ip] = tempIP[ip]+1
# 如果错误次数大于阀值的时候,直接封禁
if tempIP[ip] > password_wrong_num and not deniedDict.get(ip):
del tempIP[ip]
subprocess.getoutput('echo \'sshd:{} >> {}'.format(ip,hostDeny))
deniedDict[ip] = '1'
time_str = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))
print('{}---add ip :{} to hosts.deny for invalid password'.format(time_str,ip))
if __name__ == '__main__':
monitorLog(logFile)