面试:已知100多个数据库CVE漏洞编号,如何快速查询这些漏洞影响的数据库版本、详细漏洞说明等?

快速查询数据库CVE漏洞影响版本及说明

image.png

需求说明:

安全漏扫出Oracle、MySQL等数据库存在大几百个CVE漏洞,如何快速分析出每个CVE漏洞影响的数据库版本、详细漏洞说明等。

解决方案:

通过python程序,按年下载json格式的cve漏洞说明,自动转换为csv格式,将csv文件、扫描出的漏洞列表分别导入到mysql库不同表,进行分析查询(或者直接使用execl进行对比查询)。

实施步骤:

1.将CVE库下载到本地

默认下载的CVE库是json格式,例如:

https://nvd.nist.gov/feeds/json/cve/1.1/nvdcve-1.1-2025.json.zip

需要通过python进行远程下载并且转换为csv格式。
详细python代码见文章末尾。

python代码执行过程如下:

[root@cjc-db-03 cvelist]# python3.6 2025_csv.py 
/usr/local/lib/python3.6/site-packages/requests/__init__.py:104: RequestsDependencyWarning: urllib3 (1.26.20) or chardet (5.0.0)/charset_normalizer (2.0.12) doesn't match a supported version!
  RequestsDependencyWarning)
============================================================
CVE 数据导出工具 (CSV 格式)
============================================================
目标年份: 2025
============================================================
✓ pandas 已安装
✓ tqdm 已安装
✓ chardet 已安装
✓ html 已安装
所有必要包已安装完成
正在下载 2025 年的 CVE 数据...
来源: https://nvd.nist.gov/feeds/json/cve/1.1/nvdcve-1.1-2025.json.zip
✓ 下载成功!
正在解析 JSON 数据...
找到 18602 个 CVE 条目
处理CVE条目:   8%|█████▎                                                         | 1559/18602 [00:00<00:01, 15580.9处理CVE条目:  17%|██████████▌                                                    | 3118/18602 [00:00<00:01, 14处理CVE条目:  26%|████████████████▌                                              | 4889/18602 [00:00<00:处理CVE条目:  37%|███████████████████████▏                                       | 6831/18602 [00处理CVE条目:  46%|████████████████████████████▉                                  | 8554/1860处理CVE条目:  58%|███████████████████████████████████▊                          | 107处理CVE条目:  68%|██████████████████████████████████████████                   处理CVE条目:  77%|███████████████████████████████████████████████▉       处理CVE条目:  88%|███████████████████████████████████████████████████处理CVE条目:  98%|███████████████████████████████████████████████████处理CVE条目: 100%|██████████████████████████████████████████████████████████████| 18602/18602 [00:01<00:00, 16854.35it/s]
成功处理 18602 个 CVE 条目
正在生成 CSV 文件...
✓ 转换完成!CSV 文件已保存为: CVE_2025_Report.csv

============================================================
导出成功!CSV 文件路径: /cvelist/CVE_2025_Report.csv
============================================================
CSV 文件包含以下列:
- CVE ID: 漏洞的唯一标识符
- 描述: 漏洞的英文描述
- 发布时间: CVE 的发布日期
- CVSS 版本: 使用的 CVSS 版本 (v3.0 或 v2.0)
- CVSS 分数: 漏洞的 CVSS 基本分数
- 严重性: 漏洞的严重等级
- 供应商: 受影响的供应商列表
- 产品: 受影响的产品列表
- 参考链接: 相关参考链接
============================================================
文件大小: 10.64 MB

后续操作建议:
1. 使用 Excel 或文本编辑器打开: /cvelist/CVE_2025_Report.csv
2. 导入到 MySQL 数据库: 使用 LOAD DATA INFILE 或 MySQL Workbench
3. 使用 Python pandas 进行数据分析: pd.read_csv('/cvelist/CVE_2025_Report.csv')

按 Enter 退出...

2.将下载后的csv文件导入到MySQL数据库里。

MySQL:新增库、表

create database cve_database;
use cve_database;
CREATE TABLE `2025_cve` (
  `id` int NOT NULL AUTO_INCREMENT,
  `cve_id` varchar(50) DEFAULT NULL,
  `description` text,
  `published_date` varchar(50) DEFAULT NULL,
  `cvss_version` varchar(50) DEFAULT NULL,
  `cvss_score` varchar(50) DEFAULT NULL,
  `severity` varchar(50) DEFAULT NULL,
  `vendors` text,
  `products` text,
  `ref_links` text,
  PRIMARY KEY (`id`));

将CSV数据加载进MySQL

LOAD DATA INFILE '/cvelist/CVE_2025_Report.csv'
INTO TABLE 2025_cve
FIELDS TERMINATED BY ',' 
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS  -- 跳过标题行
(
    cve_id,
    description,
    published_date,  -- 原样导入日期字符串
    cvss_version,
    cvss_score,
    severity,
    vendors,
    products,
    ref_links
);

相同的方法,每一年使用独立的表,分别导入到 2024_cve,2023_cve,…,2018_cve表里。

创建视图,方便查询:

create view cvelist as select * from 2025_cve union all select * from 2024_cve union all select * from 2023_cve union all select * from 2022_cve union all select * from 2021_cve union all select * from 2020_cve union all select * from 2019_cve union all select * from 2018_cve;

3.将安全漏扫的数据导入到MySQL数据库里

主要是序号和CVE编号两列。

先创建对应表结构:

create table cve_check (id int,cve_id varchar(200));
ALTER TABLE cve_check CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;

准备CSV文件
原文件是execlg格式,需要另存为CSV格式,逗号分隔。例如

1,Apache Tomcat 安全漏洞(CVE-202x-xxxxx)
2,OpenSSL 信任管理问题漏洞(CVE-202x-xxxxx)
3,OpenSSL 信任管理问题漏洞(CVE-202x-xxxxx)
4,Oracle MySQL Server存在未明漏洞(CVE-202x-xxxxx)
5,Oracle MySQL Server存在未明漏洞(CVE-202x-xxxxx)
......

将csv导入到cve_check表里

LOAD DATA INFILE '/cvelist/001.csv'
INTO TABLE cve_check
FIELDS TERMINATED BY ',' 
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS  -- 跳过标题行
(
    id,
    cve_id
);

4.关联查询

安全漏扫表和CVE本地库表cvelist进行关联查询,查询出安全漏扫表里cve_id对应的漏洞描述和版本信息。
其中 安全漏扫表 记录的cve_id格式如下:
Oracle MySQL Server存在未明漏洞(CVE-202x-xxxxx)
需要 SUBSTRING_INDEX 截取出括号内的CVE编号,输出到cve_check01表里。

create table cve_check01 as select id,SUBSTRING_INDEX(SUBSTRING_INDEX(cve_id, '(', -1), ')', 1) AS cve_id from cve_check; 

关联查询,得到需要的数据

SELECT t1.id,t1.cve_id,t2.description FROM cve_check01 t1 left join cvelist t2 on t1.cve_id=t2.cve_id order by t1.id;

导出数据:

SELECT t1.id, t1.cve_id, t2.description 
FROM cve_check01 t1 
LEFT JOIN cvelist t2 ON t1.cve_id = t2.cve_id 
ORDER BY t1.id
INTO OUTFILE '/cvelist/xxx01.csv' 
FIELDS TERMINATED BY ',' 
ENCLOSED BY '"' 
LINES TERMINATED BY '\n';

最终将导出xxx01.csv表的description列,全部复制粘贴到安全扫描表里的漏洞说明列。

但是 漏洞说明 里的描述信息太多,如何把漏洞说明里的版本信息筛选出来,观察发现,版本信息都是 "affected"开头, “prior.结尾的”,所以需要使用 REGEXP_SUBSTR 函数。
例如:

affected are 5.6.41 and prior, 5.7.23 and prior and 8.0.12 and prior.

最终加上 affected_versions 列,导出

SELECT t1.id, t1.cve_id,REGEXP_SUBSTR(description,'affected are.*?prior\\.') AS affected_versions,t2.description 
FROM cve_check01 t1 
LEFT JOIN cvelist t2 ON t1.cve_id = t2.cve_id 
ORDER BY t1.id
INTO OUTFILE '/cvelist/xxx01.csv' 
FIELDS TERMINATED BY ',' 
ENCLOSED BY '"' 
LINES TERMINATED BY '\n';

2025_csv.py 代码如下:

[root@cjc-db-03 cvelist]# cat 2025_csv.py 
import requests
import zipfile
import json
import pandas as pd
import os
import io
import re
import html
import sys
import subprocess
import time
import platform
from datetime import datetime
from tqdm import tqdm

def install_with_retry(package, retries=3, delay=5):
    """带重试机制的包安装函数"""
    for attempt in range(retries):
        try:
            print(f"尝试安装 {package} (尝试 {attempt+1}/{retries})")
            
            # 使用国内镜像源并增加超时时间
            command = [
                sys.executable, "-m", "pip", "install", 
                "--index-url", "https://pypi.tuna.tsinghua.edu.cn/simple",
                "--trusted-host", "pypi.tuna.tsinghua.edu.cn",
                "--timeout", "60",  # 增加超时时间到60秒
                "--retries", "3",    # 设置重试次数
                package
            ]
            
            # 在 Windows 上禁用缓存以避免权限问题
            if platform.system() == "Windows":
                command.append("--no-cache-dir")
            
            result = subprocess.run(
                command,
                check=True,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True
            )
            print(f"✓ 成功安装 {package}")
            return True
        except subprocess.CalledProcessError as e:
            print(f"安装 {package} 失败: {e.stderr.strip()}")
            print(f"等待 {delay} 秒后重试...")
            time.sleep(delay)
            delay *= 2  # 指数退避策略
    
    print(f"✗ 无法安装 {package},已达到最大重试次数")
    return False

def check_and_install_requirements():
    """检查并安装所有必要的包"""
    required_packages = ['pandas', 'tqdm', 'chardet', 'html']
    
    # 检查是否已安装
    for package in required_packages:
        try:
            __import__(package)
            print(f"✓ {package} 已安装")
        except ImportError:
            print(f"{package} 未安装,开始安装...")
            if not install_with_retry(package):
                print("安装失败,请手动运行以下命令安装:")
                print(f"pip install {package} --index-url https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn --timeout 60")
                sys.exit(1)
    
    print("所有必要包已安装完成")

def clean_text(text):
    """清理文本确保CSV兼容性"""
    if not isinstance(text, str) or pd.isna(text):
        return ""
    
    # 1. 解码HTML实体
    text = html.unescape(text)
    
    # 2. 移除非UTF-8字符
    text = re.sub(r'[^\x00-\x7F\x80-\xFF]', '', text)
    
    # 3. 移除控制字符
    text = re.sub(r'[\x00-\x1F\x7F]', ' ', text)
    
    # 4. 替换特殊引号
    text = text.replace('"', "'").replace('\\', '')
    
    # 5. 规范化空格
    text = re.sub(r'\s+', ' ', text).strip()
    
    return text

def download_nvd_json(year):
    """
    下载指定年份的 NVD JSON 压缩文件
    """
    url = f"https://nvd.nist.gov/feeds/json/cve/1.1/nvdcve-1.1-{year}.json.zip"
    print(f"正在下载 {year} 年的 CVE 数据...")
    print(f"来源: {url}")
    
    try:
        response = requests.get(url, timeout=30)
        response.raise_for_status()  # 检查HTTP错误
        
        print("✓ 下载成功!")
        return io.BytesIO(response.content)
        
    except requests.exceptions.RequestException as e:
        print(f"✗ 下载失败: {str(e)}")
        return None

def extract_and_convert_to_csv(zip_stream, year):
    """
    从内存中的 ZIP 流提取 JSON 文件并转换为 CSV
    """
    try:
        # 使用内存中的 ZIP 文件
        with zipfile.ZipFile(zip_stream) as zip_file:
            json_filename = f"nvdcve-1.1-{year}.json"
            
            # 检查 ZIP 中是否存在该文件
            if json_filename not in zip_file.namelist():
                print(f"✗ 在 ZIP 文件中未找到 {json_filename}")
                return False
            
            # 读取 JSON 内容
            with zip_file.open(json_filename) as json_file:
                print("正在解析 JSON 数据...")
                data = json.load(json_file)
                
                # 提取 CVE 数据
                cve_list = []
                total_items = len(data["CVE_Items"])
                print(f"找到 {total_items} 个 CVE 条目")
                
                for i, item in enumerate(tqdm(data["CVE_Items"], desc="处理CVE条目")):
                    try:
                        # 提取基本 CVE 信息
                        cve_id = item["cve"]["CVE_data_meta"]["ID"]
                        
                        # 获取英文描述
                        descriptions = item["cve"]["description"]["description_data"]
                        en_description = next((desc["value"] for desc in descriptions if desc["lang"] == "en"), "无英文描述")
                        
                        # 清理描述文本
                        en_description = clean_text(en_description)
                        
                        # 提取发布时间
                        published_date = item["publishedDate"]
                        
                        # 提取严重性评分 (支持 CVSS v3 和 v2)
                        cvss_version = ""
                        base_severity = "N/A"
                        cvss_score = "N/A"
                        
                        # 先尝试获取 CVSS v3
                        if "baseMetricV3" in item["impact"]:
                            cvss_version = "v3.0"
                            cvss_data = item["impact"]["baseMetricV3"]["cvssV3"]
                            cvss_score = cvss_data.get("baseScore", "N/A")
                            base_severity = cvss_data.get("baseSeverity", "N/A")
                        # 回退到 CVSS v2
                        elif "baseMetricV2" in item["impact"]:
                            cvss_version = "v2.0"
                            cvss_data = item["impact"]["baseMetricV2"]["cvssV2"]
                            cvss_score = cvss_data.get("baseScore", "N/A")
                            base_severity = item["impact"]["baseMetricV2"].get("severity", "N/A")
                        
                        # 提取供应商和产品信息 (处理缺失字段)
                        vendors = set()
                        products = set()
                        
                        # 安全地访问 'affects' 字段
                        if "affects" in item["cve"]:
                            affects = item["cve"]["affects"]
                            if "vendor" in affects and "vendor_data" in affects["vendor"]:
                                for vendor_data in affects["vendor"]["vendor_data"]:
                                    if "vendor_name" in vendor_data:
                                        vendor_name = clean_text(vendor_data["vendor_name"])
                                        if vendor_name:
                                            vendors.add(vendor_name)
                                    
                                    # 提取产品信息
                                    if "product" in vendor_data and "product_data" in vendor_data["product"]:
                                        for product_data in vendor_data["product"]["product_data"]:
                                            if "product_name" in product_data:
                                                product_name = clean_text(product_data["product_name"])
                                                if product_name:
                                                    products.add(product_name)
                        
                        vendor_list = ", ".join(sorted(vendors)) if vendors else "N/A"
                        product_list = ", ".join(sorted(products)) if products else "N/A"
                        
                        # 提取参考链接
                        references = []
                        if "references" in item["cve"] and "reference_data" in item["cve"]["references"]:
                            for ref in item["cve"]["references"]["reference_data"]:
                                url = clean_text(ref["url"])
                                if url:
                                    references.append(url)
                        reference_list = "; ".join(references) if references else "N/A"
                        
                        # 添加数据到列表
                        cve_list.append({
                            "CVE ID": cve_id,
                            "描述": en_description,
                            "发布时间": published_date,
                            "CVSS 版本": cvss_version,
                            "CVSS 分数": cvss_score,
                            "严重性": base_severity,
                            "供应商": vendor_list,
                            "产品": product_list,
                            "参考链接": reference_list
                        })
                        
                    except Exception as e:
                        # 记录错误但继续处理其他条目
                        print(f"! 处理条目 {i+1} 时出错: {str(e)}")
                        continue
                
                print(f"成功处理 {len(cve_list)} 个 CVE 条目")
                
                # 创建 DataFrame
                df = pd.DataFrame(cve_list)
                
                # 设置输出文件名
                csv_filename = f"CVE_{year}_Report.csv"
                
                # 导出到 CSV
                print("正在生成 CSV 文件...")
                df.to_csv(csv_filename, index=False, encoding='utf-8')
                
                print(f"✓ 转换完成!CSV 文件已保存为: {csv_filename}")
                
                # 返回CSV文件路径
                return os.path.abspath(csv_filename)
                
    except Exception as e:
        print(f"✗ 处理过程中发生严重错误: {str(e)}")
        return False

def main():
    # 设置目标年份
    year = 2025
    
    print("=" * 60)
    print("CVE 数据导出工具 (CSV 格式)")
    print("=" * 60)
    print(f"目标年份: {year}")
    print("=" * 60)
    
    # 检查并安装依赖
    check_and_install_requirements()
    
    # 下载 ZIP 文件到内存
    zip_stream = download_nvd_json(year)
    
    if zip_stream:
        # 处理内存中的 ZIP 文件并转换为 CSV
        csv_path = extract_and_convert_to_csv(zip_stream, year)
        
        if csv_path:
            print("\n" + "=" * 60)
            print(f"导出成功!CSV 文件路径: {csv_path}")
            print("=" * 60)
            print("CSV 文件包含以下列:")
            print("- CVE ID: 漏洞的唯一标识符")
            print("- 描述: 漏洞的英文描述")
            print("- 发布时间: CVE 的发布日期")
            print("- CVSS 版本: 使用的 CVSS 版本 (v3.0 或 v2.0)")
            print("- CVSS 分数: 漏洞的 CVSS 基本分数")
            print("- 严重性: 漏洞的严重等级")
            print("- 供应商: 受影响的供应商列表")
            print("- 产品: 受影响的产品列表")
            print("- 参考链接: 相关参考链接")
            print("=" * 60)
            
            # 提供文件大小信息
            file_size = os.path.getsize(csv_path) / (1024 * 1024)  # 转换为MB
            print(f"文件大小: {file_size:.2f} MB")
            
            # 提供后续操作建议
            print("\n后续操作建议:")
            print(f"1. 使用 Excel 或文本编辑器打开: {csv_path}")
            print("2. 导入到 MySQL 数据库: 使用 LOAD DATA INFILE 或 MySQL Workbench")
            print("3. 使用 Python pandas 进行数据分析: pd.read_csv('" + csv_path + "')")
        else:
            print("\n✗ CSV 文件生成失败")
    else:
        print("\n✗ 无法继续处理,因为下载失败")

if __name__ == "__main__":
    main()
    print("\n按 Enter 退出...")
    input()

欢迎关注我的公众号《IT小Chen

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值