OpenVAS与Sn1per自动化集成:构建企业级漏洞扫描平台
2026/6/24 16:40:39 网站建设 项目流程

1. 项目概述:为什么需要整合OpenVAS与Sn1per?

在安全运营的日常里,漏洞扫描是基础中的基础,但也是最容易让人头疼的环节之一。很多团队可能都经历过这样的场景:安全工程师用OpenVAS跑完一轮全量扫描,导出一份几百上千条漏洞的XML报告,然后开始手动筛选、分类、整理,再一条条录入到工单系统或者Excel表格里,最后才能分发给不同的运维或开发团队去修复。这个过程不仅耗时费力,而且极易出错,一个疏忽就可能让高危漏洞在报告里“躺平”好几天。

OpenVAS作为一款老牌的开源漏洞扫描器,其扫描能力和漏洞库的丰富度是业界公认的。但它本质上是一个“扫描引擎”,在扫描任务的编排、结果的自动化处理和与现有工作流的集成方面,原生功能相对薄弱。而Sn1per,在安全圈内被很多人视为“自动化渗透测试框架”或“信息收集利器”,它真正的强大之处在于其模块化的架构和对各类安全工具(包括扫描器)的调用与结果聚合能力。

所以,这个项目的核心价值就出来了:通过OpenVAS的API,将它的扫描能力“管道化”,再通过Sn1per的框架进行任务调度、结果解析和报告生成,最终搭建一个能够自动化、流程化运行的企业级漏洞扫描平台。这不仅仅是把两个工具连起来,而是构建一个从资产发现、漏洞扫描、结果分析到报告分发的完整闭环。对于中小型安全团队或拥有大量资产需要定期巡检的企业来说,这种自动化能极大解放人力,让安全工程师从重复的“报告搬运工”角色中解脱出来,把精力聚焦在真正的漏洞分析和应急响应上。

我自己的团队在引入这套流程后,常规的周期性漏洞扫描从原来需要2个人天处理,压缩到了无人值守的4小时自动化运行加上1小时的复核,效率提升是肉眼可见的。

2. 平台架构设计与核心组件解析

搭建这样一个平台,我们需要先理清数据流和各个组件的职责。整个架构可以看作一个微型的SOAR(安全编排、自动化与响应)场景。

2.1 核心组件角色与选型考量

1. OpenVAS (GVM):扫描引擎层这是我们的“矛”,负责执行实际的漏洞检测。我们选择OpenVAS,一方面是因为其开源免费,另一方面是其漏洞检测脚本(NVTs)更新及时,覆盖全面。在部署时,通常我们会选择最新的GVM(Greenbone Vulnerability Management)套件,它包含了OpenVAS Scanner、Greenbone Security Assistant (GSA) 和 Greenbone Management Protocol (GMP)。我们主要与之交互的是GMP,它提供了丰富的RESTful API(通过GSAD的443端口暴露)。

注意:很多人会混淆OpenVAS和GVM。简单来说,OpenVAS是扫描引擎本身,而GVM是包含Web界面、管理协议和OpenVAS的完整发行版。我们对接的API是GVM提供的GMP over REST API。

2. Sn1per:自动化编排与聚合层这是我们的“指挥中心”。Sn1per在这里扮演的角色远不止一个简单的调用脚本。它的价值在于:

  • 任务调度器:可以按计划(Cron)或触发式地启动扫描任务。
  • API客户端:通过编写或集成模块,与OpenVAS API进行通信,完成创建任务、启动扫描、轮询状态、下载报告等一系列操作。
  • 结果解析器:将OpenVAS返回的XML格式报告,解析、过滤、格式化,转换成更易读的格式(如HTML、Markdown),或提取关键信息(如仅高危漏洞)。
  • 报告分发器:将最终报告通过邮件、Webhook(如钉钉、企业微信、Slack)或直接存入数据库(如Elasticsearch)进行归档和展示。

3. 数据库与存储层(可选但推荐)为了持久化扫描历史、资产信息和漏洞趋势,引入一个数据库是很有必要的。简单的可以用SQLite(Sn1per原生支持),如果希望做更复杂的关联分析和仪表盘,可以选用MySQL/PostgreSQL,甚至直接将结果推送到Elasticsearch + Kibana,构建一个可视化的漏洞管理面板。

