【物联网识别】cve_details漏洞爬虫 && 设备指纹库爬虫

设计要求
设计并制作出一种漏洞扫描平台,其要求如下:

( 1 )熟悉爬虫,爬取漏洞、设备详细数据,构建漏洞库、设备指纹库。

( 2 )使用 nmap 工具扫描网络,使用 zgrab2 工具辅助扫描网络。

( 3 )完成设备指纹识别,漏洞匹配过程。

( 4 )使用 nessus 工具验证漏洞。

( 5 )搭建可视化平台。

( 6 )完成设计报告


爬虫:

本项目中使用 python 语言对 CVE_Details、CNVD、 CNNVD、securityfocus、 ics_cnvd 等漏洞网站爬取不少于 100000 条漏洞详细信息,爬取“CVE 编号”、
“危害等级”、“漏洞类型”、“供应商”、“型号”、“设备类型”、“固件版本号”等
信息,构建 CVE 漏洞-设备信息映射库。

同时,使用 python 语言对京东、亚马逊、淘宝等电商网站爬取不少于 100000
条设备详细信息,爬取“设备类型”、“设备品牌”、“设备型号”等信息,并将其
构建一个设备指纹库。

本节小目录 (本文为作者踩坑记录,先看别直接上手,代码在最后)

scrapy框架介绍

CVE_Details爬虫代码

CVE漏洞信息数据库

setting设置

设备信息爬虫(苏宁)

设备信息爬虫(京东)

我只要代码!(好嘞,哥)


scrapy框架

Scrapy是一个快速的高级Web爬网Web爬框架,用于对网站进行爬网并从其页面中提取结构化数据。

在这里插入图片描述

