新增模块(5组29个): 反作弊扩展(6个): - AntiKillAuraService: 杀戮光环检测(攻击频率/目标切换/旋转角度) - AntiReachService: 超距攻击检测(攻击距离/位置回溯验证) - AntiSpeedService: 水平速度检测(移动速度/GroundSpoof异常) - AntiJesusService: 水上行走检测(水面/岩浆面移动验证) - AntiNoFallService: 无摔落伤害检测(落地检测/伤害验证) - AntiScaffoldService: 自动搭路检测(方块放置频率/角度/速度) 玩家行为安全(6个): - AntiNukerService: 快速破坏检测(挖矿速度/范围/模式识别) - AntiAutoClickerService: 自动点击检测(CPS统计/点击间隔熵值分析) - AntiChestStealService: 自动偷箱检测(开箱/取物速度模式识别) - AntiAutoFishService: 自动钓鱼检测(钓鱼行为时序/反应速度) - AntiInventoryManipulationService: 背包操作检测(非法丢弃/槽位异常) - AntiBaritoneService: AI寻路机器人检测(路径平滑度/行为重复率) 服务器保护(6个): - PacketFloodProtectionService: 数据包洪水防护(窗口/递增惩罚机制) - AntiSignExploitService: 告示牌漏洞防护(超长JSON/NBT/命令注入) - AntiBookBanService: 书与笔漏洞防护(超大页码/翻译键滥用/命令注入) - AntiResourcePackExploitService: 资源包漏洞防护(内网SSRF/SHA-1黑名单) - AntiTabCompleteCrashService: Tab补全崩溃防护(深度嵌套/洪水/无效UTF-8) - AntiOfflineModeSpoofService: 离线UUID欺诈防护(格式校验/IP-UUID关联) 访问控制(6个): - OPPrivilegeMonitorService: OP权限监控(权限变更/敏感命令审计) - ConfigTamperDetectionService: 配置文件篡改检测(SHA-256基线监控) - BackdoorPluginScannerService: 后门插件扫描(恶意类名/远程执行检测) - AntiVPNProxyService: VPN代理检测(托管ASN/IP范围匹配) - AntiAltAccountService: 小号检测(IP关联/行为相似度/封禁逃逸) - AntiNameSpoofService: 名称冒充检测(Unicode同形字/空白字符/编辑距离) 聊天社交安全(5个): - ChatFloodProtectionService: 聊天洪水防护(双窗口频率/相似度/递增静音) - AntiAdvertisementService: 广告检测(IP/域名/QQ群/Discord邀请正则) - AntiPhishingLinkService: 钓鱼链接检测(短链接/可疑域名/显示文本不匹配) - AntiCommandAbuseService: 命令滥用检测(洪水/插件探测/Shell注入) - PlayerPrivacyService: 玩家隐私保护(IP脱敏/GDPR合规/数据保留策略) 配置与API: - ServerGuardConfig新增29个boolean开关(默认true),全部独立可控 - application.yml新增29行YAML配置(含中文注释说明功能) - DashboardController整合29个新服务,新增60个REST API端点(总计108个端点) - 所有模块使用双构造函数(无参+@Autowired)、静态结果类工厂方法 测试:新增5个测试类164项测试,全部通过(全项目278项/17测试类/159源文件) 修复: - AntiBookBanService: helper方法改为返回int得分贡献,修复translate/score/clickEvent检测不生效 - AntiResourcePackExploitService: 修复Set.of()中.vbs重复导致ExceptionInInitializer - AntiTabCompleteCrashService: 深度嵌套得分提高至75(触发blocked阈值) - AntiOfflineModeSpoofService: 快速UUID切换得分提高至75(触发blocked阈值) - PacketFloodProtectionService: 重构检测流程,移除提前返回导致的检测屏蔽
428 lines
17 KiB
Java
428 lines
17 KiB
Java
package com.aluer.security;
|
||
|
||
import com.aluer.config.ServerGuardConfig;
|
||
import org.springframework.beans.factory.annotation.Autowired;
|
||
import org.springframework.stereotype.Service;
|
||
|
||
import java.time.Instant;
|
||
import java.util.*;
|
||
import java.util.concurrent.ConcurrentHashMap;
|
||
import java.util.concurrent.atomic.AtomicLong;
|
||
|
||
/**
|
||
* 后门插件/恶意JAR扫描 — V4.0 访问控制模块
|
||
*
|
||
* 检测原理:
|
||
* 1. 扫描服务器plugins目录下的所有JAR文件,识别已知恶意后门插件
|
||
* 2. 维护已知恶意插件名称黑名单(PluginHider, SystemX, AntiStalker, OpHider等20+后门变种)
|
||
* 3. 检测可疑插件特征:缺少plugin.yml的JAR、包含不明主类的JAR
|
||
* 4. 检测远程命令执行(RCE)嫌疑:通过分析类名和嵌入的代码字符串查找
|
||
* org.bukkit.craftbukkit.executor / java.lang.Runtime.exec 等调用
|
||
* 5. 检测JAR中隐藏的可疑配置——通过特征关键词(pass, host, port, shit, backdoor, token等)
|
||
* 推断插件可能包含硬编码后门凭据
|
||
* 6. 检测插件尝试加载字节码操作库(Reflections/ASM/Javassist等)用于隐藏自身或动态注入
|
||
* 7. 对每个已加载插件标记风险等级:SAFE(安全)、SUSPICIOUS(可疑)、MALICIOUS(恶意)
|
||
*
|
||
* 配置开关:serverguard.security.super-evolution.backdoor-plugin-scanner
|
||
*/
|
||
@Service
|
||
public class BackdoorPluginScannerService {
|
||
|
||
private final ServerGuardConfig config;
|
||
|
||
/** 已扫描插件名 -> 扫描结果 */
|
||
private final Map<String, PluginScanEntry> scannedPlugins = new ConcurrentHashMap<>();
|
||
/** 恶意插件列表 */
|
||
private final Set<String> maliciousPlugins = ConcurrentHashMap.newKeySet();
|
||
/** 可疑插件列表 */
|
||
private final Set<String> suspiciousPlugins = ConcurrentHashMap.newKeySet();
|
||
|
||
private final AtomicLong totalPlugins = new AtomicLong(0);
|
||
private final AtomicLong totalScans = new AtomicLong(0);
|
||
|
||
/** 已知恶意插件名称(20+后门变种) */
|
||
private static final Set<String> KNOWN_MALICIOUS_PLUGINS = Set.of(
|
||
"PluginHider", "SystemX", "AntiStalker", "OpHider",
|
||
"ConsoleSpamFixer", "BackdoorPlugin", "OpsGuard",
|
||
"HiddenAdmin", "ForceOP", "OPMe",
|
||
"PluginStealer", "ServerCrasher", "JarLoader",
|
||
"RemoteCmd", "SilentOP", "AdminToolz",
|
||
"MineSecure", "CraftHack", "BukkitBackdoor",
|
||
"CommandExploit", "HackLoader", "OPFinder",
|
||
"zPlugin", "xDPlugin", "SystemSpoof",
|
||
"ConfigStealer", "SessionLogger"
|
||
);
|
||
|
||
/** 可疑插件名称模式(正则,不区分大小写) */
|
||
private static final List<String> SUSPICIOUS_NAME_PATTERNS = List.of(
|
||
".*[Bb]ack[Dd]oor.*",
|
||
".*[Hh]ack.*",
|
||
".*[Ee]xploit.*",
|
||
".*[Ss]teal.*",
|
||
".*[Hh]idden.*",
|
||
".*[Ff]orce.*",
|
||
".*[Rr]emote.*",
|
||
".*[Cc]rack.*",
|
||
".*[Ii]nject.*",
|
||
".*[Pp]assword.*"
|
||
);
|
||
|
||
/** RCE相关类名关键词 */
|
||
private static final Set<String> RCE_CLASS_KEYWORDS = Set.of(
|
||
"Runtime.exec",
|
||
"ProcessBuilder",
|
||
"org.bukkit.craftbukkit.executor",
|
||
"java.lang.reflect",
|
||
"sun.misc.Unsafe",
|
||
"javax.script.ScriptEngine",
|
||
"org.yaml.snakeyaml.Yaml"
|
||
);
|
||
|
||
/** 字节码操作库特征类名 */
|
||
private static final Set<String> BYTECODE_MANIPULATION_CLASSES = Set.of(
|
||
"org.objectweb.asm",
|
||
"org.reflections",
|
||
"javassist",
|
||
"net.bytebuddy",
|
||
"org.ow2.asm",
|
||
"org.apache.bcel",
|
||
"javax.tools.JavaCompiler"
|
||
);
|
||
|
||
/** 可疑配置键关键词 */
|
||
private static final Set<String> SUSPICIOUS_CONFIG_KEYS = Set.of(
|
||
"pass", "password", "pwd",
|
||
"host", "ip", "address",
|
||
"port",
|
||
"token", "key", "secret",
|
||
"backdoor", "backdoor-enabled",
|
||
"shit", "fuck",
|
||
"admin-bypass",
|
||
"hide-commands",
|
||
"silent-mode",
|
||
"auto-op",
|
||
"webhook", "discord-webhook",
|
||
"c2-server", "c2",
|
||
"reverse-shell",
|
||
"exec", "execute-on-join"
|
||
);
|
||
|
||
/** 无参构造函数,使用默认配置 */
|
||
public BackdoorPluginScannerService() {
|
||
this(new ServerGuardConfig());
|
||
}
|
||
|
||
@Autowired
|
||
public BackdoorPluginScannerService(ServerGuardConfig config) {
|
||
this.config = config;
|
||
}
|
||
|
||
/**
|
||
* 扫描单个插件JAR(基于元数据进行分析)
|
||
* 在实际部署中,pluginClasses参数应来自JAR文件的实际类名列表
|
||
*
|
||
* @param pluginName 插件名称(来自plugin.yml的name字段)
|
||
* @param jarFileName JAR文件名
|
||
* @param hasPluginYml 是否包含plugin.yml文件
|
||
* @param mainClass 插件主类(来自plugin.yml的main字段)
|
||
* @param pluginClasses 插件JAR中包含的所有类名列表
|
||
* @param configKeys 插件config.yml中的配置键列表
|
||
* @return 扫描结果,包含风险等级和详细原因
|
||
*/
|
||
public PluginScanResult scanPlugin(String pluginName, String jarFileName,
|
||
boolean hasPluginYml, String mainClass,
|
||
List<String> pluginClasses, List<String> configKeys) {
|
||
totalScans.incrementAndGet();
|
||
|
||
// 配置开关检查
|
||
if (!config.getSecurity().getSuperEvolution().isBackdoorPluginScanner()) {
|
||
return PluginScanResult.clean(pluginName);
|
||
}
|
||
|
||
List<String> reasons = new ArrayList<>();
|
||
RiskLevel riskLevel = RiskLevel.SAFE;
|
||
|
||
if (pluginName == null || pluginName.isBlank()) {
|
||
pluginName = jarFileName != null ? jarFileName.replace(".jar", "") : "unknown";
|
||
}
|
||
|
||
// 检查1:已知恶意插件名称匹配
|
||
if (KNOWN_MALICIOUS_PLUGINS.contains(pluginName)) {
|
||
riskLevel = RiskLevel.MALICIOUS;
|
||
reasons.add("KNOWN_MALICIOUS_PLUGIN: Plugin name \"" + pluginName
|
||
+ "\" matches known backdoor/malware plugin");
|
||
maliciousPlugins.add(pluginName);
|
||
}
|
||
|
||
// 检查2:可疑插件名称模式匹配
|
||
if (riskLevel == RiskLevel.SAFE) {
|
||
for (String pattern : SUSPICIOUS_NAME_PATTERNS) {
|
||
if (pluginName.toLowerCase().matches(pattern.toLowerCase())) {
|
||
riskLevel = RiskLevel.SUSPICIOUS;
|
||
reasons.add("SUSPICIOUS_PLUGIN_NAME: \"" + pluginName
|
||
+ "\" matches suspicious naming pattern \"" + pattern + "\"");
|
||
suspiciousPlugins.add(pluginName);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 检查3:缺少plugin.yml的JAR(合法Minecraft插件必须包含plugin.yml)
|
||
if (!hasPluginYml) {
|
||
if (riskLevel.ordinal() < RiskLevel.SUSPICIOUS.ordinal()) {
|
||
riskLevel = RiskLevel.SUSPICIOUS;
|
||
}
|
||
reasons.add("MISSING_PLUGIN_YML: JAR file \"" + jarFileName
|
||
+ "\" does not contain plugin.yml — not a standard Bukkit/Paper plugin");
|
||
suspiciousPlugins.add(pluginName);
|
||
}
|
||
|
||
// 检查4:检测RCE相关类名(远程命令执行嫌疑)
|
||
if (pluginClasses != null && !pluginClasses.isEmpty()) {
|
||
for (String className : pluginClasses) {
|
||
for (String rceKeyword : RCE_CLASS_KEYWORDS) {
|
||
if (className.contains(rceKeyword)) {
|
||
if (riskLevel.ordinal() < RiskLevel.SUSPICIOUS.ordinal()) {
|
||
riskLevel = RiskLevel.SUSPICIOUS;
|
||
}
|
||
reasons.add("RCE_CAPABILITY: Class \"" + className
|
||
+ "\" indicates remote command execution capability");
|
||
suspiciousPlugins.add(pluginName);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 检查5:检测字节码操作库依赖(用于隐藏自身/动态注入)
|
||
if (pluginClasses != null && !pluginClasses.isEmpty()) {
|
||
for (String className : pluginClasses) {
|
||
for (String bcClass : BYTECODE_MANIPULATION_CLASSES) {
|
||
if (className.contains(bcClass)) {
|
||
if (riskLevel.ordinal() < RiskLevel.SUSPICIOUS.ordinal()) {
|
||
riskLevel = RiskLevel.SUSPICIOUS;
|
||
}
|
||
reasons.add("BYTECODE_MANIPULATION: Class \"" + className
|
||
+ "\" bundles bytecode manipulation library, "
|
||
+ "potentially hiding malicious behavior");
|
||
suspiciousPlugins.add(pluginName);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 检查6:检测隐藏的可疑配置键(硬编码后门凭据特征)
|
||
if (configKeys != null && !configKeys.isEmpty()) {
|
||
for (String configKey : configKeys) {
|
||
String lowerKey = configKey.toLowerCase();
|
||
for (String suspiciousKey : SUSPICIOUS_CONFIG_KEYS) {
|
||
if (lowerKey.contains(suspiciousKey)) {
|
||
if (riskLevel.ordinal() < RiskLevel.SUSPICIOUS.ordinal()) {
|
||
riskLevel = RiskLevel.SUSPICIOUS;
|
||
}
|
||
reasons.add("SUSPICIOUS_CONFIG_KEY: Config contains suspicious key \""
|
||
+ configKey + "\", possible hardcoded backdoor credential");
|
||
suspiciousPlugins.add(pluginName);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 检查7:主类为奇怪的非标准类名
|
||
if (mainClass != null && !mainClass.isBlank()) {
|
||
// 合法主类通常遵循 com.xxx.plugin.PluginMain 格式
|
||
if (!mainClass.contains(".")) {
|
||
if (riskLevel.ordinal() < RiskLevel.SUSPICIOUS.ordinal()) {
|
||
riskLevel = RiskLevel.SUSPICIOUS;
|
||
}
|
||
reasons.add("SUSPICIOUS_MAIN_CLASS: Main class \"" + mainClass
|
||
+ "\" has no package — may be obfuscated");
|
||
suspiciousPlugins.add(pluginName);
|
||
}
|
||
}
|
||
|
||
// 存储扫描结果
|
||
PluginScanEntry entry = new PluginScanEntry(pluginName, jarFileName, riskLevel,
|
||
reasons, Instant.now());
|
||
scannedPlugins.put(pluginName, entry);
|
||
|
||
if (!scannedPlugins.containsKey(pluginName)) {
|
||
totalPlugins.incrementAndGet();
|
||
}
|
||
|
||
return new PluginScanResult(pluginName, riskLevel, reasons);
|
||
}
|
||
|
||
/**
|
||
* 快速检查:仅通过插件名称判断是否为已知恶意插件
|
||
*
|
||
* @param pluginName 插件名称
|
||
* @return 扫描结果
|
||
*/
|
||
public PluginScanResult quickScan(String pluginName) {
|
||
totalScans.incrementAndGet();
|
||
|
||
if (!config.getSecurity().getSuperEvolution().isBackdoorPluginScanner()) {
|
||
return PluginScanResult.clean(pluginName);
|
||
}
|
||
|
||
if (KNOWN_MALICIOUS_PLUGINS.contains(pluginName)) {
|
||
maliciousPlugins.add(pluginName);
|
||
PluginScanEntry entry = new PluginScanEntry(pluginName, pluginName + ".jar",
|
||
RiskLevel.MALICIOUS,
|
||
List.of("KNOWN_MALICIOUS_PLUGIN: " + pluginName),
|
||
Instant.now());
|
||
scannedPlugins.put(pluginName, entry);
|
||
totalPlugins.incrementAndGet();
|
||
return new PluginScanResult(pluginName, RiskLevel.MALICIOUS,
|
||
List.of("Known malicious plugin: " + pluginName));
|
||
}
|
||
|
||
for (String pattern : SUSPICIOUS_NAME_PATTERNS) {
|
||
if (pluginName.toLowerCase().matches(pattern.toLowerCase())) {
|
||
suspiciousPlugins.add(pluginName);
|
||
PluginScanEntry entry = new PluginScanEntry(pluginName, pluginName + ".jar",
|
||
RiskLevel.SUSPICIOUS,
|
||
List.of("SUSPICIOUS_PLUGIN_NAME: " + pluginName),
|
||
Instant.now());
|
||
scannedPlugins.put(pluginName, entry);
|
||
totalPlugins.incrementAndGet();
|
||
return new PluginScanResult(pluginName, RiskLevel.SUSPICIOUS,
|
||
List.of("Suspicious plugin name: " + pluginName));
|
||
}
|
||
}
|
||
|
||
PluginScanEntry entry = new PluginScanEntry(pluginName, pluginName + ".jar",
|
||
RiskLevel.SAFE, List.of(), Instant.now());
|
||
scannedPlugins.put(pluginName, entry);
|
||
totalPlugins.incrementAndGet();
|
||
return PluginScanResult.clean(pluginName);
|
||
}
|
||
|
||
/**
|
||
* 获取指定插件的扫描结果
|
||
*
|
||
* @param pluginName 插件名称
|
||
* @return 扫描结果Map,未扫描过的插件返回null
|
||
*/
|
||
public Map<String, Object> getPluginScanInfo(String pluginName) {
|
||
PluginScanEntry entry = scannedPlugins.get(pluginName);
|
||
if (entry == null) return null;
|
||
|
||
Map<String, Object> info = new LinkedHashMap<>();
|
||
info.put("pluginName", entry.pluginName);
|
||
info.put("jarFileName", entry.jarFileName);
|
||
info.put("riskLevel", entry.riskLevel.name());
|
||
info.put("reasons", entry.reasons);
|
||
info.put("scanTime", entry.scanTime.toString());
|
||
return info;
|
||
}
|
||
|
||
/**
|
||
* 获取所有恶意插件列表
|
||
*
|
||
* @return 恶意插件名称集合
|
||
*/
|
||
public Set<String> getMaliciousPlugins() {
|
||
return new HashSet<>(maliciousPlugins);
|
||
}
|
||
|
||
/**
|
||
* 获取所有可疑插件列表
|
||
*
|
||
* @return 可疑插件名称集合
|
||
*/
|
||
public Set<String> getSuspiciousPlugins() {
|
||
return new HashSet<>(suspiciousPlugins);
|
||
}
|
||
|
||
/**
|
||
* 获取模块运行状态
|
||
*
|
||
* @return 状态Map,包含totalPlugins/suspiciousPlugins/maliciousPlugins
|
||
*/
|
||
public Map<String, Object> getStatus() {
|
||
Map<String, Object> s = new LinkedHashMap<>();
|
||
s.put("enabled", config.getSecurity().getSuperEvolution().isBackdoorPluginScanner());
|
||
s.put("totalPlugins", totalPlugins.get());
|
||
s.put("totalScans", totalScans.get());
|
||
s.put("suspiciousPlugins", suspiciousPlugins.size());
|
||
s.put("maliciousPlugins", maliciousPlugins.size());
|
||
s.put("suspiciousPluginList", new ArrayList<>(suspiciousPlugins));
|
||
s.put("maliciousPluginList", new ArrayList<>(maliciousPlugins));
|
||
|
||
// 所有已扫描插件摘要
|
||
List<Map<String, Object>> scannedList = new ArrayList<>();
|
||
scannedPlugins.forEach((name, entry) -> {
|
||
Map<String, Object> m = new LinkedHashMap<>();
|
||
m.put("pluginName", entry.pluginName);
|
||
m.put("riskLevel", entry.riskLevel.name());
|
||
m.put("reasonCount", entry.reasons.size());
|
||
scannedList.add(m);
|
||
});
|
||
s.put("scannedPluginList", scannedList);
|
||
return s;
|
||
}
|
||
|
||
// ==================== 内部数据类和枚举 ====================
|
||
|
||
/** 插件风险等级 */
|
||
public enum RiskLevel {
|
||
SAFE, // 安全:未检测到任何可疑特征
|
||
SUSPICIOUS, // 可疑:存在可疑特征,需人工审核
|
||
MALICIOUS // 恶意:匹配已知恶意插件或包含明确的后门代码特征
|
||
}
|
||
|
||
/** 插件扫描结果记录 */
|
||
private static class PluginScanEntry {
|
||
final String pluginName;
|
||
final String jarFileName;
|
||
final RiskLevel riskLevel;
|
||
final List<String> reasons;
|
||
final Instant scanTime;
|
||
|
||
PluginScanEntry(String pluginName, String jarFileName, RiskLevel riskLevel,
|
||
List<String> reasons, Instant scanTime) {
|
||
this.pluginName = pluginName;
|
||
this.jarFileName = jarFileName;
|
||
this.riskLevel = riskLevel;
|
||
this.reasons = reasons;
|
||
this.scanTime = scanTime;
|
||
}
|
||
}
|
||
|
||
/** 插件扫描检测结果 */
|
||
public static class PluginScanResult {
|
||
private final String pluginName;
|
||
private final RiskLevel riskLevel;
|
||
private final List<String> reasons;
|
||
|
||
private PluginScanResult(String pluginName, RiskLevel riskLevel, List<String> reasons) {
|
||
this.pluginName = pluginName;
|
||
this.riskLevel = riskLevel;
|
||
this.reasons = reasons;
|
||
}
|
||
|
||
public static PluginScanResult clean(String pluginName) {
|
||
return new PluginScanResult(pluginName, RiskLevel.SAFE, List.of());
|
||
}
|
||
|
||
public static PluginScanResult suspicious(String pluginName, List<String> reasons) {
|
||
return new PluginScanResult(pluginName, RiskLevel.SUSPICIOUS, reasons);
|
||
}
|
||
|
||
public static PluginScanResult malicious(String pluginName, List<String> reasons) {
|
||
return new PluginScanResult(pluginName, RiskLevel.MALICIOUS, reasons);
|
||
}
|
||
|
||
public boolean isMalicious() { return riskLevel == RiskLevel.MALICIOUS; }
|
||
public boolean isSuspicious() { return riskLevel == RiskLevel.SUSPICIOUS; }
|
||
public boolean isSafe() { return riskLevel == RiskLevel.SAFE; }
|
||
public RiskLevel getRiskLevel() { return riskLevel; }
|
||
public String getPluginName() { return pluginName; }
|
||
public List<String> getReasons() { return reasons; }
|
||
}
|
||
}
|