Ever come across a system that did so many little things wrong that you were certain you could “get r00t”? You chip away, gradually uncovering the links in the chain, but then you run out of time…
This was almost one of those. Almost.
The tl;dr; of this one is that HP Device Manager versions 5.0.3 and below, and 4.7 before service pack 13 (pending release at the time of writing) are affected by a bunch of vulnerabilities that, in the worst case, lead to unauthenticated remote command execution with SYSTEM privileges. The official advisory and mitigation advice from HP can be found here.
Update 16th Oct 2020: PoC exploit can be found here.
This all began with the discovery of an open TCP port 1099 during an internal network infrastructure security assessment. This is the default port for a Java Remote Method Invocation (RMI) service registry. The RMI protocol seems to be commonly found in enterprise systems and security of the protocol and applications using it often relies on obscurity.
Without the corresponding Java classes, you generally can’t interact with the RMI service. My tool BaRMIe uses mock Java interface classes to enable interaction with RMI endpoints, but brute force attacks may also be possible as demonstrated by the tool rmiscout.
HPDM Server RMI
I used BaRMIe to enumerate the target RMI service and found one Java object to be exposed with the name “HPDM Server RMI”. The object implemented the interface
A quick Internet search revealed that this belonged to HP Device Manager and with a bit more digging I found that I could download the software freely (free account registration required). That was the first hurdle in attacking the service.
The first attack I consider when I look at a new RMI service is deserialization. The quickest and easiest win is to call a remote method that accepts a non-primitive type parameter. I use ysoserial to generate deserialization payload objects to pass to it, and, hopefully, achieve remote code execution.
To be clear, when I say non-primitive type parameter, I mean a method parameter of any object type, including objects such as
java.lang.Long (but not the primitive type
A quick recursive grep over the HP Device Manager installation directory revealed that the
com.hp.hpdm.interf.ServiceInterf class used by the RMI service could be found in the JAR file
hpdm-common.jar. All four methods of this class could be used to deliver deserialization payloads.
hpdm-common.jar on the Java CLASSPATH we can write Java code to retrieve a reference to the remote
ServiceInterf object and call each of these methods on it such that the method execution occurs on the remote server.
Java won’t actually let us pass arbitrary objects to these methods if the parameter types aren’t compatible – for example a
java.util.HashMap, used by some deserialization payloads, is not compatible with (in this case)
com.hp.hpdm.mess.Message. BaRMIe gets around this restriction by proxying the RMI connection and injecting an otherwise “illegal” object into the method call.
In this case, there was an easier option. The remote
sendMessage() method accepted a parameter of type
com.hp.hpdm.mess.Message, which had a property of type
com.hp.hpdm.mess.Request, which in turn had a property of type
java.util.Vector that could hold arbitrary objects.
This allowed me to deliver deserialization payloads directly, as shown in the following screenshot, without the need for connection proxying.
After testing each available gadget chain, the only ones that appeared to work were the Hibernate gadget chains. Unfortunately I was unable to get the server to do a JNDI lookup via HTTP or LDAP.
Attacking Application Functionality
The other type of attack that can be performed against RMI services is to simply call the functions of the remote object. There is no built-in support for authentication in RMI and it often gets overlooked, so if there is any interesting functionality there then chances are we can call it directly.
Decompiling the HP Device Manager JAR files I started to review the code, particularly that behind the
sendMessage() method. There were a LOT of code paths that could be reached this way and I barely scratched the surface in my review.
Of particular note were the
ServGetUsers() methods. The former returned configuration data, which included entries for usernames and passwords for external services that HP Device Manager can authenticate to. Not good, but this wasn’t going to get me far.
The add user method resulted in an insufficient privileges error, so that was no use either – apparently they did implement some sort of authentication!
ServGetUsers() method returned details of all HP Device Manager user accounts, along with MD5 password hashes that were salted using a static hard-coded salt.
Unfortunately I didn’t have any luck cracking the passwords returned by the target server.
Bundled Postgres Database
HP Device Manager is bundled with a Postgres database that listens for connections on TCP port 40006. Postgres can be used to execute operating system commands so this was potentially useful. I checked the target server and confirmed that a Postgres service was listening on port 40006, before searching for the password in the virtual machine where I’d installed HP Device Manager.
The database credentials were found in a
hibernate.properties file. The password was encrypted with a static hard-coded key and was easily recovered, but the password turned out to be randomly generated during installation.
Whilst looking at the Postgres database, I spotted a second database user account named
dm_postgres, which had superuser privileges. A recursive grep of the HP Device Manager installation directory for this username returned a single result in the file
The log showed an authentication failure for the user account used by HP Device Manager, followed by an error resulting from attempting to give the
dm_postgres user the
nocreateuser role, as if trying to limit the privileges of this apparent “backdoor” database account.
I wanted the password for this account. It didn’t appear to be referenced or used by the application, so I took the hash from the Postgres users table and set out to try and crack it.
I ran a full brute force of 1-8 characters (at least lowercase, uppercase, and numbers if I remember correctly), followed by some dictionary and rule combinations, before breaking out the big guns with NPK and some EC2 GPU instances.
I couldn’t believe it when, £5 in EC2 fees later, the password was cracked.
It was ” “.
A single space character.
I did a fresh install of HP Device Manager and verified that this was in fact real. It was.
Unfortunately this was no use from a remote perspective, because the Postgres database was configured to only accept authentication from loopback IP addresses.
Up to this point I had the following:
- Potential for deserialization via RMI
- Unauthenticated access to RMI methods
- Configuration leak
- User details leak including MD5 hashes
- Backdoor Postgres super user, but no remote access to it
I started to wonder whether I could take advantage of HP Device Manager’s own connection to the Postgres database to attack it. Direct attacks against Postgres were out of the question either way though, so I got back to reviewing the decompiled code for the RMI service.
After a little more code review, specifically looking for database queries, I found several instances where method parameters were being concatenated into database query strings
I quickly located one that I could inject into via the RMI service. Without authentication.
Unfortunately the injection was within a
SELECT statement. To execute operating system commands in Postgres SQL I needed to execute a
COPY x FROM PROGRAM statement, which can not be done within a
SELECT statement. Stacked queries were not supported either.
After a bit of research I came across an excellent article that described a potential route from Postgres SQL injection to RCE by calling Postgres functions. I connected directly to the Postgres database in my VM to test this technique out in isolation which essentially worked as follows:
lo_from_bytea()to create a Postgres large object containing arbitrary data
lo_export()to write the large object to a file on disk
pg_reload_conf()to reload the Postgres configuration
The Postgres database was installed with SYSTEM privileges by default, so, in theory, I could exploit the injection as above to overwrite the
pg_hba.conf file and enable remote authentication to the Postgres database, then use the
dm_postgres user account to execute arbitrary commands.
The theory was great, until I realised that the query injection wasn’t a Postgres SQL injection at all.
Object Relational Mapping
The query injection vulnerability was a Hibernate Query Language Injection. Hibernate is an object relational mapping tool that maps arbitrary object classes to relational database tables in such a way that developers generally don’t need to write database queries by hand. It’s often the end of the line when it comes to query injection attacks, unless the application does something really wrong.
Hibernate provides its own query language, HQL, which provides developers with more power and flexibility in querying for objects. My attempts to exploit the injection to achieve RCE largely resulted in the HQL parser ripping out, rearranging (and breaking), or failing to parse the query syntax I was injecting.
Eventually I came across some research into ORM Injection by Mikhail Egorov and Sergey Soldatov that they presented at Hack in the Box Amsterdam 2016.
Probing HP Device Manager with these techniques didn’t appear to be effective, although I wasn’t expecting it to be given that the research was from early 2016.
I stopped to review the situation and what I’d achieved up to this point. I had a local privilege escalation exploit for HP Device Manager via the Postgres database. I had several vulnerabilities or weaknesses in the remotely-accessible RMI service. I didn’t have a bridge between the two.
I figured the most likely options at this stage were:
- Investigate the ORM injection techniques further – it was old research, but perhaps there were still similar issues.
- Review the RMI service for an arbitrary file write or other vulnerability – there were multiple issues in this service and there was potentially more scope for exploiting deserialization.
I opted to start with the former. I created a lab environment to remove as many variables as possible from the scenario so that I could focus on attempting to exploit this specific issue.
First, I span up a Postgres database, cranked up the log verbosity, and used
tail -f to watch the log file on one monitor. Next, I threw together a vulnerable Java application with an identical injection point, where I could type payloads and see the resulting HQL query and any exceptions in real time.
After an hour or two hammering away at this, I successfully smuggled Postgres function calls through a HQL injection.
I verified the SQLi/HQLi against HP Device Manager, including against a fresh install, before piecing the complete exploit together.
- Connect to the server via RMI and request the
HPDM Server RMIobject
- Exploit the Hibernate Query Language Injection to smuggle in a Postgres SQL injection payload which calls the Postgres function
lo_from_bytea()to create a Postgres large object
- Exploit the HQLi/SQLi again to call the Postgress function
lo_export()to overwrite the
pg_hba.conffile on the server with the contents of the large object
- Exploit the HQLi/SQLi a third time to call the Postgres function
pg_reload_conf()to reload the Postgres configuration and enable remote authentication using the new
- Establish a direct connection to the Postgres database using the
dm_postgresuser and a single space character as the password
- Create a Postgres table named
cmd_execto store the command output
- Execute the query
COPY cmd_exec FROM PROGRAM 'XXX'to execute a command on the server with SYSTEM privileges and write the output to the
- Query the
cmd_exectable to retrieve the command output
- HP Advisory PSR-2020-0174 (https://support.hp.com/us-en/document/c06921908)
- PoC exploit code (https://github.com/NickstaDB/PoC/tree/master/HP_Device_Manager_RCE)
- BaRMIe RMI Enumeration and Attack Tool (https://github.com/NickstaDB/BaRMIe)
- rmiscout RMI Brute Forcing Tool (https://github.com/BishopFox/rmiscout)
- HP Device Manager 5.0.3 Download (https://h30670.www3.hp.com/portal/swdepot/displayProductInfo.do?productNumber=HPDEVMAN)
- ysoserial Java Deserialization Payload Generator (https://github.com/frohoff/ysoserial)
- NPK Distributed Hash Cracking Platform (https://github.com/Coalfire-Research/npk)
- Postgres SQL Injection to RCE (https://pulsesecurity.co.nz/articles/postgres-sqli)
- Hibernate ORM (https://hibernate.org/)
- New Methods for Exploiting ORM Injections in Java Applications (https://conference.hitb.org/hitbsecconf2016ams/materials/D2T2%20-%20Mikhail%20Egorov%20and%20Sergey%20Soldatov%20-%20New%20Methods%20for%20Exploiting%20ORM%20Injections%20in%20Java%20Applications.pdf)