|
| 1 | +## |
| 2 | +# This module requires Metasploit: http://metasploit.com/download |
| 3 | +# Current source: https://github.com/rapid7/metasploit-framework |
| 4 | +## |
| 5 | + |
| 6 | + |
| 7 | +require 'msf/core' |
| 8 | +require 'rex' |
| 9 | +require 'net/https' |
| 10 | +require 'uri' |
| 11 | + |
| 12 | + |
| 13 | +class MetasploitModule < Msf::Auxiliary |
| 14 | + |
| 15 | + include Msf::Exploit::Remote::HttpClient |
| 16 | + include Msf::Auxiliary::Report |
| 17 | + |
| 18 | + def initialize(info={}) |
| 19 | + super(update_info(info, |
| 20 | + 'Name' => 'ZoomEye Search', |
| 21 | + 'Description' => %q{ |
| 22 | + The module use the ZoomEye API to search ZoomEye. ZoomEye is a search |
| 23 | + engine for cyberspace that lets the user find specific network |
| 24 | + components(ip, services, etc.). Site: https://www.zoomeye.org/api/doc |
| 25 | + }, |
| 26 | + 'Author' => [ 'Nixawk' ], |
| 27 | + 'License' => MSF_LICENSE |
| 28 | + )) |
| 29 | + |
| 30 | + deregister_options('RHOST', 'DOMAIN', 'DigestAuthIIS', 'NTLM::SendLM', |
| 31 | + 'NTLM::SendNTLM', 'VHOST', 'RPORT', 'NTLM::SendSPN', 'NTLM::UseLMKey', |
| 32 | + 'NTLM::UseNTLM2_session', 'NTLM::UseNTLMv2', 'SSL') |
| 33 | + |
| 34 | + register_options( |
| 35 | + [ |
| 36 | + OptString.new('ZOOMEYE_APIKEY', [true, 'The ZoomEye API Key']), |
| 37 | + OptString.new('ZOOMEYE_DORK', [true, 'The ZoomEye Dock']), |
| 38 | + OptEnum.new('RESOURCE', [true, 'ZoomEye Resource Type', 'host', ['host', 'web']]), |
| 39 | + OptInt.new('MAXPAGE', [true, 'Max amount of pages to collect', 1]) |
| 40 | + ], self.class) |
| 41 | + end |
| 42 | + |
| 43 | + def dork_search(dork, resource, page, facet=['ip']) |
| 44 | + # param: dork |
| 45 | + # ex: country:cn |
| 46 | + # access https://www.zoomeye.org/search/dorks for more details. |
| 47 | + # param: page |
| 48 | + # total page(s) number |
| 49 | + # param: resource |
| 50 | + # set a search resource type, ex: [web, host] |
| 51 | + # param: facet |
| 52 | + # ex: [app, device] |
| 53 | + # A comma-separated list of properties to get summary information |
| 54 | + |
| 55 | + zoomeye_dork_api = "https://api.zoomeye.org/#{resource}/search" |
| 56 | + zoomeye_dork_api << "?query=" + Rex::Text.uri_encode(dork) |
| 57 | + zoomeye_dork_api << "&page=#{page}" |
| 58 | + zoomeye_dork_api << "&facet=facet" |
| 59 | + |
| 60 | + uri = URI.parse(zoomeye_dork_api) |
| 61 | + http = Net::HTTP.new(uri.host, uri.port) |
| 62 | + http.use_ssl = true |
| 63 | + request = Net::HTTP::Get.new(uri.request_uri) |
| 64 | + request['Authorization'] = "JWT #{datastore['ZOOMEYE_APIKEY']}" |
| 65 | + |
| 66 | + res = http.request(request) |
| 67 | + return 'server_response_error' unless res |
| 68 | + |
| 69 | + # Invalid Token, Not enough segments |
| 70 | + # Invalid Token, Signature has expired |
| 71 | + if res.body =~ /Invalid Token, / |
| 72 | + fail_with(Failure::BadConfig, '401 Unauthorized. Your ZOOMEYE_APIKEY is invalid') |
| 73 | + end |
| 74 | + |
| 75 | + ActiveSupport::JSON.decode(res.body) |
| 76 | + |
| 77 | + end |
| 78 | + |
| 79 | + def match_records?(records) |
| 80 | + records && records.key?('matches') ? true : false |
| 81 | + end |
| 82 | + |
| 83 | + def parse_host_records(records) |
| 84 | + records.each do |match| |
| 85 | + host = match['ip'] |
| 86 | + port = match['portinfo']['port'] |
| 87 | + |
| 88 | + report_service(:host => host, :port => port) |
| 89 | + print_good("Host: #{host} ,PORT: #{port}") |
| 90 | + end |
| 91 | + end |
| 92 | + |
| 93 | + def parse_web_records(records) |
| 94 | + records.each do |match| |
| 95 | + host = match['ip'][0] |
| 96 | + domains = match['domains'] |
| 97 | + |
| 98 | + report_host(:host => host) |
| 99 | + print_good("Host: #{host}, Domains: #{domains}") |
| 100 | + end |
| 101 | + end |
| 102 | + |
| 103 | + def run |
| 104 | + dork = datastore['ZOOMEYE_DORK'] |
| 105 | + resource = datastore['RESOURCE'] |
| 106 | + page = 1 |
| 107 | + maxpage = datastore['MAXPAGE'] |
| 108 | + |
| 109 | + while page <= maxpage |
| 110 | + break if page > maxpage |
| 111 | + print_status("ZoomEye #{resource} Search: #{dork} - page: #{page}") |
| 112 | + results = dork_search(dork, resource, page) if dork |
| 113 | + break unless match_records?(results) |
| 114 | + |
| 115 | + matches = results['matches'] |
| 116 | + if resource.include?('web') |
| 117 | + parse_web_records(matches) |
| 118 | + else |
| 119 | + parse_host_records(matches) |
| 120 | + end |
| 121 | + page += 1 |
| 122 | + end |
| 123 | + end |
| 124 | +end |
0 commit comments