资讯动态

基于Java与Nmap的漏洞扫描系统实战:内网巡检与MySQL CIS审计

发布时间:2026/10/9 3:41:36 来源:尧图企业网站定制
简介基于Java实现的漏洞扫描系统项目包定位为网络安全与Java开发交叉领域的实战学习资源适合希望掌握漏扫工具原理、服务识别与漏洞匹配逻辑的开发者。资源共927个文件压缩包约33.07MB核心由604个nse脚本、146个lua文件及Java源码、class、jar构成涵盖Nmap脚本引擎与Java程序协同工作的完整结构。已有183人学习。系统知识点覆盖端口扫描、服务识别、CVE漏洞数据库匹配、扫描策略管理、报告生成与并发调度Java多线程和NIO的应用也体现在工程代码中。通过阅读源码和脚本可理解如何将Nmap探测能力封装进Java应用并学会搭建具备自定义规则和权限控制的扫描框架。整体目录含nmap服务探测库、协议指纹文件、审计脚本及Java交互模块适合作为课程设计或安全工具二次开发的参考。1. 基于 Java 的漏洞扫描系统Nmap 做引擎、Java 做外壳适合内网巡检的现成骨架这套东西说白了不是又一个自研扫描器的“牛皮”而是一个把 Nmap 的扫描能力、MySQL 的 CIS 审计脚本和 Java 的跨平台界面拼在一起的完整工程包。对中小型企业的安全岗、做等保自查的运维或是想弄懂“Java 怎么驱动底层扫描工具”的开发者来说它最值钱的地方是不用自己从零写端口扫描直接在一套打包好的代码上改参数就能出结果。它解决的问题很具体——内网里哪些机器开了不该开的端口、MySQL 配置差在哪、两轮扫描之间漏洞有没有新增。这一类活儿商业扫描器贵且重这套方案是看得见、摸得着、能自己改的替代品。2. 拆包看架构start.bat、Main.class、nmap_service.c 和 mysql-cis.audit各管哪一段2.1 先把压缩包里的文件分成四层拿到包先别急着双击先看它提供了什么。文件列表其实分成了很明显的四层层次文件作用启动层start.bat、ndiff.bat一键启动入口和扫描结果对比入口Java 界面层Main.class、DisplayForm.class主程序入口和 Swing 图形窗体扫描引擎层nmap_service.c、mysql-cis.audit服务识别模块与 MySQL 合规审计脚本元信息层CHANGELOG 及 LGPL-2.1 / MPL-1.1 / BSD-simplified 许可证版本记录和依赖组件的授权声明Main.class 是程序入口负责把各模块串起来DisplayForm.class 是典型的 Swing 界面点击扫描按钮之后后台会创建一个线程池去调度扫描任务。nmap_service.c 不是漏洞利用代码而是服务识别相关模块用来根据端口响应特征判断远程主机跑的是什么服务。mysql-cis.audit 是 Nmap 的 NSE 脚本Lua 写的专门检查 MySQL 实例是否符合 CIS 安全基线。整个包的核心链路是Java 窗体接收扫描参数ProcessBuilder 拉起 Nmap 进程Nmap 跑端口探测和服务指纹识别最后 Java 解析 XML 报告并展示在表格里。2.2 为什么这个组合能立得住Java 管调度Nmap 管识别选 Java 做外壳不是心血来潮。第一Java 的跨平台特性让同一套代码在 Windows 和 Linux 上都能跑这对安全巡检工具的部署很重要很多客户环境服务器是 Linux、办公机是 Windows一套代码两边通吃。第二Java 的并发模型天然适合扫描这类高并发的场景一台主机扫数百个端口不可能串着来。第三Swing 虽然在今天看起来老但部署简单不需要像 Electron 那样塞一个浏览器内核进去。而扫描引擎选 Nmap 更是顺理成章的事。Nmap 的指纹库沉淀了二十年几万条服务和操作系统指纹数据指望一个团队自己从零搞一套同等质量的指纹库是不现实的。这个包的做法很务实Java 只负责调度、解析和展示把最难的指纹识别工作丢给 Nmap自己只保留一个定制化的服务类型映射模块这就把工作量降到了一个合理水平。LGPL、MPL、BSD 三个许可证混在一起也不用慌——它们分别对应包里不同来源的组件只要你不把这套东西闭源后声称完全自主开发商业内部使用基本没有障碍。2.3 Java 侧的调度模型一个线程池管住整个扫描过程扫描器在 Java 侧的核心调度逻辑并不复杂常见做法是维护一个目标主机队列用固定大小的线程池从队列里拿主机每台主机再触发一次 Nmap 子进程。我一般会这样设计// 线程池调度扫描任务 int corePoolSize 8; ExecutorService executor new ThreadPoolExecutor( corePoolSize, corePoolSize, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(100), new ThreadPoolExecutor.CallerRunsPolicy()); ListString targets loadTargetList(targets.txt); CountDownLatch latch new CountDownLatch(targets.size()); for (String target : targets) { executor.submit(() - { try { // 每个主机一次 Nmap 调用输出 XML 到标准输出 NmapResult result NmapScanner.scan(target, -sS -T3 -A -oX -); ResultStore.save(result); } finally { latch.countDown(); } }); } latch.await(30, TimeUnit.MINUTES);这里有几个参数值得展开说。corePoolSize 设为 8是因为同时拉起 8 个 Nmap 子进程对一个普通内网环境来说已经足够再多容易把执行机自己的网络栈打满。队列容量 100超过上限后 CallerRunsPolicy 会让提交线程自己执行任务防止内存溢出。latch.await 的超时设为 30 分钟是为了防止某个慢目标拖垮整个扫描批次那些扫描不动的目标宁可标记为超时未完成也不死等。提示Nmap 的-oX -表示把 XML 结果输出到标准输出Java 这边直接读子进程的 stdout 就能拿到结果不需要临时文件中转这是比较干净的做法。3. 把它跑起来JDK 和 Nmap 环境准备、start.bat 解析与 Java 调 Nmap 的完整链路3.1 先做三件事装 JDK、装 Nmap、准备目标清单这个包不带 Nmap也不带 JDK环境要自己装。JDK 建议用 8 或 11代码里的 Swing 界面和 ProcessBuilder API 在 17 上也能跑但没必要追新8 的生态最稳。Nmap 是核心依赖Windows 下装完后要确认 nmap.exe 和 ncat.exe 都进了系统 PATHLinux 下用 apt install nmap 即可但版本建议 7.x 以上太老对 NSE 脚本的兼容性差。装完之后在命令行里先验证两件事java -version nmap --version如果 Java 报错检查 JAVA_HOME 有没有指向 jdk 目录如果 Nmap 报错把安装目录的路径加到 PATH 里。很多新手卡在这一步不是版本不对而是 PATH 环境变量里根本没有这两个命令。接着准备 targets.txt一行一个目标支持 IP、网段、域名三种形式192.168.1.1 192.168.1.0/24 db-server.example.com域名形式适合混合云环境扫描时会先做 DNS 解析再探测。如果目标多把文件维护在配置管理里每次扫描直接拉最新版避免手工改。3.2 start.bat 逐行拆解建议你顺手移植成 start.sh这个包里的 start.bat 内容不多核心逻辑是把 Java 虚拟机拉起来指向 Main 类echo off setlocal set JAVA_HOMEC:\Program Files\Java\jdk1.8.0_202 set NMAP_HOMEC:\Program Files (x86)\Nmap set PATH%JAVA_HOME%\bin;%NMAP_HOME%;%PATH% java -Xms256m -Xmx2g -cp .;lib\* com.scanner.Main endlocal这里有几个关键点-Xmx2g 限制堆内存上限防止解析大 XML 时把系统内存吃光-cp 指定类路径包含当前目录和 lib 目录下所有 jar 包setlocal 和 endlocal 让环境变量只在脚本内生效不污染系统全局。如果你换成 Linux 环境批处理脚本用不了需要写等价版本#!/bin/bash export JAVA_HOME/usr/lib/jvm/java-8-openjdk-amd64 export NMAP_HOME/usr/bin export PATH$JAVA_HOME/bin:$NMAP_HOME:$PATH java -Xms256m -Xmx2g -cp .:lib/* com.scanner.Main这算不上高技术但很多包交付时只提供了 .bat你要上生产环境反而得先补一个 .sh。把它翻译成 .sh 之后配合 crontab 还能做到定时巡检把一次性扫描变成周期性例行工作。3.3 Java 调 Nmap 的完整链路ProcessBuilder、超时与输出重定向Java 调外部进程的标准姿势是 ProcessBuilder这个包里的核心调用大概是下面这个写法public class NmapScanner { // 组装 Nmap 命令并执行返回 XML 字符串 public static String scan(String target, String args) throws IOException, InterruptedException, TimeoutException { ProcessBuilder pb new ProcessBuilder( nmap, args.split(\\s)[0], args.split(\\s)[1], --, target); pb.redirectErrorStream(true); // 合并 stderr 到 stdout统一读 Process process pb.start(); // 读 stdout避免子进程输出阻塞管道 StringBuilder output new StringBuilder(); try (BufferedReader reader new BufferedReader( new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) { String line; while ((line reader.readLine()) ! null) { output.append(line).append(System.lineSeparator()); } } // 最多等 10 分钟超时直接杀掉进程 if (!process.waitFor(10, TimeUnit.MINUTES)) { process.destroyForcibly(); throw new TimeoutException(Nmap 扫描超时: target); } return output.toString(); } }这段代码里有三个细节值得单独说。一是 pb.redirectErrorStream(true)如果不合并Nmap 的报错信息会单独写进 stderr读取端只读 stdout 就会漏掉关键错误排查时一头雾水二是 waitFor(10, TimeUnit.MINUTES) 用带超时的重载而不是傻等能避免某个目标网络黑洞把整个扫描任务挂死三是逐行读输出防止 XML 输出量过大时子进程被写缓冲区堵死。参数层面-sS 是 SYN 半开扫描速度快且不易被目标系统记录完整连接-T3 是适度时序控制在隐蔽性和速度之间取平衡-A 会开启服务版本探测和操作系统指纹识别信息最全但耗时翻倍。如果你只想快速摸一遍端口开放情况可以去掉 -A 改成 -sV速度能有明显提升。3.4 解析 Nmap 的 XML 输出别用正则用 DOMNmap 的 -oX 输出是标准 XML 结构最省事的做法是用 DOM 一次性加载把解析逻辑抽象成独立类DocumentBuilderFactory factory DocumentBuilderFactory.newInstance(); factory.setFeature(http://apache.org/xml/features/disallow-doctype-decl, true); DocumentBuilder builder factory.newDocumentBuilder(); Document doc builder.parse(new InputSource(new StringReader(xml))); // 遍历端口节点 NodeList ports doc.getElementsByTagName(port); for (int i 0; i ports.getLength(); i) { Element port (Element) ports.item(i); String portId port.getAttribute(portid); String protocol port.getAttribute(protocol); String state port.getAttribute(state); // 按需继续提取 service 和 script 节点 }这里最关键的一行是 setFeature(...disallow-doctype-decl, true)它关闭了 XML 外部实体解析防止恶意的 XML 文件触发 XXE。扫描器面向内网还好但如果哪天你把扫描器暴露到不可信网络解析不可信 XML 时这行代码是保命级别的安全措施。注意解析时优先选 DOM 而不是 SAX。扫描结果的 XML 体量通常在几 MB 以内DOM 完整加载后用 XPath 查询非常方便如果单次 XML 超过 100MB再考虑换成 SAX 或 StAX 省内存。4. 把 MySQL CIS 审计接进来漏扫不碰业务数据但能看出配置合不合规4.1 mysql-cis.audit 这个脚本查的是规矩不是漏洞mysql-cis.audit 是 Nmap NSE 库中的一个审计脚本对应的是 MySQL 的 CIS 安全基线检查范围包括是否允许匿名登录、root 账户是否设置了强密码、是否开启了 general log、默认端口有没有被改动、文件权限是否过宽、SSL 是否启用等几十个检查项。这些项严格说不是漏洞是配置规范——配置不合规不一定会被攻击者直接利用但往往是攻击链条上最容易被利用的一环。用法是在 Nmap 调用时按脚本名引用nmap -p 3306 --script mysql-cis.audit --script-args mysql-cis.usernamescanuser,mysql-cis.passwordscanpass 192.168.1.10注意这里需要提供一对有登录权限的 MySQL 账号密码。如果拿不到账号脚本只能连到 3306 端口但里面的检查项大多需要实际建连接执行 SQL 才能得出结果。扫描账号推荐只用 SELECT 权限的空账号不要用 root 扫一方面减少对业务库的影响另一方面也避免审计日志里留下大量 root 操作记录。4.2 把 CIS 审计结果并回 Java 展示层NSE 脚本的结果会以 script 节点写在 Nmap 的 XML 输出里Java 解析时按节点名定位即可NodeList scripts doc.getElementsByTagName(script); for (int i 0; i scripts.getLength(); i) { Element script (Element) scripts.item(i); String id script.getAttribute(id); if (mysql-cis.audit.equals(id)) { // 脚本输出在 table 节点下的 elem 节点里 NodeList tables script.getElementsByTagName(table); for (int j 0; j tables.getLength(); j) { Element table (Element) tables.item(j); String key table.getAttribute(key); // 按 key 提取检查项名称再取子节点 elem 拿结果 System.out.println(检查项: key); } } }这里有个容易翻车的地方NSE 脚本的返回结构不统一有的检查项返回 table 树状结构有的直接返回 elem 文本。写解析代码时不要假设所有项都是同一种结构最稳妥的办法是先把一段真实 XML 输出打印出来对着结构写代码而不是凭空猜。拿不到样例就一层层 getChildNodes 遍历把节点名打出来看一遍再动笔。4.3 两个容易理解错的概念扫描快不等于安全服务识别不等于攻击最后想提醒一个容易忽略的心态问题。扫描器跑完开放端口多、告警多这不代表系统就比扫描结果干净的机器更不安全——攻击者真正利用的是漏洞不是端口本身。同理nmap_service.c 做的是服务识别它根据响应特征判断这台机器上跑的可能是 MySQL这并不等于这台机器可以被攻击。把扫描器定位成摸底工具扫出高风险端口后人工复核才合理。5. 避坑实录这套 Java 漏扫跑起来最容易翻车的五个地方5.1 没有管理员权限SYN 扫描假装成功其实什么也没扫到现象Linux 上用普通用户执行 -sS 扫描Nmap 不报错结果里所有端口都是 filtered看起来像目标全开了防火墙。原因SYN 半开扫描需要发送原始 IP 包这要求 root 权限。普通用户没有原始套接字权限Nmap 会静默降级成 TCP connect 扫描但目标防火墙策略严格时connect 扫描结果和 filtered 一样表面上不报错实际没结果。解决内网巡检场景下直接用 -sT 代替 -sS牺牲一点隐蔽性换来权限兼容性生产环境跑扫描时给执行账户配置 sudo 且设置 NOPASSWD避免人工输密码打断自动化。5.2 XML 输出 GBK 编码读成 UTF-8解析结果全乱现象Windows 上 Nmap 的 XML 输出带中文注释或中文主机名Java 用 UTF-8 读出来一堆乱码节点名没乱但取值全废。原因Windows 默认编码是 GBKNmap 在某些版本会把本地化信息按系统编码写进 XML 注释和主机名。这些信息对显示结果很有用一旦乱码报告没法看。解决InputStreamReader 指定编码时不要写死 UTF-8而是从 Nmap 输出里读 encoding 声明或者在启动脚本里加 -Dfile.encodingUTF-8 和 -Dsun.jnu.encodingUTF-8两边统一后乱码问题基本消失。5.3 路径带空格ProcessBuilder 直接抛 IOException现象把包放在 C:\Program Files\Scanner 这种带空格目录下点击扫描后 Java 报 Cannot run program nmap。原因ProcessBuilder 接收的是字符串数组第一个元素是 nmap会依赖系统 PATH 解析。PATH 里没有 Nmap或者工作目录里没有 nmap.exe瞬间报错。不少人用 cmd 手工敲 nmap 是好用的进了 Java 就不行原因多半在 PATH 继承出了问题。解决在启动脚本里显式把 NMAP_HOME 加到 PATH或者 Java 侧用系统属性配置完整路径String nmapPath System.getProperty(nmap.path, C:\\Program Files (x86)\\Nmap\\nmap.exe);部署到不同机器时改启动脚本不用动代码。5.4 MySQL 账号密码写死在命令行日志里全是明文的现象用 --script-args mysql-cis.usernamescanuser,mysql-cis.passwordscanpass 扫描后发现运维平台的日志里明文记录了密码。原因命令行参数会出现在进程列表和历史记录里Linux 的 ps、Windows 的 WMIC 都能看到这不是可能泄露的问题而是一定会泄露的问题。解决NSE 脚本支持 --script-args-file 参数账号密码写到单独文件里并把文件权限设为 600。Java 侧调用时把参数从配置文件读出来再组装输出日志时对密码字段做脱敏。5.5 大批量目标时线程池爆掉内存直接 OOM现象targets.txt 里写了几百上千个目标跑起来没一会儿 Java 程序直接 OutOfMemoryError。原因很多扫描器会把所有任务一股脑提交到无界队列每个任务还持有完整的 XML 字符串内存不炸才怪。没设队列容量是第一个原因解析完的 XML 字符串没有及时释放是第二个原因。解决线程池队列设成长度上限的 LinkedBlockingQueue配合 CallerRunsPolicy解析完 XML 后立即释放引用让大字符串随方法退出被回收大批量目标分批写结果别攒在内存里最后一次性落盘。6. 把对比扫描变成习惯用 ndiff.bat 在两次扫描之间找出新增风险扫描器跑一遍拿到报告这只是做完了一半。真正能让你觉出这套工具价值的是第二遍隔一周再扫一次对比前后两个结果看出新增了哪些端口、多了哪些服务。这就是 ndiff.bat 的用武之地。ndiff 是 Nmap 自带的对比工具用法很直接ndiff scan-before.xml scan-after.xml它会输出三块内容新增的主机、主机上新增的端口、端口状态变化。我一般会把两次扫描的 XML 都保存下来然后用 ndiff 生成 diff 报告归档。比如周一全公司扫一遍下周一再扫一遍对比结果就能看出“这台服务器这周新开了一个 3389 端口”——这通常意味着有人擅自改了防火墙规则或装了远程桌面服务。一个更省事的习惯是把对比集成到定时脚本里#!/bin/bash # 每周巡检先扫再对比上周结果输出变更摘要 nmap -sT -T3 --open -oX scan-$(date %Y%m%d).xml -iL targets.txt ndiff scan-$(date -d 7 days ago %Y%m%d).xml scan-$(date %Y%m%d).xml diff-$(date %Y%m%d).txt这里 -oX 生成的 XML 文件名带日期配合 ndiff 的自然对比每周只需要翻 diff 文件就能了解整个网络的变化。这个习惯帮我省掉了大量重复翻报告的功夫——大部分机器每周不会有任何变化diff 文件通常就几行一眼看完真正需要重视的就是这几行新增的内容。从那以后我每次跑扫描之前都会强制走一遍这个流程先确认上次扫描结果在不在扫完立刻做 diff看到新增端口先查变更单查不到再上机器人工核实。希望你也能把这套 Java 漏扫系统用起来让它在内网巡检里真正发挥作用。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