4. 通知与集成层这是价值输出的最后一环。解析后的漏洞报告需要送达正确的人。除了Sn1per内置的邮件通知,我们通常会用Python脚本调用企业内部IM的API,实现精准推送。例如,将属于“Web服务器”资产组的高危漏洞,自动@对应的运维团队群。

整个数据流可以概括为:Sn1per(调度) -> 调用OpenVAS API(执行扫描)-> 获取原始报告 -> Sn1per(解析、过滤、丰富)-> 生成最终报告 -> 推送至各渠道。

2.2 环境准备与基础配置

在开始写代码之前,我们需要确保两个核心组件就绪并互通。

OpenVAS (GVM) 侧配置:

  1. 安装与启动:建议使用官方提供的OVA镜像或Docker镜像(greenbone/community-edition)进行快速部署,避免复杂的编译依赖问题。启动后,确保GSAD(Web服务)在443端口监听,GMP socket服务正常。
  2. 创建API用户:登录Web界面(GSA),进入Administration -> Users,创建一个专用于API调用的用户(如api_sniper)。务必为该用户分配足够的权限,至少需要Super Admin或者自定义一个拥有get_tasks,create_task,start_task,get_reports,get_results等权限的角色。
  3. 生成API密钥:以该API用户登录Web界面,在右上角用户菜单里找到“My Settings”,里面可以生成一个“Feed Import Token”或通过REST API直接使用用户名密码进行Bearer Token认证。更安全的方式是使用OAuth2客户端凭证流,但对于内网环境,简单的Bearer Token(由用户名密码通过/api/access_tokens接口获取)也足够。
  4. 验证API连通性:使用curl命令测试API是否可用。
    # 使用用户名密码获取Token(假设GVM地址为 https://192.168.1.100) curl -k -X POST https://192.168.1.100/api/access_tokens -H "Content-Type: application/json" -d '{"username":"api_sniper", "password":"YourStrongPassword"}'
    如果返回一个access_token字段,说明API基础访问正常。请保存好这个Token。

Sn1per 侧配置:

  1. 安装Sn1per:按照官方GitHub仓库的说明进行安装。通常就是克隆仓库并运行安装脚本。
    git clone https://github.com/1N3/Sn1per cd Sn1per sudo ./install.sh
  2. 理解Sn1per目录结构:对我们最重要的目录是~/.sniper/plugins/~/.sniper/reports/。我们将把自定义的OpenVAS API交互脚本放在plugins目录下。
  3. 安装必要的Python依赖:Sn1per本身是Bash/Python混合框架。我们需要确保Python环境有requests,xmltodict(或lxml)等库,用于调用API和解析XML。
    pip3 install requests xmltodict

3. OpenVAS API深度对接与Sn1per模块开发

这是整个项目的核心编码部分。我们需要在Sn1per的框架下,编写一个功能完整的模块,来驱动OpenVAS完成全流程。

3.1 OpenVAS API关键端点梳理

GMP REST API设计得比较清晰,主要围绕几个核心对象:Target(扫描目标)、Task(扫描任务)、Report(扫描报告)、Result(漏洞结果)。以下是我们必须用到的关键端点:

  • 认证与Token管理:
    • POST /api/access_tokens- 获取访问令牌。
  • 目标管理:
    • GET /api/targets- 获取目标列表。
    • POST /api/targets- 创建扫描目标(指定IP/域名范围、端口列表)。
  • 扫描配置与任务管理:
    • GET /api/configs- 获取扫描配置(如“Full and fast”)。
    • GET /api/scanners- 获取扫描器ID(通常是OpenVAS默认扫描器)。
    • POST /api/tasks- 创建扫描任务(关联目标、配置、扫描器)。
    • POST /api/tasks/{task_id}/start- 启动指定任务。
    • GET /api/tasks/{task_id}- 获取任务状态(status字段,Running,Done,Interrupted等)。
  • 报告与结果获取:
    • GET /api/tasks/{task_id}/reports- 获取任务关联的报告列表。
    • GET /api/reports/{report_id}- 下载特定格式的报告(通过format_id参数指定,如xml,pdf,html)。对于自动化处理,我们始终选择xml格式。
    • GET /api/results- 直接查询漏洞结果,支持过滤。

3.2 编写Sn1per自定义插件:openvas_api.py

我们将在~/.sniper/plugins/目录下创建一个Python脚本。这个脚本需要实现几个核心函数。

第一步:封装API客户端类首先,创建一个类来处理所有与OpenVAS API的交互,包括认证、错误重试和基础请求。

