前言
JNDI注入在高版本下显得比较鸡肋,不过基于本地Factory的利用方式还是值得学习的。
Tomcat-BeanFactory
比较出名的可利用Factory,而且Tomcat使用范围比较广。
该类中的getObjectInstance函数可以调用一个对象的函数,要求满足三个条件。
1
| Object bean = beanClass.getConstructor().newInstance();
|
1 2 3
| Method method = forced.get(propName); ... method.invoke(bean, valueArray);
|
1 2 3 4
| Class<?> paramTypes[] = new Class[1]; paramTypes[0] = String.class; ... forced.put(param, beanClass.getMethod(setterName, paramTypes));
|
- 其实还有一些隐含的条件,比如它的bean在通过无参构造函数实例化后,到调用该函数之间没有其他操作,也就是说可控输入就只剩下参数了(或许可以通过多次调用提前控制一些成员)。又比如它无法进行链式调用,所以反射链难以实现,要找到利用方式基本只有那种输入命令->执行,或者ClassLoader的奇妙玩法了。
存在漏洞或危险操作的依赖
通过ELProcessor、GroovyShell、Yaml、MVEL、Xstream、bsh等存在漏洞的工具的玩法。
Mlet
MLet是一个ClassLoader,继承自URLClassLoader,其public函数addURL可以通过一个字符串URL定义远程资源:
1 2 3 4 5 6 7 8 9
| public void addURL(String url) throws ServiceNotFoundException { try { URL ur = new URL(url); if (!Arrays.asList(getURLs()).contains(ur)) super.addURL(ur); } catch (MalformedURLException e) { ... } }
|
远程类需要加载并初始化才能执行其中的静态代码块,问题在于我们通过BeanFactory实例化并调用的Mlet是一个与环境隔离的ClassLoader,我们无法通过这里之外的加载/实例化点完成利用,因为环境中使用的ClassLoader不是这个Mlet。
而在Mlet类中,可以调用的loadClass函数来自父类的父类ClassLoader,而loadClass在加载失败时会调用findClass从资源中定义类:
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
| public Class<?> loadClass(String name) throws ClassNotFoundException { return loadClass(name, false); }
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } ...
if (c == null) { long t1 = System.nanoTime(); c = findClass(name);
... } } ... } }
protected Class<?> findClass(String name) throws ClassNotFoundException { return findClass(name, currentClr); }
Class<?> findClass(String name, ClassLoaderRepository clr) throws ClassNotFoundException { Class<?> c = null; MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), "findClass", name); try { c = super.findClass(name); ... } }
protected Class<?> findClass(final String name) throws ClassNotFoundException { final Class<?> result; try { result = AccessController.doPrivileged( new PrivilegedExceptionAction<Class<?>>() { public Class<?> run() throws ClassNotFoundException { String path = name.replace('.', '/').concat(".class"); Resource res = ucp.getResource(path, false); if (res != null) { try { return defineClass(name, res); } ... } else { return null; } } }, acc); } ... }
|
然而loadClass的第二个参数固定为false,也就是说仅仅是加载也无法执行静态代码块,必须要走到实例化的步骤才行。而ClassLoader中自然是不会有实例化类的操作的,所以这种方式也就走不通了。
NashornScriptEngineFactory
有无参构造函数:
1 2
| public NashornScriptEngineFactory() { }
|
有危险函数:
1 2 3
| public ScriptEngine getScriptEngine(String... args) { return this.newEngine((String[])Objects.requireNonNull(args), getAppClassLoader(), (ClassFilter)null); }
|
可以链式利用:
1
| new NashornScriptEngineFactory().getScriptEngine("js").eval("java.lang.Runtime.getRuntime().exec('calc')");
|
然而BeanFactory似乎实现不了链式调用,寄。
Mvel
一种表达式,需要依赖:
1 2 3 4 5 6
| <dependency> <groupId>org.mvel</groupId> <artifactId>mvel2</artifactId> <version>2.4.14.Final</version> </dependency>
|
MVEL类没有无参构造函数,但是有很多静态public函数,比如eval、evalToString和evalToBoolean,可以在mvel2包里面其他public类中找一找有没有调用这些静态函数的地方。
单纯用文本搜索做起来会比较麻烦,因为调用了eval的地方实在有点多,看起来很麻烦,如果用codeQL等工具污点分析就会方便很多,详见参考文章。
ShellSession类有一个无参构造函数:
1 2 3
| public ShellSession() { ... }
|
其exec函数只有一个String类型参数:
1 2 3 4 5 6
| public void exec(String command) { for (String c : command.split("\n")) { inBuffer.append(c); _exec(); } }
|
其_exec函数会调用内部定义好的Command们的execute函数:
1 2 3
| try { commands.get(inTokens[0]).execute(this, passParameters); }
|
PushContext的execute函数中会调用MVEL类的eval函数:
1 2 3 4
| public Object execute(ShellSession session, String[] args) { session.setCtxObject(MVEL.eval(args[0], session.getCtxObject(), session.getVariables())); return "Changed Context"; }
|
MemoryUserDatabaseFactory
继承了ObjectFactory接口,其getObjectInstance函数中会实例化一个MemoryUserDatabase对象,该对象可以设置几个属性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| MemoryUserDatabase database = new MemoryUserDatabase(name.toString()); RefAddr ra = null;
ra = ref.get("pathname"); if (ra != null) { database.setPathname(ra.getContent().toString()); }
ra = ref.get("readonly"); if (ra != null) { database.setReadonly(Boolean.parseBoolean(ra.getContent().toString())); }
ra = ref.get("watchSource"); if (ra != null) { database.setWatchSource(Boolean.parseBoolean(ra.getContent().toString())); }
|
然后调用其open和save函数,分别可以触发xxe和tomcat下的rce漏洞。
JDBC
详见参考文章。
参考
https://xz.aliyun.com/t/10829
https://tttang.com/archive/1405/