Scrapy Engine(引擎): 负责Spider、ItemPipeline、 Downloader、Scheduler中间的通讯, 信号、数据传递等,
Scheduler(调度器): 负责接受引擎发送过来的Request请求,并按照一定的方式进行整理排列, 入队,当引擎需要时,交还给引擎,
Downloader (下载器): 负责下载Scrapy Engine(引擎发送的所有Requests请求,并将其获取到的Responses交还给Scrapy Engine(引擎),由引擎交给Spider来处理,
Spider (爬虫) : 它负责处理所有Responses,从中分析提取数据,获取ltem字段需要的数据,并将需要跟进的URL提交给引擎,再次进入Scheduler(调度器),
Item Pipeline(管道): 负责处理Spider中获取到的Item,并进行进行后期处理(详细分析、过滤存储等)的地方.
Downloader Middlewares (下载中间件) : 你可以当作是一个可以自定义扩展下载功能的组件。
Spider Middlewares (Spider中间件) : 你可以理解为是一个可以自定扩展和操作引擎和Spider中间通信的功能组件(比如进入Spider的Responses;和从Spider出去的Requests)

运作流程:

1、从spider中获取到初始url给引擎,告诉引擎帮我给调度器;

2、引擎将初始url给调度器,调度器安排入队列;

3、调度器告诉引擎已经安排好,并把url给引擎,告诉引擎,给下载器进行下载;

4、引擎将url给下载器,下载器下载页面源码;

5、下载器告诉引擎已经下载好了,并把页面源码response给到引擎;

6、引擎拿着response给到spider,spider解析数据、提取数据;

7、spider将提取到的数据给到引擎,告诉引擎,帮我把新的url给到调度器入队列,把信息给到Item Pipelines进行保存;

8、Item Pipelines将提取到的数据保存,保存好后告诉引擎,可以进行下一个url的提取了;

9、循环3-8步,直到调度器中没有url,关闭网站(若url下载失败了,会返回重新下载)。

安装scrapy

直接用anaconda或者pip命令安装,推荐装入venv虚拟环境

conda install scrapy 或 pip install scrapy

可以在命令行直接输入scrapy验证是否安装成功

在这里插入图片描述

创建项目

scrapy startproject 项目名

就会创建以下目录结构的项目文件夹

tutorial/
    scrapy.cfg            # deploy configuration file

    tutorial/             # project's Python module, you'll import your code from here
        __init__.py

        items.py          # project items definition file

        middlewares.py    # project middlewares file

        pipelines.py      # project pipelines file

        settings.py       # project settings file

        spiders/          # a directory where you'll later put your spiders
            __init__.py

在这里插入图片描述

这些文件分别是:

  1. scrapy.cfg: 项目的配置文件,现在可以先忽略。

  2. tutorial/: 该项目的python模块。

  3. tutorial/items.py: 项目中的item文件。

​ Item 是保存爬取到的数据的容器;其使用方法和python字典类似, 并且提供了额外保护机制来避免拼写错误导致的未定义字段错误。

  1. tutorial/pipelines.py: 项目中的pipelines文件。

    Scrapy提供了pipeline模块来执行保存数据的操作。在创建的 Scrapy 项目中自动创建了一个 pipeline.py 文件,同时创建了一个默认的 Pipeline 类。比如我们可以在里面写把item提取的数据保存到mysql数据库的方法。

  2. tutorial/settings.py: 项目的设置文件。

    settings.py是Scrapy中比较重要的配置文件,里面可以设置的内容非常之多。

  3. tutorial/spiders/: 放置spider代码的目录。爬虫文件就放在里面

创建爬虫

scrapy genspider 爬虫名 要爬取的网站域名 # 注意爬虫名不要和项目名冲突,网站域名指一级域名,如:baidu.com

生成的爬虫文件会放在文件夹项目名/spider/下为爬虫名.py

启动爬虫

scrapy crawl 爬虫名字    # 注意是名字,不是爬虫py文件

CVE_Details爬虫

先从setting讲起还是从网页讲起呢

不说话那就网页吧!

访问https://www.cvedetails.com可以进入CVE_Details的主页面,这就是我们的重要漏洞库来源,看左边Browse–>vulnerabilities by data 点进去会发现自有记录以来所有的漏洞数量信息,按照年->月->页数来爬取太麻烦,跳过月,直接按照每年的爬取

随便点进一个年份,发现url为https://www.cvedetails.com/vulnerability-list/year-1999/vulnerabilities.html感觉可以替换里面的年份,试试果然可以成功访问,由于每年漏洞数量和页数不一样,页数的最大值不好搞,,直接选择前面这个简单的漏洞数量,然后进行除以50的向上取整运算,就能得到页数

在这里插入图片描述

再观察观察每一页的url,发现第2页开始url就变复杂了

https://www.cvedetails.com/vulnerability-list.php?vendor_id=0&product_id=0&version_id=0&page=3&hasexp=0&opdos=0&opec=0&opov=0&opcsrf=0&opgpriv=0&opsqli=0&opxss=0&opdirt=0&opmemc=0&ophttprs=0&opbyp=0&opfileinc=0&opginf=0&cvssscoremin=0&cvssscoremax=0&year=1999&month=0&cweid=0&order=1&trc=894&sha=8fdcb89732c98600636042e1eff8c1b2ff5cb25d

尝试过后发现几个重要的参数,page(页数), year(年份), trc(这个就是数量,去掉也没事)

问题解决,通过 year构造的url --> 得到页数并构造页数的url --> 得到每一条cve链接 -->访问cve页面爬取信息返回管道

最重要的是xpath选择器,推荐看官方文档的介绍,然后推荐一个很方便尝试的方式,比如第一步,我们访问用year构造的url,然后用选择器得到页数,可以像下面一样在cmd中输入

scrapy shell https://www.cvedetails.com/vulnerability-list/year-1999/vulnerabilities.html

可以很方便的进行选择器的调试,我通常在这里试验选择器效果

在这里插入图片描述

回到这个网页,只需要获取这个div下的b标签

在这里插入图片描述
在这里插入图片描述

在cmd里试验

>>> response.selector.xpath('//div[@class="paging"]')                                                                
[<Selector xpath='//div[@class="paging"]' data='<div class="paging" id="pagingt" styl...'>, <Selector xpath='//div[@class="paging"]' data='<div class="paging" id="pagingb">\n\tTo...'>] 
>>> response.selector.xpath('//div[@class="paging"]').get()       
'<div class="paging" id="pagingt" style="display:none; clear:both;"></div>' 
>>> response.selector.xpath('//div[@class="paging"]/b').get()                                                          
'<b>894</b>'
>>> response.selector.xpath('//div[@class="paging"]/b/text()').get() 
'894'

轻松到手!于是可以开始写了,在生成的py文件里应该有默认代码,稍微改改就能自己用

class CveDetailSpider(scrapy.Spider):
    name = 'cve_detail'
    allowed_domains = ['https://www.cvedetails.com']
    start_urls = [
        "https://www.cvedetails.com/vulnerability-list/year-" + str(i) + "/vulnerabilities.html" for i in range(1999, 2021)
    ]		# 建议大家这里改为range(2020, 1998, -1)倒序爬取
    
    def parse(self, response):
        # 得到页数,生成url
        # 获取cve的数量
        nums = response.selector.xpath('//div[@id="pagingb"]/b/text()').get()
        # 向上取整算出页数
        pages = ceil(int(nums)/50)
        # 遍历年份1999-2020年
        for year in range(1999, 2021):
            # 遍历页数
            for page in range(1, pages+1):
               	# 通过page,year,nums生成页面的url
                newurl = self.get_url(str(page), str(year), str(nums))
                yield scrapy.Request(url=newurl, callback=self.parse1, dont_filter=True)

scrapy默认从start_urls[] 寻找可爬取的url,然后默认调用parse()进行访问,注意ceil()方法需要导入math库函数,from math import ceil

get_url()是我写的生成url的函数,如下代码

yield 两句话搞定,首先它相当于return,同时它还是一个生成器

def get_url(self, page, year, trc):
        return "https://www.cvedetails.com/vulnerability-list.php?vendor_id=0&product_id=0&version_id=0&page={}&hasexp=0&opdos=0&opec=0&opov=0&opcsrf=0&opgpriv=0&opsqli=0&opxss=0&opdirt=0&opmemc=0&ophttprs=0&opbyp=0&opfileinc=0&opginf=0&cvssscoremin=0&cvssscoremax=0&year={}&month=0&cweid=0&order=1&trc={}&sha=ef7bb39664f094781e7b403da0e482830f5837d6".format(page, year, trc)

yield scrapy.Request()有两个参数,url和回调函数,我写了另外一个回调函数parse1()来处理下阶段的页面解析

继续使用cmd进行分析(上一次的退出命令是exit()

scrapy shell https://www.cvedetails.com/vulnerability-list.php?vendor_id=0&product_id=0&version_id=0&page=3&hasexp=0&opdos=0&opec=0&opov=0&opcsrf=0&opgpriv=0&opsqli=0&opxss=0&opdirt=0&opmemc=0&ophttprs=0&opbyp=0&opfileinc=0&opginf=0&cvssscoremin=0&cvssscoremax=0&year=1999&month=0&cweid=0&order=1&trc=894&sha=8fdcb89732c98600636042e1eff8c1b2ff5cb25d

这页面简单,我们只需要获取一个一个的链接,就在这些字的a标签下

在这里插入图片描述

尝试一下,就能用response.selector.xpath('//div[@id="searchresults"]/table/tr[@class="srrowns"]/td[@nowrap]/a/@href').get()定位到了,但是get()只能匹配到一个,可以用getall()将页面上的全部拿到手

>>> response.selector.xpath('//div[@id="searchresults"]/table/tr[@class="srrowns"]/td[@nowrap]/a/@href').getall() 
['/cve/CVE-2019-1020019/', '/cve/CVE-2019-1020018/', '/cve/CVE-2019-1020017/', '/cve/CVE-2019-1020016/', '/cve/CVE-2019-1020015/', '/cve/CVE-2019-1020014/', '/cve/CVE-2019-1020013/', '/cve/CVE-2019-1020012/', '/cve/CVE-2019-1020011/', '/cve/CVE-2019-1020010/', '/cve/CVE-2019-1020009/', '/cve/CVE-2019-1020008/', '/cve/CVE-2019-1020007/', '/cve/CVE-2019-1020006/', '/cve/CVE-2019-1020005/', '/cve/CVE-2019-1020004/', '/cve/CVE-2019-1020003/', '/cve/CVE-2019-1020002/', '/cve/CVE-2019-1020001/', '/cve/CVE-2019-1010319/', '/cve/CVE-2019-1010318/', '/cve/CVE-2019-1010317/', '/cve/CVE-2019-1010316/', '/cve/CVE-2019-1010315/', '/cve/CVE-2019-1010314/', '/cve/CVE-2019-1010312/', '/cve/CVE-2019-1010311/', '/cve/CVE-2019-1010310/', '/cve/CVE-2019-1010309/', '/cve/CVE-2019-1010308/', '/cve/CVE-2019-1010307/', '/cve/CVE-2019-1010306/', '/cve/CVE-2019-1010305/', '/cve/CVE-2019-1010304/', '/cve/CVE-2019-1010302/', '/cve/CVE-2019-1010301/', '/cve/CVE-2019-1010300/', '/cve/CVE-2019-1010299/', '/cve/CVE-2019-1010298/', '/cve/CVE-2019-1010297/', '/cve/CVE-2019-1010296/', '/cve/CVE-2019-1010295/', '/cve/CVE-2019-1010294/', '/cve/CVE-2019-1010293/', '/cve/CVE-2019-1010292/', '/cve/CVE-2019-1010290/', '/cve/CVE-2019-1010287/', '/cve/CVE-2019-1010283/', '/cve/CVE-2019-1010279/', '/cve/CVE-2019-1010275/'] 

随便点进一个知道下一次跳转是"https://www.cvedetails.com"+爬取的url,所以parse1()也会写了

def parse1(self, response):
    # xpath爬取url列表
    detailurls = response.selector.xpath('//div[@id="searchresults"]/table/tr[@class="srrowns"]/td[@nowrap]/a/@href').getall()
    for detailurl in detailurls:
        # for循环构造每个子页面url
        durl = "https://www.cvedetails.com" + detailurl
        yield scrapy.Request(url=durl, callback=self.parse2, dont_filter=True)

这里又用yield回调了parse2(),没错我就是命名天才,略略略

老规矩,cmd

scrapy shell https://www.cvedetails.com/cve/CVE-1999-1567/  

找到需要爬取的目标点,CVE编号,危害等级,漏洞类型,供应商,型号,设备类型,固件版本号

在这里插入图片描述

# CVE编号
cveid = response.selector.xpath('//h1/a/text()').get()
# 危害等级
score = response.selector.xpath('//div[@class="cvssbox"]/text()').get()

注意有的页面危害等级为0.0有可能是信息丢失也有可能是保密信息,反正页面没显示,写个判断直接跳过

if score == '0.0':
    return None

然后是漏洞类型,这个有点麻烦,为空的时候很难定位,有字的时候直接锁定就行了,所以我获取了整个表格倒数第二个标签信息,再用re正则表达式匹配标签的信息,如果findall()匹配不到就会返回空列表,这我很喜欢,两句代码搞定

vulntype =  re.findall(r'">(.*?)</span>', response.selector.xpath('//table[@id="cvssscorestable"]/tr').getall()[-2])
vulntype = '' if vulntype == [] else vulntype[0]

接下来的设备就比较麻烦了,因为很有可能一个漏洞对应很多个设备同时很多个版本,总之,直接把设备列表的每一行都获取到就行了

>>> response.selector.xpath('//table[@id="vulnprodstable"]/tr').getall()[1:]           
['<tr>\n\t\t\t\t\t\t\t<td class="num">\n\t\t\t\t\t\t\t\t1\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\tApplication\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t<a href="//www.cvedetails.com/vendor/216/Seapine-Software.html" title="Details for Seapine Software">Seapine Software</a>\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t<a href="//www.cvedetails.com/product/380/Seapine-Software-Testtrack.html?vendor_id=216" title="Product Details Seapine Software Testtrack">Testtrack</a>\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t <a href="/version/1106/Seapine-Software-Testtrack-.html" title="Seapine Software Testtrack ">Version Details</a>&amp;nbsp<a href="/vulnerability-list/vendor_id-216/product_id-380/version_id-1106/Seapine-Software-Testtrack-.html" title="Vulnerabilities of Seapine Software Testtrack ">Vulnerabilities</a>\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>']

很恶心,不过没关系,两个规则直接匹配

rule1 = re.compile(r'<a .*>(.*)</a>')
rule2 = re.compile(r'<td>\s+(.*?)\s+</td>')
vendor,product,_ = rule1.findall(make)
producttype,_,_,version,_,_,_,_ = rule2.findall(make)

emmmm,正则有问题的可以隔壁去看看正则表达式,反正我也是用一次查一次

最后实例化管道,然后每一行的设备都和cveid他们存入管道,通过管道存入数据库,下面是parse2()代码

def parse2(self, response):
    # CVE编号,危害等级,漏洞类型,供应商,型号,设备类型,固件版本号
    cveid = response.selector.xpath('//h1/a/text()').get()
    score = response.selector.xpath('//div[@class="cvssbox"]/text()').get()
    if score == '0.0':
        return None
    vulntype =  re.findall(r'">(.*?)</span>', response.selector.xpath('//table[@id="cvssscorestable"]/tr').getall()[-2])
    vulntype = '' if vulntype == [] else vulntype[0]
    makes = response.selector.xpath('//table[@id="vulnprodstable"]/tr').getall()[1:]   
    rule1 = re.compile(r'<a .*>(.*)</a>')
    rule2 = re.compile(r'<td>\s+(.*?)\s+</td>')
    for make in makes:
        vendor,product,_ = rule1.findall(make)
        producttype,_,_,version,_,_,_,_ = rule2.findall(make)
        item = CveDetailsItem()
        item['cveid'],item['score'],item['vulntype'],item['vendor'],item['product'],i
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值