Python爬虫最新漏洞监控实现定时邮件推送和可视化

0x00前言

最近由于实验室有需求,要对漏洞进行及时监控并进行分析,于是就参考浮萍牛之前作品,并根据网上不同的源,进行漏洞监控脚本的书写,小试牛刀,涉及的过程有,先对漏洞源进行爬虫,然后进行关键词提取,进行数据库存储,并进行可视化,实现邮件定时推送等等。

0x01代码结构

  • 首先对收集的源进行爬虫,这里暂时只提取了四个源(CVE,exploit_db,hacker_news,theat_book)
  • 然后爬虫对获取到的数据进行关键字检索处理,将检索的数据存入数据库
  • 爬虫同时生成部分csv文件在output文件夹中,方便后边进行数据分析进行简单可视化
  • 然后利用echarts或者matplotlib对数据进行画图处理
  • 最后邮件推送,将爬到数据和图片作为附件进行定时邮件推送

0x02效果如下

0x03实现过程

爬虫提取

首先是对几个源进行爬虫,这里只用了BeautifulSoup来进行html标签的提取等等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
def get_exploit_db_info():
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0',
'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language':'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
'Cookie':'_ga=GA1.3.151879025.1520582790; _gid=GA1.3.1728776549.1523330453; PHPSESSID=niirk5p8e6glodfoqphqteud73; _gat=1'
}
url = 'https://www.exploit-db.com/'
res = requests.get(url=url,headers=headers,timeout=60)
#print(res.text)
soup = bs(res.text,'html.parser')
trs = soup.find_all('tr',{'class':'featured'})
total_exploit_db =len(trs)
#print(trs)
select_msg = ''
keywords = config.keywords
wordlist = []
try:
for tr in trs:
description = tr.find_all('td')[4].find('a').string
#print(description)
date = tr.find_all('td')[0].string
platform = tr.find_all('td')[5].find('a').string
#print(platform)
site = tr.find_all('td')[4].find('a')['href']
for k in keywords:
if k in description:
keyword = k
wordlist.append(keyword)
data = {'date':date,'description':description,'platform':platform}
storage_to_db.storage_data(data)
select_msg += '<p><b>发布日期:'+date+'</p></b>'+'<br><b>漏洞描述:</b>'+'<a href ="'+site\
+'">'+description+ '</a></br>'
except Exception as e:
pass

if len(trs) == 0:
msg = nowtime + '<p>今日Exploit_db风和日丽,没有大事发生。</p>'
dataframe = pd.DataFrame({'exploit_db告警总数':[0],'exploit_db检索数':[0]})
dataframe.to_csv(os.path.join(config.output_path, 'exploit_db_data.csv'),encoding="gb2312")
return msg
else:
msg_header = '<p>今日Exploit_db一共<font size="3" color="red">' + str(len(trs))+'</font>条。'
if len(wordlist) == 0:
key_msg = '根据设置的关键字,未匹配到关注的Exploit_db信息。</p>'
msg = nowtime + msg_header + key_msg
dataframe = pd.DataFrame({'exploit_db告警总数':[len(trs)],'exploit_db检索数':[0]})
dataframe.to_csv(os.path.join(config.output_path, 'exploit_db_data.csv'),encoding="gb2312")
return msg
else:
key_msg = '</p>根据设置的关键字,关注的Exploit_db信息一共<font size="3" color="red">' + str(len(wordlist))+'</font>个。具体如下:<br><br>'
msg = nowtime + key_msg + select_msg
dataframe = pd.DataFrame({'exploit_db告警总数':[len(trs)],'exploit_db检索数':[len(wordlist)]})
dataframe.to_csv(os.path.join(config.output_path, 'exploit_db_data.csv'),encoding="gb2312")
return msg

数据库存储

其实脚本里对存储的数据并没有进行调用,不存储亦可,方便后期统计,起初用过mysql存储,但是对于每个源来说,爬取的字段数并不一致,用mysql存储的话就需要用不同的表,相对麻烦。于是参照一些大牛的套路,就用MongoDB进行存储,用了之后感觉挺好的,同时还搭配了一款MongoDB数据库查询软件,GUI棒棒哒

这里参考浮萍牛的MongoDB配置

1
2
3
4
5
mkdir /var/data/ #创建数据存储位置
#启动mongodb,指定端口和路径,且仅本机可连
mongod --port 65521 --dbpath /var/data/ --bind_ip 127.0.0.1
mongo 127.0.0.1:65521/mydb
db.createUser({user:'tass',pwd:'liehu',roles:[{role:'dbOwner',db:'mydb'}]}) #添加认证