# ~/.sniper/plugins/openvas_api.py import requests import time import xml.etree.ElementTree as ET from requests.exceptions import RequestException class OpenVASClient: def __init__(self, host, username, password, verify_ssl=False): self.base_url = f"{host}/api" self.username = username self.password = password self.verify_ssl = verify_ssl self.token = None self.headers = {} self._authenticate() def _authenticate(self): """获取并设置Bearer Token""" auth_url = f"{self.base_url}/access_tokens" payload = {"username": self.username, "password": self.password} try: resp = requests.post(auth_url, json=payload, verify=self.verify_ssl, timeout=30) resp.raise_for_status() data = resp.json() self.token = data.get('access_token') if not self.token: raise ValueError("Failed to obtain access token") self.headers = {'Authorization': f'Bearer {self.token}', 'Content-Type': 'application/json'} print(f"[+] Successfully authenticated to OpenVAS at {self.base_url}") except RequestException as e: print(f"[-] Authentication failed: {e}") raise def _make_request(self, method, endpoint, **kwargs): """统一的请求方法,包含Token和错误处理""" url = f"{self.base_url}/{endpoint}" kwargs['headers'] = self.headers kwargs['verify'] = self.verify_ssl try: resp = requests.request(method, url, **kwargs) resp.raise_for_status() return resp except requests.exceptions.HTTPError as e: # 如果是401,可能是Token过期,尝试重新认证一次 if e.response.status_code == 401: print("[!] Token可能过期,尝试重新认证...") self._authenticate() # 更新headers后重试一次 kwargs['headers'] = self.headers resp = requests.request(method, url, **kwargs) resp.raise_for_status() return resp else: print(f"[-] API请求失败 {method} {url}: {e}") raise

第二步:实现核心工作流函数在同一个类中,添加创建目标、任务、启动扫描、等待完成和下载报告的方法。

