While performing security assessments, as penetration tests and red team activities, IBM WebSphere was one of the enterprise products we encountered the most: as such, we decided to carry out a deep analysis of this product, given how common it is in enterprise contexts. Even though enterprises extensively use this software, the technical documentation is poor: despite making the analysis more complex, it made them far more interesting. That is where our journey starts.
Websphere is a collection of Java modules created by IBM that allows the management of enterprise applications. The core component is called WebSphere Application Server (WAS): by this component it is possible to expose Java web applications or servlet applications (not limited to Java-based).
WebSphere Application Server could be seen as a web application server on the surface, but more in depth it works as a middleware framework that hosts web applications. WebSphere is built using open standards such as Java EE, XML, and Web Services. It is multi-platform so it could be run on Windows, Linux, Solaris, etc. Moreover, Websphere platform is designed as a distributed computing platform that could be installed on multiple machines, usually known as WebSphere cell. The management of all cells could be done from a management node, the so called Deployment Manager. The configuration information for the entire cell are stored into an XML configuration files that are distributed throughout the cells of every node. It works with a number of Web servers including Apache HTTP Server, Microsoft IIS, and many others. As default, it uses port 9060 for connecting to the administration console and port 9080 as the default website publication port. WebSphere supports standard interfaces like Common Object Request Broker Architecture (CORBA) and Java Database Connectivity (JDBC).
There are several versions of WebSphere: given that some of those are more vulnerable, this should be taken into account during a security assessment. In fact, available CVEs for a specific version (even with same release and codebase number) might not be compatible on other versions or architectures.
In order to be able to analyze and perform a successful penetration test against WebSphere, it is necessary to understand the overall structure and how the main components work.
The CORBA standard stays high on our priority list, since there are many CVEs exploiting it and we need a deeper grasp on how things work at lower level. This short analysis will help in understanding the next steps we are going to see in this post (e.g. enumeration phase)
CORBA is an acronym for Common ORB Architecture. It is simply a technical standard for an ORB (Object Request Broker), which is an object-oriented version of RPC (Remote Call Procedure). An ORB is a mechanism for invoking operations on an object in a different remote process that may be running on the same or a different server. Many people refer to CORBA as middleware or integration software because CORBA is often used to get existing, standalone applications communicating with each other. One of CORBA’s key points is that it is distributed middleware. In particular, it allows applications to talk to each other even if the applications are:
CORBA is also thought to be object-oriented. This means that a client does not make calls to a server process: instead, a CORBA client makes calls to objects (available on the target server)
By Naming Service we mean services that store information in a central place, which enables users, machines, and applications to communicate across the network. This information can include the following:
A known example of a Naming Service is the DNS service.
Without a central naming service, each machine would need to keep its copy of this information. Naming service information can be stored in files, maps, or database tables. Centrally locating this data makes it easier to administer large networks. Naming services are fundamental to any computing network. Among other features, naming service provide functionality doing the following tasks:
A network information service enables machines to be identified by common names instead of numerical addresses. This makes communication simpler because users do not have to remember and try out cumbersome numerical addresses like an IP address.
As we did with the CORBA standard, we now need to understand how GIOP (General Inter-ORB Protocol) works since it is a relevant protocol in order to perform WebSphere version enumeration or other types of attacks. Among the most important goals of this protocol are:
GIOP specifications are composed of the following elements:
CDR (Common Data Representation): a transfer syntax mapping OMG IDL data types into a bicanonical low-level representation for “on-the-wire” transfer between ORBs and Inter-ORB bridges.
GIOP Transport Assumptions: general assumptions made concerning any network transport layer that may be used to transfer GIOP messages. The specification also describes how connections may be managed, and constraints on GIOP message ordering.
The IIOP specification adds the following element to the GIOP specification:
Now that we have a very basic understanding of the common components and protocols of Websphere, we can start with basic enumeration activities. As we saw in the first part of the post, version enumeration is fundamental to find exploitable CVE.
In order to verify any WebSphere installation, as a rule of thumb we should start with a port scan. WebSphere exposes lots of different services: for this reason it is very easy to recognize the presence of this technology. Particular attention shall be paid to identifying the following default ports: 11003, 11004, 11005, 11006, 11008, 9043, 9060, 9080, 7061
Since WebSphere uses the GIOP protocol on several ports, its presence could be a further evidence that such technology is in use.
In order to identify this technology we can execute the custom scripts related to GIOP with nmap. Command example:
nmap -sT -sV -p - -T4 -O -v -n -Pn --reason --script giop-info --script-trace 127.0.0.1
Regarding the CORBA services, it should be noted that in a default installation they run on these TCP ports: 2809, 9100, 9402, 9403
It is also possible identify common Websphere installations by checking exposed HTTP/HTTPS services. By default Websphere exposes the administration console on this path: /ibm/console/. Furthermore, is possible to proceed with a bruteforce attack on this console: the default user on WebSphere is wsadmin.
Once the presence of a WebSphere installation has been determined, we still need to identify the version and the build number before exploiting any CVE. In order to identify the build version of Websphere, we need to think of some tricks to exploit pre-existing profiling modules, as the ones employed by Nessus, OpenVAS or nmap.
In fact, even while using the verbose flag (es: –script-trace for nmap), we are not able to identify precisely the version in use of WebSphere. Why is that?
By analyzing these modules, we saw that all of them are almost the same and operate doing such similar checks:
[pattern = "^WebSphere Application Server/([0-9.]+)($|[^0-9.])";]
[matches = pregmatch(pattern:"^.*WASRemoteRuntimeVersion=.([0-9.]+).*$", string:line);]
In order to verify if a Websphere installation is vulnerable or not we need to know the build version. Starting from the nessus plugin WebSphere_detect.nasl, we understood the reason it could not identify the correct version. This module, like the others, sends an initialization GIOP packet, but for some reason it could not identify the WebSphere version from the server response. To explore this, we generated a new initialized packet using the WebSphere libraries, but that did not help. Finally, after a deeper investigation, we understood that this happens because Websphere may send split the response in different packets; all the modules we analyzed stopped after the first received one, like this:
data = recv(socket:s, length:4096);
To overcome this problem, we created a basic python script. In order to identify the build number using the common modules, it is possible to run nmap after having modified the script giop-info, in which the port 9100 should be added, and analyze those packets with a packet sniffer like wireshark.
On Windows, unfortunately, the nmap plugin does not seem to be able to read the response even after such modifications.
After a much needed introduction, we finally got to the most exciting part of the article! We will now analyze some critical CVEs that we used during some of our engagement, explaining how we analyzed them and how we reversed some WebSphere patch, and then finally exploiting the target.
First Case Study: From Patch to Exploit
In one occasion we found that our Websphere target was vulnerable to CVE-2019-4505:
IBM WebSphere Application Server Network Deployment could allow a remote attacker to obtain sensitive information, caused by sending a specially-crafted URL. This can lead the attacker to view any file in a certain directory.
Unfortunately, there is no documentation or PoC available on the web. :(
Given the potential outcome of exploiting such vulnerability we decided to investigate further, as to better understand if the host that we were testing was susceptible or not to such CVE. After diving into the IBM portal, we were able to download the patch and luckily for us the diff (only 3 lines) has shown that the vulnerability exists only in particular configurations of WebSphere: Intelligent Management Service. From a deeper analysis, we saw that it is possible to exploit that vulnerability in case the Intelligent Management Service is enabled, which runs by default on 7061 port.
Activating the services through the UI of WebSphere is quite unpleasant, so we decided to proceed with a custom initialization by reverse engineering the whole process. As we finished such process and we have completed the initialization, we could finally access the vulnerable service FileXService and test the CVE.
In order to test it out in a local environment we used this portion of the code that abuse the WebSphere internal libs:
Properties xs = new Properties(); ServicesComponentImpl servicesComponent = new ServicesComponentImpl(); HttpServiceConfig ht = new BaseHttpServiceConfig(); servicesComponent.initService(ht); servicesComponent.addXServices(xs); servicesComponent.startService(); BaseHttpServiceConfig o = new BaseHttpServiceConfig(); HttpInboundComponentImpl httpInboundComponent = new HttpInboundComponentImpl(); httpInboundComponent.initService(o); httpInboundComponent.startService();
Using the above piece of the code we were able to start a standalone instance of the Intelligent Management Service and testing how to exploit the CVE locally.
As posted in the image, the exploitation is quite simple and we had the option to read local resources. From here, it is possible to reach sensitive information like the console administration password and gain a Remote Code Execution.
Second Case Study: CVE-2019-4279 - Porting exploit for *nix
Another common CVE that could be abused goes with the ID CVE-2019-4279:
There is a remote code execution vulnerability in WebSphere Application Server Network Deployment address by CVE-2019-4279.
The ports 1100[2,3,4,5,6,7] are employed by WebSphere ND (ND stands for Network Deployment) to communicate and exchange data between the listening services. The mentioned CVE exploits a vulnerability inside one of the input validation controls: an attacker may exploit this to execute binaries or commands on the server. The used ports are exposed by default.
In details, the vulnerability targets the “Management Overlay TCP Port” service exposed by the Websphere ND’s cluster management node: command execution could be achieved creating a management node profile due to untrusted data deserialization.
To exploit this vulnerability there are several ways but only one PoC is publicly available. The PoC is made by a metasploit module that works only for windows instances. Given that our target consisted of a nix machine, we had to figure out how the exploit worked in order to make some adaptations.
While debugging the exploit on a GNU/Linux machine, we identified that the command execution lines were correctly reached, but the RCE was failing for some (yet) unknown reason. Time to dig deeper to adapt the exploit to run on Linux!
From a general perspective, the exploit works by executing a file with arbitrary content by the means of the java method Runtime.getRuntime().exec(). The file’s contents are controlled by the attacker; thus, when such file is written on UNIX systems its default permissions are the following:
As you can see, the execution permissions are missing: therefore the call Runtime.getRuntime() exec is going to fail. During the analysis we found a way to bypass this behavior by changing the way our commands are executed.
Original exploit code:
enc_stream = construct_bcast_task_msg(node_port, "..\\..\\..\\" + bin_name, payload_contents, bin_name);
enc_stream = construct_bcast_task_msg(node_port, "ksh", "ping -n1 XXX.burpcollaborator.com", "ksh");
At this point, using the ksh binary we can execute the contents of the file called “ksh” with our custom payload as the arbitrary command of our choice. In this way, we got a shell on the system and ported an exploit available only for Windows systems.
Third Case Study - CVE-2020-4450: Bonus flag for the Exploitation
Deserialization vulnerabilities are pretty common in Java application, and even this vulnerability exploits a deserialization bug. The CVE-2020-4450 concerns a critical vulnerability that we exploited in one of our particular security assessments:
IBM WebSphere Application Server traditional could allow a remote attacker to execute arbitrary code on the system with a specially-crafted sequence of serialized objects.
This CVE exploits an insecure deserialization of a stream of bytes that could be controlled by an attacker. This vulnerability affects non ND installation of WebSphere only. Given the severity of this vulnerability, we suggest to consider it if you are doing a Penetration Test of a WAS infrastructure. For future needs, we also developed an internal PoC. Let’s dive a little bit more into this vulnerability.
IIOP request data is processed by the receive_request method. During the processing phase, when the ServiceContext object is not empty, demarshalContext is used to retrieve ServiceContext from a byte stream that could be controlled by an attacker. Arbitrary objects embedded within this byte stream are extracted by calling a method that makes use of the readObject() function.
Even if is possible to control the object that will be deserialized, it is not so easy to achieve an RCE: IBM Java SDK implements many defenses to tackle deserialization vulnerabilities. As a quick example, IBM SDK does not make use of the Oracle JDK’s for the JNDI (Java Naming and Directory Interface). This means that it is not vulnerable to remote class loading through RMI/LDAP.
Anyway, as highlighted in the ZDI blog post relative to this vulnerability, it is possible to bypass the in place mitigations utilizing WSIFPort_EJB as the entry point.
For the next steps, first we have to let WebSphere execute to the point of deserialization by constructing the sent data. Then, due to the limitations of the IBM JAVA SDK itself, we cannot abuse JNDI to load classes through RMI/LDAP, as we said before; we need to find a class that implements ObjectFactory locally. If we were able to find such a class that parses the Reference, loads and parses a malicious WSDL file, we would be mostly done. After that, the final step would be invoking the eval method of javax.el.ELProcessor through reflection based on the return value of the getObjectInstance method, finally executing our malicious code.
The exploit is quite complex and we took time to reach a working PoC with the information available on the network.
As a little extra finding, we uncovered some previously unknown issues related to how SSL may prevent the PoC from working as expected.
By default, during the first debug of the exploit we disabled the SSL setting in order to avoid problems while analyzing the exploitation vector. After a working PoC we enabled again our SSL settings as default installation and our PoC failed.
The documentation of IBM is not useful because they suggest you to change the CSIv2 Transport Layer setting but actually we cannot access to the administration console.
Go to the WAS Administrative Console and change the WAS Global Security settings, >specify the CSIv2 inbound and outbound transports to “SSL-Supported”, restart the >server Security -> Global security, then go to CSIv2 inbound communications (and outbound >communications as well) -> CSIv2 Transport Layer -> Transport, change SSL-Required >(the default in V8) to SSL-Supported
In order to fix this issue we need to use a give in input for our exploit the SSL and CORBA configuration.
That’s is not sufficient, we also need to add to our hosts file the name of our target if we are not using the same DNS server. After tuning our settings as shown before, we were finally able to exploit our target.
Fourth Case Study: Hello 0day
During our analysis, we spotted few more bugs: an unauthenticated Local File Inclusion & Arbitrary File Delete exist on WebSphere.
While waiting for a CVE to be issued, we can suggest you to patch your WebSphere to the latest release, segregate your environment and apply an updated WAF, even for internal network services.
In this section we try to summarize some of improvements that could be considered in order to improve the security of the WAS infrastructure.
The following elements may be a good starting point:
Use HTTPS: If your site performs any authentication, introduce the use of HTTPS. If HTTPS is not used, information such as passwords, user activities, WebSphere Application Server session cookies, and also LTPA security cookies can potentially be seen by intruders as the communications are exchanged over the external/internal network. Remember that if an LTPA token is successfully captured, a malicious user can impersonate the user identified until this token expires.
Keep up to date with patches and fixes: as it is for every software, updates are of crucial importance since they often fix security issues. If possible, a subscription to support bulletins for any used product is strongly advised. As for WebSphere Application Server, the security bulletin site for the currently installed version should be monitored.
Consider introducing salt in file-based Federated Repository registry: if you are using the Federated Repository registry and are also using the default configuration, then userids and passwords are stored in the fileregistry.xml file in the cell configuration directory. These passwords are one-way hashed, but in some WAS version the length of the salt and the hashing algorithm may be changed. As an example, it is possible to enable Advanced Encryption Standard (AES) so that passwords stored in your configuration files and properties files will be correctly secured: the AES key will be saved in the aesKey.jceks file inside the cell configuration directory. This file, as well fileregistry.xml, should also be protected by OS-defined access control lists so only privileged OS users should be able to read the file.
Disable unused ports: one of the basic principles of security hardening tells us to minimize the attack surface. If a service is not required for the system to function correctly, it should be immediately removed to minimize the likelihood of an attacker taking advantage of this additional function at some point in the future.
Set up strong password policies: bruteforce or password spraying attacks could always happen, and the outcomes of the compromise of an administrator account could be catastrophic. Password policies can help a lot on this matter: consider setting a minimum password length, the frequency of password reuse, disallow default user names or user IDs, and last but not least specify a minimum password age.
Penetration Test: conduct periodic manual Penetration Test it’s very important in order to measure the security of an infrastructure.
This post was written to show that the use of automatic software is not enough: it is always necessary to analyze in depth every single bit of information we have. Executing automatic software scanner such as nmap scripts would have not lead to the correct enumeration of the version in use, and as a consequence to the inability to get a remote shell on the servers.