另外MongoDB在存储时,可以用insert,也可以用update进行更新,不存在就会插入,存在就会更新

1
c.update({"description": data['description']}, {'$set': data}, True)

如果存在相同的描述,则会进行更新

数据读取可视化

这里呢本来是想回调参数进行绘图简单可视化,发现比较冗余,于是进行csv文件的保存,而且打算后期方便统计,然后利用这些数据进行月度绘图等等,可能以后会添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
df_anquanke = pd.read_csv('./output/An_quan_ke_data.csv',encoding="gb2312")
total_anquanke = df_anquanke.loc[0].iloc[1]
key_anquanke = df_anquanke.loc[0].iloc[2]

total_vulns = [total_CVE,total_exploit_db,total_hacker_news,total_threat_book,total_anquanke]
key_vulns = [key_CVE,key_exploit_db,key_hacker_news,key_threat_book,key_anquanke]
# print(total_vulns,key_vulns)

ind = np.arange(size)
total_width = 0.9
width = total_width / size

fig,ax = plt.subplots()
rects1 = ax.bar(ind, total_vulns, width, color='g')
rects2 = ax.bar(ind + width, key_vulns, width, color='r')

ax.set_ylabel('漏洞告警数')
ax.set_title('今日漏洞告警分析')
ax.set_xticks(ind + width)
ax.set_xticklabels(('CVE', 'Exploit_db','Hacker_news','Thread_book','Anquanke'))

ax.legend((rects1[0], rects2[0]), ('告警总数', '检索数'))
plt.savefig(os.path.join(config.output_path, 'inspect.png'))
# plt.show()

读取完数据之后,就打算绘图,起初考虑过用echart并且上面也绘制了,但是在邮件推送时,echart可交互式图表不能展示,不能插入js代码执行。但是有个idea是可以实现的,就是在公网vps上运行脚本,然后搭建web,生成文件在web目录下,然后邮件超链过去,如果钟爱echart可以这么做

在制图过程中,考虑过用堆叠柱状图,并列柱状图,包括饼图等等,最终还要用了简单的并列柱状图
echart的交互性非常好,如果在html页面展示 最佳选择

邮件推送

要进行邮件推送,就找到了python的库smtplib
可以满足既能发送html的爬虫数据,还能附件格式发送生成的可视化图表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def send_email_with_pic(mail_msg):
sender = 'xxx@xx.com' # 发件人
password = 'xxx' # 发件人密码
receiver = 'xxx@qq.com' # 收件人
message = MIMEMultipart('alternative')
message['From'] = sender
message['To'] = receiver
subject = '最新漏洞和CVE告警信息'
message['Subject'] = Header(subject, 'utf-8')
message.attach(MIMEText(mail_msg, 'html', 'utf-8'))
with open('./output/inspect.png','rb') as f:
mm = MIMEBase('image', 'png', filename='inspect.png')
mm.add_header('Content-Disposition', 'attachment', filename='inspect.png')
mm.add_header('Content-ID', '<0>')
mm.add_header('X-Attachment-Id', '0')
mm.set_payload(f.read())
encoders.encode_base64(mm)
message.attach(mm)

try:
smtpObj = smtplib.SMTP('smtp.163.com')
smtpObj.login(sender, password)
smtpObj.sendmail(sender, receiver, message.as_string())
print('邮件发送成功')
except smtplib.SMTPException:
print('Error: 无法发送邮件')

再尝试了这个库之后,我突然萌生了短信炸弹的念头,我怎么走哪都有重放漏洞的影子呢,着魔了

不信就看看我的邮箱,一天天把自己炸坏了,不断测试

定时发送

这里定时发送利用到了Linux的定时命令crontab来实现

通过在vps上可以定时让其在指定时刻运行脚本,不就能定时推送各种漏洞预警,然后进行及时分析,拿到第一手漏洞信息,岂不是美滋滋

看完这个图,应该对这个定时命令的参数就非常了解了,运行完就等着收邮件好了

0x04写在最后

在写脚本的时候也会遇到各种坑,各种成长,没有办法,谁还不经历点挫折,谁还不遇上几个渣男,不对跑题了,哈哈O(∩_∩)O,代码结构可能很糙,大牛勿喷,么么哒,后期还会更新源,优化结构,希望能做到更好,就这样,下次见咯。

代码地址

代码地址:https://github.com/jwx0539/InspectVulns

-------------本文结束感谢您的阅读-------------
坚持原创技术分享,您的支持将鼓励我继续创作!