Skip to content

Commit 05ffa00

Browse files
David MaloneyDavid Maloney
authored andcommitted
Land 37460, zoomeye search module
Lands nixawk's zoomeye search aux module
2 parents 85c26c6 + ada571b commit 05ffa00

File tree

1 file changed

+185
-0
lines changed

1 file changed

+185
-0
lines changed
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
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/proto/http'
9+
10+
11+
class MetasploitModule < Msf::Auxiliary
12+
13+
include Msf::Auxiliary::Report
14+
15+
def initialize(info={})
16+
super(update_info(info,
17+
'Name' => 'ZoomEye Search',
18+
'Description' => %q{
19+
The module use the ZoomEye API to search ZoomEye. ZoomEye is a search
20+
engine for cyberspace that lets the user find specific network
21+
components(ip, services, etc.).
22+
},
23+
'Author' => [ 'Nixawk' ],
24+
'References' => [
25+
['URL', 'https://github.com/zoomeye/SDK'],
26+
['URL', 'https://www.zoomeye.org/api/doc'],
27+
['URL', 'https://www.zoomeye.org/help/manual']
28+
],
29+
'License' => MSF_LICENSE
30+
))
31+
32+
deregister_options('RHOST', 'DOMAIN', 'DigestAuthIIS', 'NTLM::SendLM',
33+
'NTLM::SendNTLM', 'VHOST', 'RPORT', 'NTLM::SendSPN', 'NTLM::UseLMKey',
34+
'NTLM::UseNTLM2_session', 'NTLM::UseNTLMv2', 'SSL')
35+
36+
register_options(
37+
[
38+
OptString.new('USERNAME', [true, 'The ZoomEye username']),
39+
OptString.new('PASSWORD', [true, 'The ZoomEye password']),
40+
OptString.new('ZOOMEYE_DORK', [true, 'The ZoomEye Dock']),
41+
OptEnum.new('RESOURCE', [true, 'ZoomEye Resource Type', 'host', ['host', 'web']]),
42+
OptInt.new('MAXPAGE', [true, 'Max amount of pages to collect', 1])
43+
], self.class)
44+
end
45+
46+
# Check to see if api.zoomeye.org resolves properly
47+
def zoomeye_resolvable?
48+
begin
49+
Rex::Socket.resolv_to_dotted("api.zoomeye.org")
50+
rescue RuntimeError, SocketError
51+
return false
52+
end
53+
54+
true
55+
end
56+
57+
def login(username, password)
58+
# See more: https://www.zoomeye.org/api/doc#login
59+
60+
access_token = ''
61+
@cli = Rex::Proto::Http::Client.new('api.zoomeye.org', 443, {}, true)
62+
@cli.connect
63+
64+
data = {'username' => username, 'password' => password}
65+
req = @cli.request_cgi({
66+
'uri' => '/user/login',
67+
'method' => 'POST',
68+
'data' => data.to_json
69+
})
70+
71+
res = @cli.send_recv(req)
72+
73+
unless res
74+
print_error('server_response_error')
75+
return
76+
end
77+
78+
records = ActiveSupport::JSON.decode(res.body)
79+
access_token = records['access_token'] if records && records.key?('access_token')
80+
access_token
81+
end
82+
83+
def dork_search(dork, resource, page)
84+
# param: dork
85+
# ex: country:cn
86+
# access https://www.zoomeye.org/search/dorks for more details.
87+
# param: page
88+
# total page(s) number
89+
# param: resource
90+
# set a search resource type, ex: [web, host]
91+
# param: facet
92+
# ex: [app, device]
93+
# A comma-separated list of properties to get summary information
94+
95+
begin
96+
req = @cli.request_cgi({
97+
'uri' => "/#{resource}/search",
98+
'method' => 'GET',
99+
'headers' => { 'Authorization' => "JWT #{@zoomeye_token}" },
100+
'vars_get' => {
101+
'query' => dork,
102+
'page' => page,
103+
'facet' => 'ip'
104+
}
105+
})
106+
107+
res = @cli.send_recv(req)
108+
109+
rescue ::Rex::ConnectionError, Errno::ECONNREFUSED, Errno::ETIMEDOUT
110+
print_error("HTTP Connection Failed")
111+
end
112+
113+
unless res
114+
print_error('server_response_error')
115+
return
116+
end
117+
118+
# Invalid Token, Not enough segments
119+
# Invalid Token, Signature has expired
120+
if res.body =~ /Invalid Token, /
121+
fail_with(Failure::BadConfig, '401 Unauthorized. Your ZOOMEYE_APIKEY is invalid')
122+
end
123+
124+
ActiveSupport::JSON.decode(res.body)
125+
end
126+
127+
def match_records?(records)
128+
records && records.key?('matches')
129+
end
130+
131+
def parse_host_records(records)
132+
records.each do |match|
133+
host = match['ip']
134+
port = match['portinfo']['port']
135+
136+
report_service(:host => host, :port => port)
137+
print_good("Host: #{host} ,PORT: #{port}")
138+
end
139+
end
140+
141+
def parse_web_records(records)
142+
records.each do |match|
143+
host = match['ip'][0]
144+
domains = match['domains']
145+
146+
report_host(:host => host)
147+
print_good("Host: #{host}, Domains: #{domains}")
148+
end
149+
end
150+
151+
def run
152+
# check to ensure api.zoomeye.org is resolvable
153+
unless zoomeye_resolvable?
154+
print_error("Unable to resolve api.zoomeye.org")
155+
return
156+
end
157+
158+
@zoomeye_token = login(datastore['USERNAME'], datastore['PASSWORD'])
159+
if @zoomeye_token.blank?
160+
print_error("Unable to login api.zoomeye.org")
161+
return
162+
end
163+
164+
# create ZoomEye request parameters
165+
dork = datastore['ZOOMEYE_DORK']
166+
resource = datastore['RESOURCE']
167+
page = 1
168+
maxpage = datastore['MAXPAGE']
169+
170+
# scroll max pages from ZoomEye
171+
while page <= maxpage
172+
print_status("ZoomEye #{resource} Search: #{dork} - page: #{page}")
173+
results = dork_search(dork, resource, page) if dork
174+
break unless match_records?(results)
175+
176+
matches = results['matches']
177+
if resource.include?('web')
178+
parse_web_records(matches)
179+
else
180+
parse_host_records(matches)
181+
end
182+
page += 1
183+
end
184+
end
185+
end

0 commit comments

Comments
 (0)