0x01 前言

前几天在先知上看到伪全栈式安全研发:CVE监控这篇文章,就想着也实现一下代码进行最新CVE的监控。语言采用了Python,数据库也为Mongodb数据库。代码和实现的什么不重要,重要的是过程。

主要包括以下几个方面。

  1. 获取最新的CVE列表和详情
    主要采用了python的requests模块和BeautifulSoup模块。
  2. 将最新的CVE信息存入数据库
    数据库使用了Mongodb,采用了pymongo模块。
  3. 通过邮件发送最新的CVE信息
    发送邮件采用了smtplib模块。
  4. 定时执行任务
    使用了linux的crontab来实现。

0x02 实现过程

1. 获取最新的CVE列表和详情

访问https://cassandra.cerias.purdue.edu/CVE_changes/today.html ,可以获取每天新增的CVE信息。

接口1

接口源代码
通过查看源代码,发现没html没什么规律可言,都是些超链接。要想获取最新的列表,可以通过取文本中间的方法来获取。
这里需要获取New entries:Graduations之间的内容。然后通过BeautifulSoup来解析其中的超链接。
主要代码如下:

1
2
3
4
5
6
7
8
9
10
11
def getCVES():# 获取最新到CVE列表
try:
url = 'https://cassandra.cerias.purdue.edu/CVE_changes/today.html'
res = requests.get(url, headers=headers, timeout=60)
CVEList_html = getMiddleStr(res.text, 'New entries:', 'Graduations')
soup = BeautifulSoup(CVEList_html, 'html.parser')
for a in soup.find_all('a'):
print(a['href'])
print(a.string)
except Exception as e:
print(e)

获取文本中间内容的代码:

1
2
3
4
5
6
def getMiddleStr(content, startStr, endStr): # 获取文本中间内容
startIndex = content.index(startStr)
if startIndex >= 0:
startIndex += len(startStr)
endIndex = content.index(endStr)
return content[startIndex:endIndex]

运行效果:
获取cve列表
超链接的地址是CVE的详情。随便进入一个查看效果。
例如:http://cve.mitre.org/cgi-bin/cvename.cgi?name=2017-0874
CVE详细信息
这里需要记录的信息有:CVE-ID、Description、Assigning CNA和Date Entry Created。

CVE详情网页源代码
通过查看网页源码发现,所有需要记录的信息在一个表格里面。但该页面有很多table,而且没有明显的标识来区分。而该table在div中,可以通过id来获取。
CVE-ID可以直接通过soup.find(nowrap='nowrap').find('h2').string获取。其他的几个信息可以通过获取相应tr中的td中的内容获得。
获取CVE详情
这样就可以获取最新的CVE列表和详情。

2. 将最新的CVE信息存入数据库

数据库采用了Mongodb。安装方法apt-get install mongodb
然后启动数据库

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

Mongodb数据库插入一条数据,一般使用的是insert。

1
db.test.insert({"title":"test1", "blog_cont":"test1"})

如果我们想实现一个如果title存在,就对数据进行更新,不存在,就插入。可以这样来实现。

1
2
3
db.test.update({"title":"test2"}, {$set:{"title":"test2", "blog_cont":"test2"}}, {upsert:true})
db.test.update({"title":"test1"}, {$set:{"title":"test1", "blog_cont":"test3"}}, {upsert:true})
db.test.find()

执行完成后最终有两条数据,title分别为test1和test2,对应的内容为test3和test2.

存在更新,不存在插入
因此在插入数据的时候,我们可以直接使用db.test.update({"title":"test2"}, {$set:{"title":"test2", "blog_cont":"test2"}}, {upsert:true})这种方式来实现。

插入测试
更新只需更改data内容即可。
更新测试

为了数据库的安全性,使用--bind_ip 127.0.0.1来设置数据库仅本地可以连接。更多mongodb数据库的配置可以参考MongoDB Mongodb.conf 配置 Auth

3. 通过邮件发送最新的CVE信息

发送邮件这里用到了smtplib。
发送邮件比较简单,就直接贴代码了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def sendEmail(mail_msg):  # 发送邮件
sender = '[email protected]' # 发件人
password = 'password' # 发件人密码
receiver = '[email protected]' # 收件人
message = MIMEText(mail_msg, 'plain', 'utf-8') #以文本发送
message['From'] = sender
message['To'] = receiver

subject = '最新CVE列表'
message['Subject'] = Header(subject, 'utf-8')

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

4. 定时执行任务

直接使用linux下的crontab来完成。
例如设置每天早上7点执行,可以这样设置:

1
0 7 * * * python /myJob/CVE-Monitor.py >> /log/CVE-Monitor.log

根据https://cassandra.cerias.purdue.edu/CVE_changes/ 看到today.html更新的时间是明天的06:53,对应北京时间是19:53。若想及时获取,可以更换时间为20:00.

5.完善和优化

到这里监控脚本完成的差不多了,剩下就是如何来融合一起并改善了。
为了方便发送邮件内容和插入数据库,我们新建类CVEInfo。主要代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class CVEInfo:
def __init__(self,url, cveid, description, company, createdate):
self.url = url
self.cveid = cveid
self.description = description
self.company = company
self.createdate = createdate

def show(self):
return '<p><b>漏洞编号:</b><a href="'+self.url+'">'+self.cveid+'</a></p><b>相关厂商:</b>'\
+self.company +'<br><b>披露日期:</b>'\
+self.createdate+'<br><b>漏洞描述:</b>'\
+self.description + '<br><br><hr/>'

def add(self):
data = {
'cveid': self.cveid,
'description': self.description,
'company': self.company,
'createdate': datetime.strptime(self.createdate, "%Y%m%d"),
'addDate': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())),

}
return data

为了美观,将邮件以html方式发送

1
message = MIMEText(mail_msg, 'html', 'utf-8')

邮箱收到的效果:
邮件内容
查看数据库数据:
数据库内容

从上面两张图片可以看到有三十多个,但我们有时候并不是都需要看。我们可以根据Description中关键信息来进行过滤,仅仅将我们需要关注的CVE信息发送到邮箱或进行入库操作。
如下图为获取CVE-2017-8295的信息。
添加关键字匹配
然后修改main方法,根据是否有关注的CVE信息来决定邮件的内容。
这里先用本地服务器为例,新建today.html文件,其中包含CVE-2017-9805CVE-2017-16241
关键字匹配
运行代码结果打印了一条包含了我们的关键字的数据。
邮件中的内容如下所示:
关键字匹配邮件内容
这样就能过滤其他CVE信息,仅仅记录我们关注的内容了。

0x03 总结

本文主要用到了BeautifulSoup解析网页和mongodb数据库的使用,然后就可以将想要的内容保存到数据库中。脚本并不限于在此处使用,也可以修改一下抓取其他网站内容。
代码地址:https://github.com/fupinglee/MyPython/blob/master/work/CVE-Monitor.py
查询的功能就不做了,若想实现其他功能,可以自行增加和修改。

0x03 参考

[1]https://xianzhi.aliyun.com/forum/topic/1694/
[2]http://blog.csdn.net/guoxingege/article/details/47339885