def create_target(self, name, hosts, port_list_id="33d0cd82-57c6-11e1-8ed1-406186ea4fc5"): """创建扫描目标。port_list_id '33d0...' 是默认的 'All IANA assigned TCP' 列表ID""" target_data = { "name": name, "hosts": hosts, # 可以是单个IP,逗号分隔的IP,或CIDR范围 "port_list_id": port_list_id } resp = self._make_request('POST', 'targets', json=target_data) target_id = resp.json().get('id') print(f"[+] Created target '{name}' with ID: {target_id}") return target_id def create_task(self, name, target_id, config_id="daba56c8-73ec-11df-a475-002264764cea", scanner_id="08b69003-5fc2-4037-a479-93b440211c73"): """创建扫描任务。 config_id 'daba56c8...' 是默认的 'Full and fast' 配置ID。 scanner_id '08b69003...' 是默认的OpenVAS扫描器ID。 """ task_data = { "name": name, "target_id": target_id, "config_id": config_id, "scanner_id": scanner_id } resp = self._make_request('POST', 'tasks', json=task_data) task_id = resp.json().get('id') print(f"[+] Created task '{name}' with ID: {task_id}") return task_id def start_task(self, task_id): """启动扫描任务""" resp = self._make_request('POST', f'tasks/{task_id}/start') print(f"[+] Task {task_id} started.") return True def get_task_status(self, task_id): """获取任务状态""" resp = self._make_request('GET', f'tasks/{task_id}') status = resp.json().get('status') return status # 可能的值:'New', 'Requested', 'Running', 'Stop Requested', 'Stopped', 'Done', 'Internal Error' def wait_for_task_completion(self, task_id, interval=60): """轮询任务状态直到完成""" print(f"[*] Waiting for task {task_id} to complete...") while True: status = self.get_task_status(task_id) if status in ['Done', 'Stopped', 'Internal Error']: print(f"[*] Task {task_id} finished with status: {status}") return status elif status == 'Running': print(f" ... still running (checked at {time.strftime('%H:%M:%S')})") else: print(f"[!] Unexpected task status: {status}") time.sleep(interval) def get_task_report_id(self, task_id): """任务完成后,获取其主报告ID""" # 报告可能不会立即生成,需要稍等片刻 time.sleep(10) resp = self._make_request('GET', f'tasks/{task_id}/reports') reports = resp.json().get('reports', []) if reports: # 通常取最新的一个报告 report_id = reports[0].get('id') print(f"[+] Found report for task {task_id}: {report_id}") return report_id else: print(f"[-] No report found for task {task_id}") return None def download_report(self, report_id, format='xml'): """下载指定格式的报告""" # format_id: 'a994b278-1f62-11e1-96ac-406186ea4fc5' 对应 XML 格式 format_id_map = {'xml': 'a994b278-1f62-11e1-96ac-406186ea4fc5', 'pdf': 'c402cc3e-b531-11e1-9163-406186ea4fc5', 'html': '6c248850-1f62-11e1-b082-406186ea4fc5'} if format not in format_id_map: format = 'xml' params = {'format_id': format_id_map[format]} resp = self._make_request('GET', f'reports/{report_id}', params=params) # 注意:返回的内容可能是XML字符串或PDF/HTML的二进制数据 if format == 'xml': return resp.text else: return resp.content

第三步:编写Sn1per可调用的主函数Sn1per的插件通常通过命令行参数调用。我们需要编写一个主函数来解析参数,并串联整个流程。

# 继续在 openvas_api.py 中 import argparse import sys import os from datetime import datetime def main(): parser = argparse.ArgumentParser(description='OpenVAS API Integration for Sn1per') parser.add_argument('--target', '-t', required=True, help='Target hosts (IP, range, or comma-separated list)') parser.add_argument('--name', '-n', help='Task name (default: auto-generated)') parser.add_argument('--config', '-c', default='daba56c8-73ec-11df-a475-002264764cea', help='Scan config UUID') parser.add_argument('--output', '-o', help='Output directory for reports (default: ~/.sniper/reports)') parser.add_argument('--host', default='https://192.168.1.100', help='OpenVAS GVM host URL') parser.add_argument('--user', '-u', default='api_sniper', help='OpenVAS API username') parser.add_argument('--password', '-p', required=True, help='OpenVAS API password') parser.add_argument('--no-verify', action='store_true', help='Disable SSL verification (for self-signed certs)') args = parser.parse_args() # 设置任务名 task_name = args.name if args.name else f"sniper_scan_{datetime.now().strftime('%Y%m%d_%H%M%S')}" # 初始化客户端 client = OpenVASClient(host=args.host, username=args.user, password=args.password, verify_ssl=not args.no_verify) try: # 1. 创建目标 target_id = client.create_target(name=f"Target_{task_name}", hosts=args.target) # 2. 创建任务 task_id = client.create_task(name=task_name, target_id=target_id, config_id=args.config) # 3. 启动任务 client.start_task(task_id) # 4. 等待任务完成 final_status = client.wait_for_task_completion(task_id) if final_status != 'Done': print(f"[!] Scan did not complete successfully. Status: {final_status}") sys.exit(1) # 5. 获取并下载报告 report_id = client.get_task_report_id(task_id) if report_id: xml_report = client.download_report(report_id, format='xml') # 确定输出路径 output_dir = args.output if args.output else os.path.expanduser('~/.sniper/reports') os.makedirs(output_dir, exist_ok=True) report_filename = os.path.join(output_dir, f"{task_name}_openvas.xml") with open(report_filename, 'w', encoding='utf-8') as f: f.write(xml_report) print(f"[+] XML report saved to: {report_filename}") # 这里可以继续调用报告解析函数(见下一节) parse_and_summarize(xml_report, output_dir, task_name) else: print("[-] Failed to retrieve report.") sys.exit(1) except Exception as e: print(f"[-] An error occurred during the scan process: {e}") sys.exit(1) if __name__ == "__main__": main()

4. 报告解析、结果过滤与自动化工作流集成

拿到原始的XML报告只是第一步。一份OpenVAS的完整报告可能包含大量信息,其中很多是低危漏洞、日志信息或误报。我们需要从中提取出真正需要关注的内容。

4.1 解析XML报告并提取关键漏洞信息

我们需要编写一个函数,来解析XML,并按照严重等级、漏洞名称、受影响主机等维度进行提取和过滤。

# 在 openvas_api.py 中添加以下函数 import xmltodict # 这是一个更易于处理XML的库,也可以用内置的xml.etree.ElementTree def parse_and_summarize(xml_content, output_dir, task_name): """解析OpenVAS XML报告,生成摘要和过滤后的报告""" try: # 使用xmltodict将XML转换为有序字典 report_dict = xmltodict.parse(xml_content) except Exception as e: print(f"[-] Failed to parse XML: {e}") return # 导航到结果部分。OpenVAS XML结构较深,路径大致如下: # report -> report -> results -> result results = [] try: report = report_dict.get('report', {}).get('report', {}) result_list = report.get('results', {}).get('result', []) # 注意:如果只有一个result,它可能不是列表,而是单个字典 if isinstance(result_list, dict): result_list = [result_list] results = result_list except KeyError as e: print(f"[-] Could not find results in the report structure: {e}") return if not results: print("[*] No vulnerability results found in the report.") return # 定义我们关心的严重等级映射 (OpenVAS使用threat level) severity_map = { 'Log': 0, 'Debug': 0, 'False Positive': 0, 'Low': 1, 'Medium': 2, 'High': 3, 'Critical': 4 } filtered_results = [] for res in results: # 提取关键字段 severity = res.get('threat', 'N/A') name = res.get('name', 'N/A') nvt_oid = res.get('nvt', {}).get('@oid', 'N/A') if isinstance(res.get('nvt'), dict) else 'N/A' host = res.get('host', 'N/A') port = res.get('port', 'N/A') description = res.get('description', '') # 有些结果可能是日志或误报,根据严重等级过滤 if severity in severity_map and severity_map[severity] >= 2: # 只关注Medium及以上 filtered_results.append({ 'severity': severity, 'name': name, 'nvt_oid': nvt_oid, 'host': host, 'port': port, 'description': description[:200] + '...' if len(description) > 200 else description # 截取描述 }) # 按严重等级排序(Critical -> High -> Medium) filtered_results.sort(key=lambda x: severity_map.get(x['severity'], 0), reverse=True) # 生成文本摘要报告 summary_report = f""" # OpenVAS Scan Summary Report - Task Name: {task_name} - Scan Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - Total Results Found: {len(results)} - Filtered (Medium & Above): {len(filtered_results)} ## Vulnerabilities (Medium, High, Critical) """ for vuln in filtered_results: summary_report += f""" ### {vuln['severity']} - {vuln['name']} - **Host:** {vuln['host']}:{vuln['port']} - **NVT OID:** {vuln['nvt_oid']} - **Description:** {vuln['description']} """ # 保存摘要报告 summary_filename = os.path.join(output_dir, f"{task_name}_summary.md") with open(summary_filename, 'w', encoding='utf-utf-8') as f: f.write(summary_report) print(f"[+] Summary report saved to: {summary_filename}") # 生成CSV格式报告,便于导入表格或数据库 import csv csv_filename = os.path.join(output_dir, f"{task_name}_filtered.csv") if filtered_results: fieldnames = ['severity', 'name', 'host', 'port', 'nvt_oid', 'description'] with open(csv_filename, 'w', newline='', encoding='utf-8') as csvfile: writer = csv.DictWriter(csvfile, fieldnames=fieldnames) writer.writeheader() for vuln in filtered_results: writer.writerow(vuln) print(f"[+] Filtered CSV report saved to: {csv_filename}") # 输出统计信息到控制台 severity_count = {} for vuln in filtered_results: sev = vuln['severity'] severity_count[sev] = severity_count.get(sev, 0) + 1 print("\n=== Scan Statistics ===") for sev, count in severity_count.items(): print(f" {sev}: {count}") print(f" Total (Filtered): {len(filtered_results)}")

4.2 集成到Sn1per工作流并设置自动化

现在,我们已经有了一个功能完整的插件。接下来需要将其集成到Sn1per的主工作流中,并设置定时任务。

1. 创建Sn1per自定义扫描模式:在Sn1per中,我们可以通过创建自定义的“工作流”或“模式”来调用我们的插件。最简单的方法是在Sn1per的扫描命令中直接调用我们的Python脚本,或者创建一个Bash包装脚本。

~/.sniper/目录下创建一个新的扫描模式配置文件(例如modes/openvas_scan.ini),但更直接的方式是修改Sn1per的启动脚本或直接使用命令行。

一个更“Sn1per原生”的方式是,在Sn1per的sniper主脚本中,添加一个新的扫描选项。不过,对于快速集成,我们可以创建一个别名或一个简单的Bash脚本,放在PATH中。

例如,创建/usr/local/bin/sniper-openvas

#!/bin/bash # sniper-openvas - Wrapper to integrate OpenVAS scan into Sn1per workflow # Usage: sniper-openvas -t <target> -p <password> [其他选项] # 切换到sniper插件目录,执行我们的Python脚本 cd /home/your_user/.sniper/plugins/ python3 openvas_api.py "$@" # 可选:将生成的报告移动到Sn1per的统一报告目录,并触发后续处理(如发送通知) if [ -f "$REPORT_PATH" ]; then # 假设REPORT_PATH由python脚本设置或通过参数传递 cp "$REPORT_PATH" ~/.sniper/reports/latest_openvas.xml # 可以在这里调用其他通知脚本 # python3 ~/.sniper/plugins/notify_teams.py ~/.sniper/reports/latest_openvas.xml fi

赋予执行权限:chmod +x /usr/local/bin/sniper-openvas

2. 配置定时扫描(Cron Job):企业级漏洞扫描平台的核心是自动化。我们可以通过Cron来设置定期扫描。

编辑Crontab:crontab -e

# 每周日凌晨2点,对内部网络进行全量扫描 0 2 * * 0 /usr/local/bin/sniper-openvas -t 192.168.1.0/24 -p 'YourAPIPassword' --host https://openvas.company.com --no-verify --name "Weekly_Internal_Scan" --output /var/log/sniper_scans/ > /var/log/sniper_scans/weekly_scan.log 2>&1 # 每天凌晨3点,对关键服务器进行扫描 0 3 * * * /usr/local/bin/sniper-openvas -t 10.0.1.10,10.0.1.11,10.0.1.12 -p 'YourAPIPassword' --host https://openvas.company.com --name "Daily_Critical_Assets" --output /var/log/sniper_scans/ > /var/log/sniper_scans/daily_critical.log 2>&1

3. 添加结果通知:解析报告后,我们需要将结果发送出去。这里以发送到企业微信机器人为例,展示如何编写一个简单的通知脚本。

# ~/.sniper/plugins/notify_wechat.py import json import requests import sys import os from datetime import datetime def send_to_wechat_robot(webhook_url, summary_file_path): """发送Markdown格式的摘要到企业微信群机器人""" if not os.path.exists(summary_file_path): print(f"[-] Summary file not found: {summary_file_path}") return False with open(summary_file_path, 'r', encoding='utf-8') as f: markdown_content = f.read() # 企业微信机器人支持Markdown,但消息结构是JSON message = { "msgtype": "markdown", "markdown": { "content": markdown_content } } try: resp = requests.post(webhook_url, json=message, timeout=10) if resp.status_code == 200: print("[+] Notification sent to WeChat successfully.") return True else: print(f"[-] Failed to send notification. Status: {resp.status_code}, Response: {resp.text}") return False except Exception as e: print(f"[-] Error sending notification: {e}") return False if __name__ == "__main__": # 从环境变量或命令行参数获取Webhook URL和报告路径 webhook_url = os.getenv('WECHAT_WEBHOOK_URL', 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR_KEY') # 假设报告路径作为第一个参数传入 if len(sys.argv) > 1: summary_path = sys.argv[1] else: # 默认寻找最新的摘要报告 report_dir = os.path.expanduser('~/.sniper/reports') # 这里需要根据你的命名规则找到最新的文件,示例略 summary_path = find_latest_summary(report_dir) send_to_wechat_robot(webhook_url, summary_path)

然后,你可以在sniper-openvas包装脚本的最后,或者在Cron Job的命令行中,添加调用通知脚本的步骤。

5. 高级优化、问题排查与安全实践

平台搭建起来只是第一步,要让其稳定、高效、安全地运行,还需要考虑很多细节。

5.1 性能优化与高级配置

  1. 扫描策略调优:

    • 避免全量高频扫描:对全部资产进行“Full and fast”扫描非常耗时耗资源。建议分层扫描:
      • 关键资产(每日/每周):使用“Full and fast”或自定义的深度策略。
      • 一般资产(每月):使用“Base”或仅扫描常见高危端口的策略。
      • 外部资产(持续):可以结合OpenVAS的“Alive Test”和快速端口扫描。
    • 自定义扫描配置:在OpenVAS的Web界面中创建针对特定场景的扫描配置。例如,一个只扫描Web端口(80,443,8080,8443)并执行所有Web相关NVTs的配置,可以显著缩短扫描时间。
  2. 并发控制与资源管理:

    • OpenVAS Scanner本身有最大并发扫描任务数限制。在/etc/openvas/openvas.conf或GVM的Web管理界面中,可以调整max_hostsmax_checks参数,避免扫描器过载。
    • 在Sn1per的调度层面,如果同时扫描多个目标段,不要一次性创建大量任务。可以在Python脚本中加入队列机制,控制同时运行的OpenVAS任务数量(例如,最多3个)。
  3. 结果去重与聚合:长期运行后,同一个漏洞可能在多次扫描中重复出现。我们的解析脚本可以增加与历史数据库(如SQLite)对比的逻辑,只报告新出现的漏洞,或者标记已持续存在的漏洞。

5.2 常见问题排查实录

在实际运行中,你几乎一定会遇到下面这些问题:

  1. API调用返回401或403错误:

    • 原因:Token过期或权限不足。
    • 排查:检查API用户的权限是否完整。我们的代码中已经实现了Token过期的重试逻辑。如果持续失败,尝试在Web界面用该用户登录,确认其功能正常。
  2. 扫描任务创建成功但一直不开始(Status: ‘New’或’Requested’):

    • 原因:OpenVAS Scanner服务未运行,或者任务队列堵塞。
    • 排查:登录OpenVAS Web界面,检查Administration -> Scanner的状态是否为“Up”。在Scan Management -> Tasks查看任务队列。重启openvas-scannergvmd服务有时能解决问题。
    • 命令:sudo systemctl restart openvas-scanner gvmd
  3. 扫描报告为空或结果异常少:

    • 原因:目标主机防火墙阻止了扫描流量;扫描配置过于宽松;目标网络不可达。
    • 排查:
      • 从OpenVAS服务器本身ping/nmap一下目标,确认网络连通性。
      • 检查OpenVAS任务日志(在Web界面点击任务详情),看是否有错误信息。
      • 尝试对一个已知存在漏洞的测试主机(如Metasploitable)进行扫描,验证扫描器本身是否工作正常。
  4. XML报告解析失败:

    • 原因:OpenVAS API返回的XML结构可能随版本变化。
    • 排查:将下载的原始XML报告保存下来,用文本编辑器或xmllint工具检查其结构。调整parse_and_summarize函数中的字典键路径。使用print(report_dict.keys())一层层查看数据结构是最直接的方法。
  5. Sn1per插件执行权限问题:

    • 原因:Sn1per通常以特定用户运行,可能无权访问某些目录或环境变量。
    • 排查:确保Python脚本有执行权限 (chmod +x)。在Cron Job中,使用绝对路径,并明确设置PYTHONPATH和环境变量(如Webhook URL)。

5.3 安全与维护最佳实践

  1. 凭证安全:永远不要在脚本中硬编码密码。使用环境变量或加密的配置文件来存储OpenVAS的API密码。在Cron Job中,可以通过export设置环境变量,或者使用像python-dotenv这样的库。

    # 在用户profile或单独的环境文件中设置 export OPENVAS_PASSWORD='YourStrongPassword' # 在脚本中读取 password = os.getenv('OPENVAS_PASSWORD')
  2. API访问控制:为Sn1per使用的API账户设置最小必要权限。不要使用超级管理员账户。如果OpenVAS部署在公网(极其不推荐),务必使用强密码并考虑通过VPN或跳板机访问API。

  3. 日志与审计:确保所有扫描活动都有日志记录。我们的脚本已经将输出重定向到日志文件。此外,应定期备份OpenVAS的配置和Sn1per的报告数据库。

  4. 漏洞生命周期管理:这个平台解决了“发现”的问题,但漏洞的“修复”和“验证”闭环同样重要。可以考虑将解析后的CSV报告,通过脚本自动导入到Jira、Redmine或GitLab Issues等工单系统,并分配给相应的资产负责人。然后,在下次扫描同一资产时,可以关联历史工单,自动关闭已修复的漏洞。

  5. 定期更新:OpenVAS的漏洞库(NVTs)需要定期更新,否则扫描会失效。确保设置了自动化的NVT同步(greenbone-feed-sync或通过Web界面手动更新)。同时,关注Sn1per和所用Python库的更新。

搭建这样一个平台,初期会花费一些时间在调试和集成上,但一旦跑通,它将成为你安全运营体系中一个高度自动化的“哨兵”。它带来的效率提升和风险降低的收益,会远远超过最初的投入。最重要的是,你拥有了一个可以根据自己团队需求灵活定制和扩展的扫描框架,而不是被某个商业产品的固定功能所限制。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询