1. 为什么我要把端口扫描和LLM红队塞进同一个平台
做安全的人大概都有这种体会:工具越攒越多,nmap、masscan、httpx、nuclei、sqlmap,每个都单独跑,扫完一轮下来,资产清单在txt里,漏洞结果在json里,报告还得手动拼。更麻烦的是,扫出来的东西越来越多,真正需要人判断的那部分——比如某个端口暴露的服务到底有没有实际风险、某条报错信息是不是泄露了内部路径——反而被淹没在几千行输出里。我搭这个全栈安全作战平台的出发点很直接:把“机器能干的活”交给扫描器,把“需要判断的活”交给大模型,中间用一条流水线串起来。
这个平台的核心链路是:资产发现 → 端口扫描 → 服务识别 → 指纹匹配 → 漏洞初筛 → LLM红队分析 → 报告生成。前半段是传统安全工具的主场,后半段是AI大模型发挥的地方。我把它叫做“作战平台”而不是“扫描器”,是因为它不只是扫,还要做研判、做优先级排序、做攻击路径推演。适合谁看?有一定安全基础、想把自己手头的零散工具整合成流水线的朋友;也适合对AI大模型应用开发感兴趣、想找一个真实落地场景练手的人。哪怕你只是想知道“32G内存能不能跑本地大模型”这种问题,后面我也会给实测数据。
先说清楚一件事:这个平台是自用和授权测试场景下的工具,所有扫描行为都必须在你拥有明确授权的目标上进行。这一点不展开,但它是前提。
2. 整体架构设计与技术选型思路
2.1 为什么采用“扫描器 + 大模型”双引擎而不是纯AI
一开始我也想过,能不能直接让大模型去“看”目标,让它自己判断哪里有漏洞。实测下来这条路走不通,原因有两个。第一,大模型没有主动发起网络请求的能力,它只能基于你喂给它的文本做推理,你不可能让它自己去连一个端口。第二,大模型的上下文窗口有限,你把一个C段的扫描结果全塞进去,它要么截断要么开始胡说。所以正确的分工是:扫描器负责“采集事实”,大模型负责“解读事实”。
这个分工背后有个很重要的原则:事实归工具,判断归模型。端口开没开、banner是什么、HTTP响应头有哪些,这些是客观事实,用nmap、httpx这类工具拿到的结果比大模型猜的准一万倍。而“这个暴露的Redis未授权访问风险有多高”“这条报错信息可能对应什么类型的漏洞”“这几个低危漏洞组合起来能不能形成一条攻击链”,这些是判断,才是大模型的价值所在。
我试过反过来,让大模型直接根据域名猜开放端口,结果它给我编了一个“通常80和443是开的”,这种输出毫无意义。所以架构上必须坚持双引擎。
2.2 全栈平台的分层结构
整个平台我分成四层,从下往上依次是:
- 采集层:nmap做端口和service探测,masscan做大规模快速扫描,httpx做HTTP服务存活和指纹,subfinder做子域名枚举。这一层全是命令行工具,用Python的subprocess封装调用。
- 处理层:把各工具的输出统一成JSON格式,做去重、归一化、关联。比如nmap扫到8080端口开放,httpx确认是Tomcat,处理层就把这两条信息合并成一条资产记录。
- 分析层:这是大模型介入的地方。把处理后的资产数据按目标分组,构造prompt,调用大模型做风险研判、漏洞推演、报告生成。
- 展示层:一个简单的Web界面,用FastAPI做后端,前端就是原生HTML+JS,不搞复杂框架,够用就行。
为什么不用现成的扫描平台?因为现成的平台要么太重,要么不开放大模型接口。自己搭的好处是每一层都能按需替换,比如你今天想换个指纹库,明天想换个模型,改一个模块就行。
2.3 大模型选型:本地部署还是调用API
这是被问得最多的问题。我的建议是分场景:
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 敏感目标、内网资产 | 本地部署 | 数据不出本地,合规 |
| 公开资产、批量扫描 | API调用 | 省算力,模型能力强 |
| 学习练手 | 本地小模型 | 成本低,可反复折腾 |
| 生产环境高并发 | API + 本地缓存 | 平衡成本和速度 |
本地部署这块,我实测过几个配置。32G内存的机器,跑7B级别的模型(4bit量化)是够的,大概占用6-8G显存或内存,推理速度在消费级显卡上能到每秒20-40个token。14B的模型4bit量化需要大概10-12G,32G内存也能扛,但速度会慢一些。再往上70B就别想了,除非你有A100。所以“32G内存能装AI大模型”这个问题的答案是:能装7B到14B的量化版本,日常研判够用,但别指望它有多强的推理能力。
我自己的配置是:本地跑一个14B的量化模型做初步研判和脱敏,把脱敏后的结果再送给云端大模型做深度分析。这样既保证了敏感信息不外泄,又利用了云端模型更强的能力。
3. 核心模块拆解与实操要点
3.1 端口扫描模块:nmap和masscan怎么配合
端口扫描是整个流水线的入口,扫不全后面全白搭。我的做法是masscan打头阵,nmap做精扫。
masscan的优势是快,扫一个B段的全端口(1-65535)在千兆网环境下大概几分钟。但它的缺点是服务识别能力弱,只能告诉你端口开没开。所以流程是:
# 第一步:masscan快速发现开放端口 masscan -p1-65535 --rate=1000 -iL targets.txt -oJ masscan_result.json # 第二步:提取开放端口,交给nmap精扫 nmap -sV -sC -p <提取的端口列表> -iL targets.txt -oX nmap_result.xml这里有个坑要注意:masscan的--rate参数不要设太高,1000是相对安全的,设到10000以上容易丢包,反而扫不全。我试过用5000的rate扫一个C段,结果漏了三个端口,降到1000重扫就全了。另外masscan和nmap不要同时跑,会互相干扰,串行执行。
nmap的-sV做版本探测,-sC跑默认脚本,这两个组合能拿到大部分服务的banner和基本信息。但-sC的脚本有些会触发目标告警,授权测试里没问题,但如果你不确定授权范围,建议只用-sV。
3.2 服务识别与指纹匹配:httpx和自建指纹库
端口开了不代表服务活着,尤其是Web服务。httpx的作用就是确认HTTP/HTTPS服务是否真的在响应,并提取标题、状态码、技术栈指纹。
httpx -l urls.txt -title -status-code -tech-detect -json -o httpx_result.json-tech-detect用的是Wappalyzer的指纹库,能识别出大部分常见CMS、框架、中间件。但实际用下来,它的准确率大概在70%左右,有些自研系统或者改过banner的服务识别不出来。所以我在处理层加了一个自建指纹库,用正则匹配banner里的特征字符串。比如banner里有“Server: Apache-Coyote/1.1”就标记为Tomcat,有“X-Powered-By: ThinkPHP”就标记为ThinkPHP。
指纹匹配的优先级是:自建库 > httpx tech-detect > 端口默认服务。因为自建库是我根据实际遇到的系统慢慢攒的,针对性更强。
3.3 LLM红队分析模块:prompt怎么设计才不胡说
这是整个平台最核心也最难调的部分。大模型不是安全专家,你直接问它“这个目标有什么漏洞”,它要么给你一堆泛泛而谈,要么开始编造。我的经验是,prompt必须做到三件事:限定角色、提供事实、约束输出格式。
我用的系统prompt大概长这样:
你是一名资深渗透测试工程师,正在对授权目标进行安全评估。 你只能基于以下提供的事实信息进行分析,不得编造任何未提供的信息。 如果信息不足以判断,明确说明“信息不足,建议进一步探测”。 事实信息: - 目标IP:{ip} - 开放端口:{ports} - 服务版本:{services} - HTTP响应头:{headers} - 页面标题:{title} - 已知指纹:{fingerprints} 请按以下格式输出: 1. 风险点列表(每条包含:风险描述、依据的事实、风险等级高/中/低) 2. 建议的下一步探测动作(具体到工具和参数) 3. 如果发现多个风险点,分析它们之间是否存在组合利用可能这个prompt的关键在于“只能基于事实”和“信息不足要说明”。我试过不加这两条约束,模型会自己脑补出“该服务器可能存在心脏滴血漏洞”这种没有依据的结论。加上约束后,输出质量明显提升,虽然偶尔还是会跑偏,但至少不会凭空捏造。
还有一个技巧是分步分析。不要一次性把所有资产丢给模型,而是按目标分组,每个目标单独分析。这样上下文更聚焦,模型不容易混淆。如果目标很多,可以先用一个轻量模型做初筛,把明显没风险的过滤掉,再把有疑点的送给大模型深度分析。
3.4 报告生成:从JSON到可读报告
扫描结果最终要变成人能看的报告。我的做法是让大模型基于分析结果生成Markdown格式的报告,包含执行摘要、资产清单、风险详情、修复建议四个部分。执行摘要用自然语言概括整体风险状况,资产清单用表格呈现,风险详情按等级排序,修复建议要具体到操作步骤。
这里有个细节:修复建议不要让模型自由发挥,而是给它一个知识库。我整理了一份常见漏洞的修复方案库,模型生成建议时先从库里检索,检索不到再让它自己写。这样既保证了建议的准确性,又保留了灵活性。
4. 完整实操流程:从零跑通一条扫描链路
4.1 环境准备与依赖安装
先列一下我用的环境:
- 操作系统:Ubuntu 22.04(本地部署模型的话建议用这个,驱动兼容性好)
- Python:3.10+
- 核心工具:nmap 7.94、masscan 1.3.2、httpx 1.3.7、subfinder 2.6.3
- 大模型:本地用Ollama跑qwen2.5:14b,云端用某API(这里不具体说哪家,避免广告嫌疑)
安装依赖:
# 安装Python依赖 pip install fastapi uvicorn requests jinja2 python-nmap # 安装Ollama(本地模型) curl -fsSL https://ollama.com/install.sh | sh ollama pull qwen2.5:14b # 验证模型可用 ollama run qwen2.5:14b "你好"如果你的机器内存只有16G,建议换成7B的模型,14B会有点吃力。32G内存跑14B量化版是舒服的,我实测推理时内存占用在12G左右,留了足够余量给扫描工具。
4.2 扫描流水线的串联
整个流水线我用一个Python脚本串起来,核心逻辑是:
import subprocess import json def run_masscan(targets): # 执行masscan,返回开放端口 cmd = f"masscan -p1-65535 --rate=1000 -iL {targets} -oJ masscan.json" subprocess.run(cmd, shell=True, check=True) with open("masscan.json") as f: return json.load(f) def run_nmap(targets, ports): # 用nmap精扫 port_str = ",".join(str(p) for p in ports) cmd = f"nmap -sV -p{port_str} -iL {targets} -oX nmap.xml" subprocess.run(cmd, shell=True, check=True) # 解析XML,这里省略解析代码 return parse_nmap_xml("nmap.xml") def run_httpx(urls): cmd = f"httpx -l {urls} -title -status-code -tech-detect -json -o httpx.json" subprocess.run(cmd, shell=True, check=True) with open("httpx.json") as f: return [json.loads(line) for line in f] def analyze_with_llm(asset_data): # 构造prompt,调用大模型 prompt = build_prompt(asset_data) response = call_ollama(prompt) return response这个脚本跑一轮完整流程,对一个C段(254个IP)的全端口扫描,大概耗时:masscan 5分钟,nmap精扫 15分钟,httpx 3分钟,LLM分析 2分钟(本地14B模型)。总共25分钟左右。如果用云端API,LLM分析能压缩到30秒以内。
4.3 大模型调用的参数配置
调用本地Ollama的代码:
import requests def call_ollama(prompt, model="qwen2.5:14b"): url = "http://localhost:11434/api/generate" payload = { "model": model, "prompt": prompt, "stream": False, "options": { "temperature": 0.3, # 低温度,减少随机性 "top_p": 0.9, "num_predict": 2048 # 限制输出长度 } } resp = requests.post(url, json=payload) return resp.json()["response"]temperature设0.3是关键,安全分析需要确定性,不能让它自由发挥。我试过0.7,同样的输入两次输出差异很大,有的甚至互相矛盾。0.3就稳定多了。num_predict限制2048是为了防止模型输出太长,实际分析结果一般1000字以内就够了。
4.4 结果展示与报告输出
Web界面我用FastAPI搭了一个简单的API,前端用fetch调用。核心接口就三个:/scan触发扫描、/status查进度、/report拿报告。报告用Jinja2模板渲染成HTML,同时导出Markdown版本方便复制。
报告里的风险等级我用颜色区分:高危红色、中危橙色、低危黄色、信息蓝色。这个颜色映射是写死在模板里的,不依赖模型输出,避免模型把等级写错导致颜色混乱。
5. 踩坑记录与常见问题排查
5.1 大模型“幻觉”问题怎么压制
这是最头疼的问题。即使加了“只能基于事实”的约束,模型偶尔还是会编。我遇到过的典型幻觉包括:编造不存在的CVE编号、把低危说成高危、建议的修复方案根本不可行。
压制幻觉的手段我总结了三条:
第一,事实信息结构化。不要用自然语言描述扫描结果,而是用JSON格式喂给模型。结构化数据模型更难“误解”。比如不要写“目标开了80端口,是nginx”,而是写{"port": 80, "service": "nginx", "version": "1.18.0"}。
第二,要求模型引用依据。在prompt里明确要求“每条风险必须引用具体的事实信息作为依据”。这样模型编造的时候会露馅,因为它找不到对应的事实。
第三,后置校验。模型输出后,用一个规则引擎做校验。比如模型说“存在CVE-2021-44228漏洞”,规则引擎检查事实信息里有没有log4j的指纹,没有就标记为“待人工确认”。这一步能过滤掉大部分幻觉。
5.2 扫描速度与准确率的平衡
masscan的rate、nmap的timing、httpx的并发数,这三个参数直接影响扫描速度和准确率。我的经验值:
| 工具 | 参数 | 推荐值 | 说明 |
|---|---|---|---|
| masscan | --rate | 1000 | 再高容易丢包 |
| nmap | -T | 4 | T5太快会漏,T3太慢 |
| httpx | -threads | 50 | 再高容易被封 |
| httpx | -timeout | 10 | 默认5秒太短 |
这些值不是绝对的,要根据目标网络质量调整。内网可以适当提高,公网建议保守一点。我试过httpx开200并发扫公网,结果一半的请求超时,降到50就正常了。
5.3 常见问题速查表
| 问题现象 | 可能原因 | 解决方法 |
|---|---|---|
| masscan扫不到端口 | rate太高丢包 | 降到1000重扫 |
| nmap -sV识别不出服务 | 目标改了banner | 用自建指纹库补充 |
| httpx全部超时 | 并发太高或被封 | 降并发、加代理池 |
| 大模型输出乱码 | 上下文超限 | 减少输入数据量 |
| 大模型编造CVE | 幻觉 | 加事实约束+后置校验 |
| 本地模型推理慢 | 模型太大 | 换7B或用量化版 |
| 报告生成失败 | 模板变量缺失 | 检查JSON字段完整性 |
5.4 几个独家避坑技巧
技巧一:扫描前先做存活探测。不要直接对整段做全端口扫描,先用ping或TCP SYN探测筛出存活主机,能省一半时间。我用的是nmap -sn做存活探测,虽然慢一点但准确。
技巧二:大模型分析分批进行。一次不要超过10个目标,否则上下文太长模型会“忘记”前面的内容。我试过一次性喂30个目标,结果模型只分析了最后几个,前面的直接忽略了。
技巧三:报告里的修复建议要人工过一遍。模型给的修复建议大部分是对的,但偶尔会有“重启服务”这种万能答案。我现在的做法是模型生成后,用关键词匹配检查是否包含具体操作步骤,没有的话标记出来人工补充。
技巧四:本地模型和云端模型搭配用。本地模型做初筛和脱敏,云端模型做深度分析。这样既保护了敏感数据,又保证了分析质量。脱敏的规则很简单:把IP替换成[IP_1]、[IP_2],把域名替换成[DOMAIN_1],分析完再替换回来。
6. 平台后续可扩展的方向
这个平台目前跑通的是“扫描→分析→报告”的主链路,但安全作战远不止这些。我接下来打算加两个模块。
一个是持续监控。现在的扫描是一次性的,扫完就完了。实际场景里资产是变化的,今天开的端口明天可能关了,今天没漏洞明天可能上了新服务。我打算加一个定时任务,每天凌晨跑一轮,对比前一天的结果,只把变化的部分送给大模型分析。这样既省算力,又能及时发现新风险。
另一个是攻击路径推演。现在的大模型分析是单目标独立的,但实际上攻击往往是链式的:从A目标的信息泄露拿到凭据,用凭据登录B目标,再从B目标横向到C目标。我打算把多个目标的分析结果汇总,让大模型做跨目标的路径推演。这个难度比较大,因为需要模型理解目标之间的网络关系,但值得尝试。
还有一个方向是接入更多扫描器。现在只集成了nmap、masscan、httpx,后面想加nuclei做漏洞模板扫描、加sqlmap做注入检测。每加一个工具,处理层就多一个数据源,大模型的分析依据就更充分。但要注意的是,工具越多噪声越大,去重和关联的逻辑要跟上,否则大模型会被垃圾数据淹没。
最后说一个实际体会:这个平台最大的价值不是自动化,而是把人的注意力从“找问题”转移到“判断问题”。以前扫完一轮要花几个小时看结果,现在大模型把明显没风险的过滤掉,把有疑点的标出来,我只需要看那几条就行。效率提升是实实在在的。但也要清醒认识到,大模型目前还替代不了人的判断,它更像一个不知疲倦的初级分析师,能帮你做初筛,但最终决策还得自己来。