ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于Java的漏洞扫描系统毕业设计:从端口扫描到POC插件实战

基于Java的漏洞扫描系统毕业设计:从端口扫描到POC插件实战 简介这份资源是面向高校计算机与网络安全相关专业学生的毕业设计完整项目包主题为基于Java实现的漏洞扫描系统适合正在准备毕设、需要参考网络攻防实战代码与工程结构的学习者。压缩包共927个文件约33.11MB以nse与lua脚本、class字节码、xml配置、jar依赖、java源码及txt说明为主另含少量exe、dll与properties文件整体呈现出扫描引擎、插件脚本与运行环境相结合的工程形态。项目围绕网络扫描、爬虫抓取、SQL注入与XSS等常见漏洞检测、报告生成及并发处理等模块展开可帮助读者理解漏洞扫描器的架构设计与实现思路。目前已有204人学习下载适合作为毕设选题参考、代码研读与二次开发的实践素材。1. 从一份“基于Java的漏洞扫描系统”说起它到底在扫什么很多同学拿到“基于Java的漏洞扫描系统”这个毕业设计题目时第一反应是去搜“Java漏洞扫描系统源码”然后复制一份跑起来交差。但真正动手做过的人都知道这类系统最核心的问题不是“能不能跑”而是“扫得准不准、扫得动多少、误报怎么压”。我见过太多答辩现场被老师一句“你这个扫描器扫出来的结果误报率多少跟Nessus比差在哪”问得哑口无言的场景。这个题目的本质是用Java构建一个能对目标资产做安全检测的工具。它通常包含几个模块目标发现端口扫描、服务识别、漏洞检测基于规则或插件、结果输出报告生成。适合谁做适合计算机、信息安全、软件工程专业的本科或硕士毕业生也适合想入门安全工具开发的Java开发者。它不需要你从零写一个Nmap但需要你理解扫描的基本原理并用Java把流程串起来。接下来我会按“先立住理论、再动手复现、最后避坑”的顺序把这个系统从选型到落地讲透。2. 漏洞扫描系统的技术选型与核心模块拆解2.1 为什么用Java而不是Python从并发模型和生态说起安全圈里Python是主流Pocsuite、Sqlmap都是Python写的。但毕业设计选Java往往是因为学校课程以Java为主或者导师要求用Java Web技术栈。这其实不是劣势Java在并发处理和工程化上有自己的优势。漏洞扫描的本质是大量网络IO操作Java的NIO和线程池能很好地支撑高并发扫描。比如你要扫一个C段254个IP每个IP扫100个端口就是25400次连接尝试。用单线程跑每次超时1秒得跑7个小时。用Java的线程池50个并发几分钟就能出结果。常见做法是用java.util.concurrent包下的ThreadPoolExecutor配合Socket或HttpURLConnection做探测。如果要做更底层的包构造可以用java.net.DatagramSocket发UDP包或者引入第三方库如pcap4j做原始套接字。但毕业设计不建议搞太底层用现成的HTTP客户端和Socket就够了。选型上Web框架用Spring Boot最省事前端用Thymeleaf或Vue都行。数据库用MySQL存扫描任务和结果。插件引擎可以用Java的SPI机制或者简单的反射加载。下面这张表是我一般会建议的模块划分模块技术选型作用目标管理Spring Boot MySQL录入IP、域名、URL端口扫描Java Socket 线程池探测开放端口服务识别HTTP请求 Banner抓取判断Web服务、中间件漏洞检测规则引擎 POC插件匹配漏洞特征报告生成POI FreeMarker输出Word/HTML报告2.2 端口扫描模块用Java Socket实现一个可用的TCP连接扫描端口扫描是漏洞扫描的第一步。最常用的是TCP全连接扫描直接调Socket.connect()连上了就是开放。优点是实现简单、不需要管理员权限缺点是慢、容易被目标记录日志。但毕业设计够用了。下面是一个最小可用的扫描核心代码import java.net.InetSocketAddress; import java.net.Socket; import java.util.concurrent.*; public class PortScanner { // 线程池大小根据目标数量和网络质量调整 private static final int THREAD_POOL_SIZE 50; // 连接超时时间毫秒。内网可设200公网建议1000-2000 private static final int CONNECT_TIMEOUT 1000; public static void scan(String ip, int startPort, int endPort) { ExecutorService executor Executors.newFixedThreadPool(THREAD_POOL_SIZE); for (int port startPort; port endPort; port) { final int p port; executor.submit(() - { try (Socket socket new Socket()) { socket.connect(new InetSocketAddress(ip, p), CONNECT_TIMEOUT); System.out.println([OPEN] ip : p); } catch (Exception e) { // 连接失败说明端口关闭或过滤忽略 } }); } executor.shutdown(); try { executor.awaitTermination(10, TimeUnit.MINUTES); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } public static void main(String[] args) { scan(127.0.0.1, 1, 1024); } }这段代码的逻辑很直白给每个端口提交一个任务任务里尝试建立TCP连接成功就打印开放。参数说明THREAD_POOL_SIZE控制并发度设太大容易把目标打挂或者被防火墙封IP设太小扫得慢。CONNECT_TIMEOUT是关键内网可以设200毫秒公网设1000到2000毫秒再长就浪费时间。注意try-with-resources确保Socket关闭不然文件描述符会耗尽。实际项目中我会把扫描结果存到一个ConcurrentHashMap或者阻塞队列里再由消费者线程写入数据库。不要在每个扫描线程里直接写库数据库连接池扛不住。2.3 漏洞检测引擎基于POC插件的规则匹配怎么做端口扫完知道哪些端口开了下一步是判断有没有漏洞。毕业设计里最常见的做法是写一批POCProof of Concept插件每个插件对应一个漏洞比如“Tomcat弱口令”、“Struts2远程命令执行”、“SQL注入检测”。插件用Java类实现通过反射动态加载。先定义一个插件接口public interface PocPlugin { // 插件名称如 tomcat-weak-password String getName(); // 检测方法传入目标URL或IP端口返回是否存在漏洞 boolean check(String target); }然后写一个具体的弱口令检测插件import java.net.HttpURLConnection; import java.net.URL; import java.util.Base64; public class TomcatWeakPasswordPoc implements PocPlugin { Override public String getName() { return tomcat-weak-password; } Override public boolean check(String target) { // target格式如 http://192.168.1.1:8080/manager/html String[] credentials {admin:admin, tomcat:tomcat, root:root}; for (String cred : credentials) { try { URL url new URL(target); HttpURLConnection conn (HttpURLConnection) url.openConnection(); String encoded Base64.getEncoder().encodeToString(cred.getBytes()); conn.setRequestProperty(Authorization, Basic encoded); conn.setConnectTimeout(2000); conn.setReadTimeout(2000); int code conn.getResponseCode(); if (code 200) { return true; // 登录成功存在弱口令 } } catch (Exception e) { // 忽略异常继续尝试下一个口令 } } return false; } }逻辑说明这个插件尝试用常见的Tomcat管理后台口令去登录如果返回200就说明弱口令存在。参数上setConnectTimeout和setReadTimeout都要设不然遇到慢目标会卡死。credentials数组可以扩展但不要放太多否则扫描时间线性增长。插件加载用Java的SPI或者简单的目录扫描import java.io.File; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.List; public class PluginLoader { public static ListPocPlugin loadPlugins(String pluginDir) throws Exception { ListPocPlugin plugins new ArrayList(); File dir new File(pluginDir); File[] jars dir.listFiles((d, name) - name.endsWith(.jar)); if (jars null) return plugins; for (File jar : jars) { URLClassLoader loader new URLClassLoader(new URL[]{jar.toURI().toURL()}); // 假设每个jar里有一个poc.properties指定主类 // 这里简化处理实际可用ServiceLoader } return plugins; } }这段加载代码是骨架实际用ServiceLoader更规范。在META-INF/services下放接口全限定名文件里面写实现类全限定名然后ServiceLoader.load(PocPlugin.class)就能自动发现。这样新增POC只需要丢一个jar进去不用改代码。2.4 扫描结果存储与报告生成从数据库到Word的完整链路扫描结果要存下来才能生成报告。表结构设计至少三张表scan_task任务、scan_result端口结果、vuln_result漏洞结果。用JPA或MyBatis都行毕业设计用Spring Data JPA最省事。Entity Table(name vuln_result) public class VulnResult { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; private Long taskId; private String target; private String vulnName; private String severity; // HIGH, MEDIUM, LOW private String description; // getter/setter省略 }报告生成用Apache POI写Word。很多同学搜“java poi word能生成图表吗”答案是能但比较复杂。毕业设计建议先用POI生成表格和文字图表可以用POI的XWPFChart或者干脆生成HTML报告再用浏览器打印成PDF。下面是一个生成Word表格的片段import org.apache.poi.xwpf.usermodel.*; import java.io.FileOutputStream; public class ReportGenerator { public static void generate(String outputPath, ListVulnResult results) throws Exception { XWPFDocument doc new XWPFDocument(); XWPFParagraph title doc.createParagraph(); title.setAlignment(ParagraphAlignment.CENTER); XWPFRun run title.createRun(); run.setText(漏洞扫描报告); run.setBold(true); run.setFontSize(18); XWPFTable table doc.createTable(results.size() 1, 4); // 表头 table.getRow(0).getCell(0).setText(目标); table.getRow(0).getCell(1).setText(漏洞名称); table.getRow(0).getCell(2).setText(危险等级); table.getRow(0).getCell(3).setText(描述); // 填充数据 for (int i 0; i results.size(); i) { VulnResult r results.get(i); table.getRow(i 1).getCell(0).setText(r.getTarget()); table.getRow(i 1).getCell(1).setText(r.getVulnName()); table.getRow(i 1).getCell(2).setText(r.getSeverity()); table.getRow(i 1).getCell(3).setText(r.getDescription()); } try (FileOutputStream out new FileOutputStream(outputPath)) { doc.write(out); } doc.close(); } }参数说明results是从数据库查出来的漏洞列表。createTable的行数是结果数加1表头。POI的表格样式需要额外设置不然很丑但功能是完整的。生成HTML报告更简单用FreeMarker模板引擎把数据塞进模板就行这里不展开。3. 从零跑通一个最小扫描系统环境、依赖与启动步骤3.1 开发环境与依赖清单要跑通这个系统本地需要装JDK 8或11Spring Boot 2.x对JDK 8兼容最好Maven 3.6以上MySQL 5.7或8.0。IDE用IntelliJ IDEA社区版就够。依赖方面核心是这几个dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-jpa/artifactId /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.28/version /dependency dependency groupIdorg.apache.poi/groupId artifactIdpoi-ooxml/artifactId version5.2.3/version /dependency /dependencies版本号不要乱写POI 5.x和JDK 8兼容MySQL驱动8.0.28是稳定版。如果学校要求用JDK 17Spring Boot要升到3.x但很多老教程不兼容建议还是JDK 8或11。3.2 数据库建表与配置在MySQL里建库vuln_scanner然后执行建表语句CREATE TABLE scan_task ( id BIGINT AUTO_INCREMENT PRIMARY KEY, target VARCHAR(255) NOT NULL, start_time DATETIME, end_time DATETIME, status VARCHAR(50) ); CREATE TABLE vuln_result ( id BIGINT AUTO_INCREMENT PRIMARY KEY, task_id BIGINT, target VARCHAR(255), vuln_name VARCHAR(255), severity VARCHAR(20), description TEXT, FOREIGN KEY (task_id) REFERENCES scan_task(id) );application.yml配置spring: datasource: url: jdbc:mysql://localhost:3306/vuln_scanner?useSSLfalseserverTimezoneUTC username: root password: yourpassword driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update show-sql: trueddl-auto: update会自动根据实体类建表但生产环境不要用。serverTimezoneUTC不加会报时区错误这是血泪经验。3.3 启动扫描任务的完整流程写一个Controller触发扫描RestController RequestMapping(/api/scan) public class ScanController { Autowired private ScanService scanService; PostMapping(/start) public String start(RequestParam String target, RequestParam int startPort, RequestParam int endPort) { // 异步执行避免阻塞HTTP请求 new Thread(() - scanService.scan(target, startPort, endPort)).start(); return 扫描任务已提交; } }ScanService里先插一条scan_task记录然后调端口扫描再对开放端口跑POC插件最后把结果写vuln_result。整个流程串起来后访问http://localhost:8080/api/scan/start?target127.0.0.1startPort1endPort1024就能触发。注意用新线程跑不然HTTP请求会超时。3.4 验证扫描结果是否正确跑完之后查数据库vuln_result表看有没有数据。也可以写一个简单的查询接口返回JSON。验证端口扫描是否准确可以用telnet或nc手动确认几个端口。验证POC是否有效可以本地起一个Tomcat设个弱口令看插件能不能扫出来。这一步是答辩时老师最可能让你现场演示的一定要提前准备好靶机环境。4. 避坑与常见问题扫描器开发中容易翻车的五个点4.1 扫描线程太多导致目标服务崩溃现象扫描一个网站时目标直接502或者响应超时甚至把内网交换机打挂。原因线程池设得太大比如开了500个并发瞬间发出大量TCP连接目标服务器或网络设备扛不住。解决把THREAD_POOL_SIZE控制在50以内公网目标建议20。加一个Semaphore做限流或者用RateLimiter控制每秒请求数。扫描前最好跟目标方打个招呼毕业设计扫自己搭的靶机最安全。4.2 误报率高把正常服务当成漏洞现象报告里一堆“漏洞”但人工验证发现都是误报。比如把HTTP 200当成弱口令成功其实返回的是登录失败页面。原因POC的判定条件太粗糙只看状态码不看响应内容。解决在插件里加特征匹配。比如弱口令检测不能只看200还要看响应里有没有“管理后台”、“注销”等关键词。SQL注入检测要对比注入前后页面差异。误报是扫描器的天敌宁可漏报不可误报。4.3 数据库连接池耗尽现象扫描跑一会儿就报“Connection is not available”或者“Too many connections”。原因每个扫描线程都去拿数据库连接线程多了连接池不够用。解决扫描线程只负责探测把结果放到BlockingQueue单独起一个消费者线程批量写库。或者用Async配合默认的简单连接池但最好还是生产者-消费者模式。HikariCP的默认最大连接数是10可以调到20但不要太大。4.4 报告生成时中文乱码现象生成的Word文档里中文变成方块或乱码。原因POI默认字体不支持中文或者输出流编码不对。解决在XWPFRun里设置字体run.setFontFamily(宋体)并且确保FileOutputStream没有指定错误编码。如果生成HTML在meta里加charsetUTF-8。这个坑几乎每个人都会踩一次。4.5 扫描任务无法停止现象点“停止扫描”没反应任务还在跑。原因线程池提交的任务没有检查中断标志或者Socket.connect阻塞时不会响应中断。解决给每个扫描任务加一个volatile boolean running标志在循环里检查。对于Socket连接设置超时时间超时后自然返回。不要试图用Thread.stop()那是废弃方法。优雅停止的做法是executor.shutdownNow()配合中断检查。5. 进阶技巧用插件热加载和扫描策略优化提升系统可用性走到这里一个能跑的漏洞扫描系统已经成型了。但答辩时如果想拿高分还得在“可扩展性”和“扫描效率”上做点文章。我一般会从两个方向入手插件热加载和扫描策略优化。插件热加载的核心是让系统在不重启的情况下加载新的POC。前面提到的ServiceLoader是标准做法但它要求jar包在classpath里。更灵活的方式是用URLClassLoader动态加载指定目录下的jar然后通过反射实例化。下面是一个可用的热加载实现import java.io.File; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.List; import java.util.ServiceLoader; public class HotPluginLoader { private ListPocPlugin plugins new ArrayList(); public void loadFromDir(String dirPath) throws Exception { File dir new File(dirPath); File[] jars dir.listFiles((d, name) - name.endsWith(.jar)); if (jars null) return; ListURL urls new ArrayList(); for (File jar : jars) { urls.add(jar.toURI().toURL()); } URLClassLoader loader new URLClassLoader(urls.toArray(new URL[0]), Thread.currentThread().getContextClassLoader()); ServiceLoaderPocPlugin serviceLoader ServiceLoader.load(PocPlugin.class, loader); plugins.clear(); for (PocPlugin plugin : serviceLoader) { plugins.add(plugin); } } public ListPocPlugin getPlugins() { return plugins; } }这段代码的关键是URLClassLoader的父加载器设为当前线程的上下文类加载器不然SPI文件找不到。每次调用loadFromDir会重新加载所有jar实现热更新。参数上dirPath是插件目录建议放在项目外的plugins/文件夹方便替换。注意每次加载会创建新的类加载器旧的可能无法回收毕业设计场景下不用太在意内存泄漏。扫描策略优化方面我习惯把扫描分成三轮第一轮快速扫常见端口21,22,80,443,8080等第二轮对开放端口做服务识别第三轮只对识别出的服务跑对应的POC。这样比无差别全端口扫描快得多。可以用一个MapInteger, ListPocPlugin做端口到插件的映射比如80端口只跑Web类POC3306端口只跑MySQL弱口令POC。验证优化效果的方法很简单拿同一个目标对比优化前后的扫描耗时和漏洞发现数量。我实测过一个C段目标优化前全端口扫描加全插件跑要40分钟优化后8分钟出结果漏洞发现数量不变。这个数据放在答辩PPT里很有说服力。最后说一个我自己的习惯每次写完一个POC先拿本地靶机验证三遍确认没有误报再放进插件目录。扫描器这东西宁可少报几个漏洞也不要让报告里全是误报。希望帮到你。本文还有配套的精品资源点击获取
返回列表