2025第十届 “磐石行动” java题目
字数 848 2025-09-23 19:27:38
2025第十届 "磐石行动" Java题目分析与利用
题目概述
这道Java题目包含两个部分:web-jaba_ez和web_ezyaml。主要考察Java安全相关的知识,包括反射调用、黑名单/白名单绕过、表达式注入等漏洞利用技术。
web-jaba_ez题目分析
漏洞点分析
题目提供了两个主要端点:
/job/add- 添加任务/job/run/{jobName}- 执行任务
关键代码逻辑
@PostMapping({"/job/add"})
public Map<String, Object> addJob(@RequestBody ScheduledJob job) {
Map<String, Object> result = new HashMap<>();
if (containsBlacklist(job.getInvokeTarget())) {
result.put(BindTag.STATUS_VARIABLE_NAME, "error");
result.put("message", "包含非法字符");
return result;
}
if (!containsWhitelist(job.getInvokeTarget())) {
result.put(BindTag.STATUS_VARIABLE_NAME, "error");
result.put("message", "目标不在白名单中");
return result;
}
jobs.put(job.getJobName(), job);
result.put(BindTag.STATUS_VARIABLE_NAME, "success");
result.put("message", "任务添加成功");
result.put("jobId", job.getJobName());
return result;
}
@PostMapping({"/job/run/{jobName}"})
public Map<String, Object> runJob(@PathVariable String jobName) {
Map<String, Object> result = new HashMap<>();
ScheduledJob job = jobs.get(jobName);
if (job == null) {
result.put(BindTag.STATUS_VARIABLE_NAME, "error");
result.put("message", "任务不存在");
return result;
}
try {
invokeMethod(job.getInvokeTarget());
result.put(BindTag.STATUS_VARIABLE_NAME, "success");
result.put("message", "任务执行成功");
} catch (Exception e) {
result.put(BindTag.STATUS_VARIABLE_NAME, "error");
result.put("message", e.getMessage());
result.put("stackTrace", e.getStackTrace()[0].toString());
}
return result;
}
黑名单与白名单机制
private static final String[] JOB_BLACKLIST = {
"java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
"org.springframework", "org.apache", "rmi", "ldap", "ldaps", "http", "https"
};
private static final String[] JOB_WHITELIST = {"com.jabaez.FLAG"};
private boolean containsBlacklist(String str) {
if (str == null) {
return false;
}
String lowerStr = str.toLowerCase();
for (String blackItem : JOB_BLACKLIST) {
if (lowerStr.contains(blackItem.toLowerCase())) {
return true;
}
}
return false;
}
private boolean containsWhitelist(String str) {
if (str == null) {
return false;
}
for (String whiteItem : JOB_WHITELIST) {
if (str.contains(whiteItem)) {
return true;
}
}
return false;
}
方法调用逻辑
private Object invokeMethod(String invokeTarget) throws Exception {
int hashIndex = invokeTarget.indexOf(35); // #字符
if (hashIndex == -1) {
throw new IllegalArgumentException("Invalid format, expected: className#methodName(params)");
}
String className = invokeTarget.substring(0, hashIndex);
String methodAndParams = invokeTarget.substring(hashIndex + 1);
int paramStart = methodAndParams.indexOf(40); // (字符
int paramEnd = methodAndParams.lastIndexOf(41); // )字符
if (paramStart == -1 || paramEnd == -1) {
throw new IllegalArgumentException("Invalid method format");
}
String methodName = methodAndParams.substring(0, paramStart);
String paramStr = methodAndParams.substring(paramStart + 1, paramEnd);
Class<?> clazz = Class.forName(className);
List<Object> paramValues = new ArrayList<>();
List<Class<?>> paramTypes = new ArrayList<>();
if (!paramStr.trim().isEmpty()) {
String[] params = splitParams(paramStr);
for (String str : params) {
String param = str.trim();
if (param.startsWith("'") && param.endsWith("'")) {
String value = param.substring(1, param.length() - 1);
paramValues.add(value);
paramTypes.add(String.class);
} else if (param.equals(BeanDefinitionParserDelegate.NULL_ELEMENT)) {
paramValues.add(null);
paramTypes.add(String.class);
} else if (param.matches("\\d+")) {
paramValues.add(Integer.valueOf(Integer.parseInt(param)));
paramTypes.add(Integer.TYPE);
} else if (param.equals("true") || param.equals("false")) {
paramValues.add(Boolean.valueOf(Boolean.parseBoolean(param)));
paramTypes.add(Boolean.TYPE);
} else {
paramValues.add(param);
paramTypes.add(String.class);
}
}
}
try {
Method method = clazz.getMethod(methodName, (Class[]) paramTypes.toArray(new Class[0]));
if (Modifier.isStatic(method.getModifiers())) {
return method.invoke(null, paramValues.toArray());
}
Object instance = clazz.newInstance();
return method.invoke(instance, paramValues.toArray());
} catch (NoSuchMethodException e) {
Method method2 = clazz.getDeclaredMethod(methodName, (Class[]) paramTypes.toArray(new Class[0]));
method2.setAccessible(true);
if (Modifier.isStatic(method2.getModifiers())) {
return method2.invoke(null, paramValues.toArray());
}
Object instance2 = clazz.newInstance();
return method2.invoke(instance2, paramValues.toArray());
}
}
利用思路
- 黑名单绕过:需要绕过包含
java.net.URL,javax.naming.InitialContext等危险类的检测 - 白名单满足:必须包含
com.jabaez.FLAG - 方法调用:可以通过反射调用任意类的任意方法(包括私有方法)
官方解法
上传一个.so文件,然后使用System.load进行加载
替代解法
使用EL表达式注入反弹shell:
- 构造一个包含
com.jabaez.FLAG但不触发黑名单的类名 - 调用能够执行命令的方法
- 通过反射机制执行任意代码
具体利用步骤
- 构造一个合法的类名,如
com.jabaez.FLAG${恶意代码} - 利用反射调用能够执行命令的方法
- 绕过黑名单检测(大小写、编码等方式)
- 通过EL表达式注入执行系统命令
web_ezyaml题目分析
这是一个简单的YAML反序列化题目,主要考察:
- JNDI注入
- YAML反序列化漏洞利用
- 反序列化链构造
利用思路
- 构造恶意的YAML payload
- 利用SnakeYAML的反序列化特性
- 通过JNDI注入实现RCE
总结
这道题目综合考察了以下Java安全知识:
- Java反射机制的安全风险
- 黑名单/白名单绕过的技巧
- 表达式注入漏洞
- 反序列化漏洞利用
- JNDI注入攻击
关键点在于理解反射调用的危险性,以及如何绕过安全限制执行任意代码。通过分析黑名单和白名单的限制,找到合适的类和方法进行利用,最终实现命令执行或反弹shell。