diff --git a/modules/auxiliary/gather/corpwatch_lookup_id.rb b/modules/auxiliary/gather/corpwatch_lookup_id.rb new file mode 100644 index 0000000000..abfa10fe1d --- /dev/null +++ b/modules/auxiliary/gather/corpwatch_lookup_id.rb @@ -0,0 +1,535 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + +require 'msf/core' +require 'rexml/document' + +class Metasploit3 < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'CorpWatch Company ID Information Search', + 'Description' => %q{ + This module interfaces with the CorpWatch API to get publicly available + info for a given CorpWatch ID of the company. If you don't know the + CorpWatch ID, please use the corpwatch_lookup_name module first. + }, + 'Author' => [ 'Brandon Perry' ], + 'References' => + [ + [ 'URL', 'http://api.corpwatch.org/' ] + ] + )) + + register_options( + [ + OptString.new('CW_ID', [ true, "The CorpWatch ID of the company", ""]), + OptString.new('YEAR', [ false, "Year to look up", ""]), + OptBool.new('GET_LOCATIONS', [ false, "Get locations for company", true]), + OptBool.new('GET_NAMES', [ false, "Get all registered names ofr the company", true]), + OptBool.new('GET_FILINGS', [ false, "Get all filings", false ]), + OptBool.new('GET_CHILDREN', [false, "Get children companies", true]), + OptInt.new('CHILD_LIMIT', [false, "Set limit to how many children we can get", 5]), + OptBool.new('GET_HISTORY', [false, "Get company history", false]) + ], self.class) + + deregister_options('RHOST', 'RPORT', 'VHOST', 'Proxies') + end + + def cleanup + datastore['RHOST'] = @old_rhost + datastore['RPORT'] = @old_rport + end + + def run + # Save the original rhost/rport in case the user was exploiting something else + @old_rhost = datastore['RHOST'] + @old_rport = datastore['RPORT'] + + # Initial api.corpwatch.org's rhost and rport for HttpClient + datastore['RHOST'] = 'api.corpwatch.org' + datastore['RPORT'] = 80 + + loot = "" + uri = "/" + uri << (datastore['YEAR']) if datastore['YEAR'] != "" + uri << ("/companies/" + datastore['CW_ID']) + + res = send_request_cgi({ + 'uri' => uri + ".xml", + 'method' => 'GET' + }, 25) + + if res == nil + print_error("No response from server.") + return + end + + begin + doc = REXML::Document.new(res.body) + rescue + print_error("Malformed XML or broken response") + return + end + + root = doc.root + + if doc.root == nil + print_error("No document root, no results returned") + return + end + + elements = root.get_elements("result") + + if elements == nil || elements.length == 0 + print_error("No results returned") + return + end + + results = elements[0] + + if results == nil + print_error("No results returned") + return + end + + elements = results.get_elements("companies") + + if elements == nil || elements.length == 0 + print_error("No companies returned") + return + end + + results = elements[0] + + if results == nil || results.elements == nil + print_error("No results returned") + return + end + + results.elements.each { |e| + loot << ("CorpWatchID: " + (cwid = grab_text(e, "cw_id"))) + loot << ("\nCentral Index Key " + (cik = grab_text(e, "cik"))) + loot << ("\nName: " + (name = grab_text(e, "company_name"))) + loot << ("\nIRS Number: " + (irsno = grab_text(e, "irs_number"))) + loot << ("\nSIC Code: " + (sic_code = grab_text(e, "sic_code"))) + loot << ("\nSector: " + (sector = grab_text(e, "sector_name"))) + loot << ("\nSource: " + (source = grab_text(e, "source_type"))) + loot << ("\nAddress: " + (address = grab_text(e, "raw_address"))) + loot << ("\nCountry: " + ( country = grab_text(e, "country_code"))) + loot << ("\nSub-Division: " + (subdiv = grab_text(e, "subdiv_code"))) + loot << ("\nTop Parent CW_ID: " + (top_parent = grab_text(e, "top_parent_id"))) + loot << ("\nNumber of parents: " + (num_parents = grab_text(e, "num_parents"))) + loot << ("\nNumber of children: " + (num_children = grab_text(e, "num_children"))) + loot << ("\nMax searchable year: " + (max_year = grab_text(e, "max_year"))) + loot << ("\nMinimum searchable year: "+ (min_year = grab_text(e, "min_year"))) + loot << "\n\n\n" + + print_status("Basic Information\n--------------------") + print_status("CorpWatch ID: " + cwid) + print_status("Central Index Key (CIK): " + cik) + print_status("Full Name: " + name) + print_status("IRS Number: " + irsno) + print_status("SIC Code: " + sic_code) + print_status("Sector: " + sector) + print_status("Source Type: " + source) + + print_line("") + print_status("Address and Location Information\n-----------------------------") + print_status("Full Address: " + address) + print_status("Country Code: " + country) + print_status("Subdivision: " + subdiv) + + print_line("") + print_status("Parent and Children Information\n---------------------------") + print_status("Top Parent ID: " + top_parent) + print_status("Number of parent companies: " + num_parents) + print_status("Number of child companies: " + num_children) + print_status("Max lookup year: " + max_year) + print_status("Min lookup year: " + min_year) + } + + if datastore['GET_LOCATIONS'] + + res = send_request_cgi( + { + 'uri' => uri + "/locations.xml", + 'method' => 'GET' + }, 25) + + if res == nil + print_error ("Server down or bad response") + return + end + + begin + doc = REXML::Document.new(res.body) + rescue + print_error("Query returned bad or poorly formatted data.") + return + end + + root = doc.root + + elements = root.get_elements("result") + + if elements == nil || elements.length == 0 + print_error("no results returned") + return + end + + results = elements[0] + + if results == nil + print_status("No results returned") + else + results = results.get_elements("locations")[0] + + results.elements.each { |e| + loot << ("CorpWatch ID: " + (cwid = grab_text(e, "cw_id"))) + loot << ("\nCountry code: " + (country_code = grab_text(e, "country_code")) + loot << ("\nSubdivision code: " + (subdiv_code = grab_text(e, "subdiv_code"))) + loot << ("\nType: " + (type = grab_text(e, "type"))) + loot << ("\nFull address: " + full_address = grab_text(e, "raw_address"))) + loot << ("\nStreet 1: " + (street1 = grab_text(e, "street_1"))) + loot << ("\nStreet 2: " + (street2 = grab_text(e, "street_2"))) + loot << ("\nCity: " + (city = grab_text(e, "city"))) + loot << ("\nState: " + (state = grab_text(e, "state"))) + loot << ("\nZIP: " + (zip = grab_text(e, "postal_code"))) + loot << ("\nDate valid: " + (date_valid = grab_text(e, "date"))) + loot << ("\nMax searchable year: " + (max_year = grab_text(e, "max_year"))) + loot << ("\nMin searchable year: " + (min_year = grab_text(e, "min_year"))) + loot << "\n\n\n" + + print_line("") + print_status("Detailed Location Information\n----------------------------------") + print_status("Country Code: " + country_code) + print_status("Subdivision: " + subdiv_code) + print_status("Residential/Business address: " + type) + print_status("Full Address: " + full_address) + print_status("Street 1: " + street1) + print_status("Street 2: " + street2) + print_status("City: " + city) + print_status("State:" + state) + print_status("Postal Code: " + zip) + print_status("Date address was valid: " + date_valid) + print_status("Max lookup year: " + max_year) + print_status("Min lookup year: " + min_year) + } + end + end + + if datastore['GET_NAMES'] + + res = send_request_cgi( + { + 'uri' => uri + "/names.xml", + 'method' => 'GET' + }, 25) + + if res == nil + print_error("Server down or bad response") + return + end + + begin + doc = REXML::Document.new(res.body) + rescue + print_error("Query returned bad or poorly formatted XML") + return + end + + root = doc.root + + if root == nil + print_error("document root nil") + return + end + + elements = root.get_elements("result") + + if elements == nil || elements.length == 0 + print_error("Returned no or broken results") + return + end + + results = elements[0] + + if results == nil + print_status("No results returned") + else + results = results.get_elements("names")[0] + + results.elements.each { |e| + loot << ("Name: " + (name = grab_text(e, "company_name"))) + loot << ("\nSource: " + (source = grab_text(e, "source"))) + loot << ("\nDate: " + (date = grab_text(e, "date"))) + loot << ("\nMax searchable year: " + (max_year = grab_text(e, "max_year"))) + loot << ("\nMin searchable year: " + (min_year = grab_text(e, "min_year"))) + loot << "\n\n\n" + + print_line("\n") + print_status("Detailed Name Information\n---------------------------") + print_status("Name: " + name) + print_status("Source: " + source) + print_status("Date valid: " + date) + print_status("Max lookup year: " + max_year) + print_status("Min lookup year: " + min_year) + } + end + end + + if datastore['GET_FILINGS'] + + res = send_request_cgi( + { + 'uri' => uri + "/filings.xml", + 'method' => 'GET' + }, 25) + + if res == nil + print_error("Server down or response broken") + return + end + + begin + doc = REXML::Document.new(res.body) + rescue + print_error("Query return bad or broken data") + return + end + + root = doc.root + + elements = root.get_elements("result") + + if elements == nil || elements.length == 0 + print_error("Results were either broken or not returned") + return + end + + results = elements[0] + + if results == nil + print_status("No results returned") + else + elements = results.get_elements("filings") + + if elements == nil + print_error("Results broken or not returned") + return + end + + results = elements[0] + + if results == nil + print_status("No filings found") + else + results.elements.each { |e| + loot << ("Central Index Key: " + (cik = grab_text(e, "cik"))) + loot << ("\nYear filed: " + (year_filed = grab_text(e, "year"))) + loot << ("\nQuarter filed: " + (quarter_filed = grab_text(e, "quarter"))) + loot << ("\nReport period: " + (report_period = grab_text(e, "period_of_report"))) + loot << ("\nFiling date: " + (filing_date = grab_text(e, "filing_date"))) + loot << ("\nForm 10k: " + (form10k = grab_text(e, "form_10K_url"))) + loot << ("\nSEC21: " + (sec21 = grab_text(e, "sec_21_url"))) + loot << ("\nIs a filer: " + (is_filer = grab_text(e, "company_is_filer"))) + loot << "\n\n\n" + + print_line("\n") + print_status("Detailed Filing Information\n---------------------") + print_status("Central Index Key: " + cik) + print_status("Year filed: " + year_filed) + print_status("Quarter Filed: " + quarter_filed) + print_status("Report Period: " + report_period) + print_status("Filing Date: " + filing_date) + print_status("10K Filing Form: " + form10k) + print_status("SEC 21 Form: " + sec21) + print_status("Company is active filer: " + (is_filer == "1" ? "true" : "false")) + } + end + end + end + + if datastore['GET_CHILDREN'] + child_uri = (uri + "/children.xml") + + if datastore['CHILD_LIMIT'] != nil + child_uri << "?limit=#{datastore['CHILD_LIMIT']}" + print_status("Limiting children results to 5") + end + + res = send_request_cgi( + { + 'uri' => child_uri, + 'method' => 'GET' + }, 25) + + if res == nil + print_error("Server down or bad response") + return + end + + begin + doc = REXML::Document.new(res.body) + rescue + print_error("Query return bad or broken data") + return + end + + root = doc.root + + elements = root.get_elements("result") + + results = elements[0] + + if results == nil + print_status("No results were returned.") + else + results = results.get_elements("companies")[0] + + if results == nil + print_status("No results returned") + else + results.elements.each { |e| + loot << ("CorpWatch ID: " + (cwid = grab_text(e, "cw_id"))) + loot << ("\nCentral Index Key: " + (cik = grab_text(e, "cik"))) + loot << ("\nCompany Name: " + (name = grab_text(e, "company_name"))) + loot << ("\nIRS number: " + (irsno = grab_text(e, "irs_number"))) + loot << ("\nSIC Code: " + (sic_code = grab_text(e, "sic_code"))) + loot << ("\nSector: " + (sector = grab_text(e, "sector_name"))) + loot << ("\nSource: " + (source = grab_text(e, "source_type"))) + loot << ("\nAddress: " + (address = grab_text(e, "raw_address"))) + loot << ("\nCountry: " + (country = grab_text(e, "country_code"))) + loot << ("\nSubdivision: " + (subdiv = grab_text(e, "subdiv_code"))) + loot << ("\nTop parent: " + (top_parent = grab_text(e, "top_parent_id"))) + loot << ("\nNumber of parents: " + (num_parents = grab_text(e, "num_parents"))) + loot << ("\nNumber of children: " + (num_children = grab_text(e, "num_children"))) + loot << ("\nMax searchable year: " + (max_year = grab_text(e, "max_year"))) + loot << ("\nMin searchable year: " + (min_year = grab_text(e, "min_year"))) + loot << "\n\n\n" + + print_line("\n") + print_status("Child Information\n--------------------") + print_status("CorpWatch ID: " + cwid) + print_status("Central Index Key (CIK): " + cik) + print_status("Full Name: " + name) + print_status("IRS Number: " + irsno) + print_status("SIC Code: " + sic_code) + print_status("Sector: " + sector) + print_status("Source Type: " + source) + + print_line("") + print_status("Address and Location Information\n-----------------------------") + print_status("Full Address: " + address) + print_status("Country Code: " + country) + print_status("Subdivision: " + subdiv) + + print_line("") + print_status("Parent and Children Information\n---------------------------") + print_status("Top Parent ID: " + top_parent) + print_status("Number of parent companies: " + num_parents) + print_status("Number of child companies: " + num_children) + print_status("Max lookup year: " + max_year) + print_status("Min lookup year: " + min_year) + } + end + end + end + + if datastore['GET_HISTORY'] + + res = send_request_cgi({ + 'uri' => uri + "/history.xml", + 'method' => 'GET' + }, 25) + + if res == nil + print_error("Server down or bad response") + return + end + + begin + doc = REXML::Document.new(res.body) + rescue + print_error("Query return bad or broken data") + return + end + + root = doc.root + + elements = root.get_elements("result") + + if elements == nil || elements.length == 0 + print_error("No results.") + return + end + + results = elements[0] + + if results == nil + print_status("No results returned.") + else + results = results.get_elements("companies")[0] + + results.elements.each { |e| + loot << ("CorpWatch ID: " + (cwid = grab_text(e, "cw_id"))) + loot << ("\nCentral Index Key: " + (cik = grab_text(e, "cik"))) + loot << ("\nIRS Number: " + (irsno = grab_text(e, "irs_number"))) + loot << ("\nSIC Code: " + (sic_code = grab_text(e, "sic_code"))) + loot << ("\nIndustry: " + (industry = grab_text(e, "industry_name"))) + loot << ("\nSector: " + (sector = grab_text(e, "sector_name"))) + loot << ("\nSIC Sector: " + (sic_sector = grab_text(e, "sic_sector"))) + loot << ("\nSource: " + (source = grab_text(e, "source_type"))) + loot << ("\nAddress: " + (address = grab_text(e, "raw_address"))) + loot << ("\nCountry: " + (country_code = grab_text(e, "country_code"))) + loot << ("\nSub-division Code: " + (subdiv_code = grab_text(e, "subdiv_code"))) + loot << ("\nTop parent ID: " + (top_parent = grab_text(e, "top_parent_id"))) + loot << ("\nNumber of parents: " + (num_parents = grab_text(e, "num_parents"))) + loot << ("\nNumber of children: " + (num_children = grab_text(e, "num_children"))) + loot << ("\nMax searchable year: " + (max_year = grab_text(e, "max_year"))) + loot << ("\nMin searchable year: " + (min_year = grab_text(e, "min_year"))) + loot << ("\nHistory year: " + (history_year = grab_text(e, "year"))) + loot << "\n\n\n" + + print_line("\n") + print_status("Company History for year #{history_year}\n--------------------------------") + print_status("CorpWatch ID: " + cwid) + print_status("Central Index Key: " + cik) + print_status("IRS number: " + irsno) + print_status("SIC Code: " + sic_code) + print_status("Industry: " + industry) + print_status("Sector: " + sector) + print_status("SIC Sector: " + sic_sector) + print_status("Source: " + source) + print_status("Address: " + address) + print_status("Country: " + country_code) + print_status("Subdivision: " + subdiv_code) + print_status("Top Parent ID: " + top_parent) + print_status("Number of parents: " + num_parents) + print_status("Number of children: " + num_children) + print_status("Max lookup year: " + max_year) + print_status("Min lookup year: " + min_year) + } + end + end + + p = store_loot("corpwatch_api.#{datastore['CW_ID']}_info","text/plain",nil,loot,"company_#{datastore['CWID']}.txt","#{datastore["CW_ID"]} Specific Information") + + print_line() + print_status("Saved in: #{p}") + end + + def grab_text(e, name) + (e.get_elements(name) && e.get_elements(name)[0] && + e.get_elements(name)[0].get_text ) ? + e.get_elements(name)[0].get_text.to_s : "" + end + +end