# -*- coding: binary -*-

module Msf
  ###
  #
  # This module provides methods for working with F5 equipment
  #
  ###
  module Auxiliary::F5
    include Msf::Auxiliary::Report

    def f5_config_eater(thost, tport, config, store = true)

      credential_data = {
        address: thost,
        port: tport,
        protocol: 'tcp',
        workspace_id: myworkspace_id,
        origin_type: :service,
        private_type: :nonreplayable_hash,
        # https://support.f5.com/csp/article/K65081001
        jtr_format: 'sha512,crypt', # default on the devices 11.4.0+
        service_name: '',
        module_fullname: fullname,
        status: Metasploit::Model::Login::Status::UNTRIED
      }

      # Default SNMP to UDP
      if tport == 161
        credential_data[:protocol] = 'udp'
      end

      if store
        store_loot('f5.config', 'text/plain', thost, config.strip, 'config.txt', 'F5 Configuration')
      end

      host_info = {
        host: thost,
        os_name: 'F5'
      }
      report_host(host_info)

      # generated by: tmsh list auth user

      # auth user admin {
      #    description "Admin User"
      # encrypted-password $6$4FAWSZLi$VeSaxPM2/D1JOhMRN/GMkt5wHcbIVKaIC2g765ZD0VA9ZEEm8iyK40/ncGrZIGyJyJF4ivkScNZ59HWAIKMML/
      #    partition Common
      #    partition-access {
      #        all-partitions {
      #            role admin
      #        }
      #    }
      #    shell none
      # }

      config.scan(%r{auth user ([^ ]+) {\s*description "?([^\n"]+)"?\n\s*encrypted-password ([$\w\+\./]+)\n[\w\s\-{}]+\s+shell (tmsh|bash|none)\n}}mi).each do |result|
        username = result[0].strip
        description = result[1].strip
        hash = result[2].strip
        shell = result[3].strip
        cred = credential_data.dup
        cred[:username] = username
        cred[:jtr_format] = Metasploit::Framework::Hashes.identify_hash(hash)
        cred[:private_data] = hash
        create_credential_and_login(cred)
        print_good("#{thost}:#{tport} Username '#{username}' with description '#{description}' and shell #{shell} with hash #{hash}")
      end

      # generated by: tmsh list sys snmp communities

      # sys snmp {
      #    communities {
      #        comm-public {
      #            community-name public
      #            source default
      #        }
      #        ro {
      #            community-name rocommunity
      #        }
      #        rw {
      #            access rw
      #            community-name rwcommunity
      #        }
      #    }
      # }

      config.scan(/(?:(access rw)?\n)\s+community-name (\w+)/).each do |result|
        if result[0].nil?
          access = 'RO'
        else
          access = 'RW'
        end
        cred = credential_data.dup
        cred[:port] = 161
        cred[:protocol] = 'udp'
        cred[:service_name] = 'snmp'
        cred[:jtr_format] = ''
        cred[:private_data] = result[1].strip
        cred[:private_type] = :password
        cred[:access_level] = access
        create_credential_and_login(cred)
        print_good("#{thost}:#{tport} SNMP Community '#{result[1].strip}' with #{access} access")
      end

      # generated by: cat /config/bigip.conf

      # cm device /Common/f5bigip.ragegroup.com {
      #    active-modules { "BIG-IP, VE Trial|VTFAAAA-AAAAAAA|Rate Shaping|External Interface and Network HSM, VE|SDN Services, VE|SSL, Forward Proxy, VE|BIG-IP VE, Multicast Routing|APM, Limited|SSL, VE|DNS (1K QPS), VE|Routing Bundle, VE|ASM, VE|Crytpo Offload, VE, Tier 1 (25M - 200M)|Max Compression, VE|AFM, VE|DNSSEC|Anti-Virus Checks|Base Endpoint Security Checks|Firewall Checks|Network Access|Secure Virtual Keyboard|APM, Web Application|Machine Certificate Checks|Protected Workspace|Remote Desktop|App Tunnel|VE, Carrier Grade NAT (AFM ONLY)|PSM, VE" }
      #    base-mac 00:11:11:a1:a1:a1
      #    build 0.0.9
      #    cert /Common/dtdi.crt
      #    chassis-id 164aaf79-aace-3494-1237671446c7
      #    configsync-ip 10.10.10.222
      #    edition "Point Release 2"
      #    hostname f5bigip.home.com
      #    key /Common/dtdi.key
      #    management-ip 1.1.1.1
      #    marketing-name "BIG-IP Virtual Edition"
      #    platform-id Z100
      #    product BIG-IP
      #    self-device true
      #    time-zone America/Los_Angeles
      #    version 15.1.0.2
      # }

      if /^cm device (?<content>.+)}$/m =~ config
        if /hostname (?<hostname>[\w\.-]+)$/i =~ content
          print_good("#{thost}:#{tport} Hostname: #{hostname}")
          host_info[:name] = hostname
          report_host(host_info)
        end
        if /base-mac (?<mac>[\d:a-f]+)$/i =~ content
          print_good("#{thost}:#{tport} MAC Address: #{mac}")
          host_info[:mac] = mac
          report_host(host_info)
        end
        if /management-ip (?<ip>[\d\.]+)$/ =~ content
          print_good("#{thost}:#{tport} Management IP: #{ip}")
        end
        if /product (?<product>[\w-]+)$/i =~ content
          print_good("#{thost}:#{tport} Product #{product}")
          host_info[:os_name] = "F5 #{product}"
          report_host(host_info)
        end
        if /version (?<version>[\d\.]+)$/i =~ content
          print_good("#{thost}:#{tport} OS Version: #{version}")
          host_info[:os_flavor] = version
          report_host(host_info)
        end
      end

      # generated by: cat /config/bigip.conf

      # sys file ssl-key /Common/f5_api_com.key {
      #    cache-path /config/filestore/files_d/Common_d/certificate_key_d/:Common:f5_api_com.key_63086_1
      #    passphrase $M$iE$cIdy72xi7Xbk3kazSrpdfscd+oD1pdsXJbwhvhMPiss4Iw0RKIJQS/CuSReZl/+kseKpPCNpBWNWOOaBCwlQ0v4sl7ZUkxCymh5pfFNAjhc=
      #    revision 1
      #    source-path file:///config/ssl/ssl.key/f5_api_com.key
      # }
      config.scan(%r{^sys file ssl-key (.+) \{.+passphrase ([$\w/\+=]+).+source-path file://([\w/\.]+)}mi).each do |result|
        username = result[0].strip # its not really a username, but we'll leave it as is since its a common name
        hash = result[1].strip
        file = result[2].strip
        cred = credential_data.dup
        cred[:username] = username
        cred[:jtr_format] = Metasploit::Framework::Hashes.identify_hash(hash)
        cred[:private_data] = hash
        create_credential_and_login(cred)
        print_good("#{thost}:#{tport} SSL Key '#{username}' and hash #{hash} for #{file}")
      end

      # generated by tmsh show sys crypto master-key

      # --------------------------------------------------------------------------------
      # Sys::Master-Key
      # --------------------------------------------------------------------------------
      # master-key hash  <EFt+B7/aTWwPwLoMd8KLYW4JB3K5B6301k4pGsoWnZEb2yUbvEJgNU3FcLHo0S4QvdrwVcKrNtHLzebC7HizHQ==>
      # previous hash    <EFt+B7/aTWwPwLoMd8KLYW4JB3K5B6301k4pGsoWnZEb2yUbvEJgNU3FcLHo0S4QvdrwVcKrNtHLzebC7HizHQ==>

      config.scan(%r{(master-key|previous) hash\s+<([\w\+/=]+)>}i). each do |result|
        key_type = result[0].strip
        key = result[1].strip
        cred = credential_data.dup
        cred[:username] = "F5 #{key_type} hash"
        cred[:jtr_format] = Metasploit::Framework::Hashes.identify_hash(key) # will come bacy empty
        cred[:private_data] = key
        create_credential_and_login(cred)
        print_good("#{thost}:#{tport} F5 #{key_type} hash #{key}")
      end

    end
  end
end
