shodan 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/shodan/api.rb +204 -203
- data/lib/shodan/version.rb +3 -3
- metadata +6 -6
data/lib/shodan/api.rb
CHANGED
@@ -1,203 +1,204 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'cgi'
|
3
|
-
require 'json'
|
4
|
-
require 'net/http'
|
5
|
-
|
6
|
-
module Shodan
|
7
|
-
|
8
|
-
# The WebAPI class interfaces with the shodanhq.com/api
|
9
|
-
# It currently supports 2 methods:
|
10
|
-
# 1. search (query)
|
11
|
-
# 2. host (ip)
|
12
|
-
#
|
13
|
-
# Author:: achillean (mailto:jmath at surtri.com)
|
14
|
-
#
|
15
|
-
# :title:Shodan::WebAPI
|
16
|
-
class WebAPI
|
17
|
-
attr_accessor :api_key
|
18
|
-
attr_accessor :base_url
|
19
|
-
attr_accessor :dataloss
|
20
|
-
attr_accessor :exploitdb
|
21
|
-
attr_accessor :msf
|
22
|
-
|
23
|
-
def initialize(api_key)
|
24
|
-
@api_key = api_key
|
25
|
-
@base_url = "http://www.shodanhq.com/api/"
|
26
|
-
@dataloss = DatalossDB.new(self)
|
27
|
-
@exploitdb = ExploitDB.new(self)
|
28
|
-
@msf = Msf.new(self)
|
29
|
-
end
|
30
|
-
|
31
|
-
# Internal method that sends out the HTTP request.
|
32
|
-
# Expects a webservice function (ex. 'search') name and a hash of arguments.
|
33
|
-
def request(func, args)
|
34
|
-
# Convert the argument hash into a string
|
35
|
-
args_string = args.map{|k, v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v)}"}.join("&")
|
36
|
-
|
37
|
-
# Craft the final request URL
|
38
|
-
url = "#{@base_url}#{func}?key=#{@api_key}&#{args_string}"
|
39
|
-
|
40
|
-
# Send the request
|
41
|
-
response = Net::HTTP.get_response(URI.parse(url))
|
42
|
-
|
43
|
-
# Convert the JSON data into a native Ruby hash
|
44
|
-
data = JSON.parse(response.body)
|
45
|
-
|
46
|
-
# Raise an error if something went wrong
|
47
|
-
if data.has_key? 'error'
|
48
|
-
raise data['error']
|
49
|
-
end
|
50
|
-
|
51
|
-
return data
|
52
|
-
end
|
53
|
-
|
54
|
-
# Get all available information on an IP.
|
55
|
-
#
|
56
|
-
# Arguments:
|
57
|
-
# ip - host IP (string)
|
58
|
-
#
|
59
|
-
# Returns a hash containing the host information
|
60
|
-
def host(ip)
|
61
|
-
return request('host', {:ip => ip})
|
62
|
-
end
|
63
|
-
|
64
|
-
# Perform a search on Shodan.
|
65
|
-
#
|
66
|
-
# Arguments:
|
67
|
-
# query - search query; same format as the website (string)
|
68
|
-
#
|
69
|
-
# Returns a hash containing the search results
|
70
|
-
def search(query)
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
#
|
77
|
-
#
|
78
|
-
#
|
79
|
-
#
|
80
|
-
#
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
#
|
90
|
-
#
|
91
|
-
#
|
92
|
-
#
|
93
|
-
#
|
94
|
-
#
|
95
|
-
#
|
96
|
-
#
|
97
|
-
#
|
98
|
-
#
|
99
|
-
#
|
100
|
-
#
|
101
|
-
#
|
102
|
-
#
|
103
|
-
#
|
104
|
-
#
|
105
|
-
#
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
#
|
114
|
-
#
|
115
|
-
#
|
116
|
-
#
|
117
|
-
#
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
#
|
127
|
-
#
|
128
|
-
#
|
129
|
-
#
|
130
|
-
#
|
131
|
-
#
|
132
|
-
#
|
133
|
-
#
|
134
|
-
#
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
#
|
141
|
-
#
|
142
|
-
#
|
143
|
-
#
|
144
|
-
#
|
145
|
-
#
|
146
|
-
#
|
147
|
-
#
|
148
|
-
#
|
149
|
-
#
|
150
|
-
#
|
151
|
-
#
|
152
|
-
#
|
153
|
-
#
|
154
|
-
#
|
155
|
-
#
|
156
|
-
#
|
157
|
-
#
|
158
|
-
#
|
159
|
-
#
|
160
|
-
#
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
#
|
170
|
-
#
|
171
|
-
#
|
172
|
-
#
|
173
|
-
#
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
#
|
183
|
-
#
|
184
|
-
#
|
185
|
-
#
|
186
|
-
#
|
187
|
-
#
|
188
|
-
#
|
189
|
-
#
|
190
|
-
#
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
1
|
+
require 'rubygems'
|
2
|
+
require 'cgi'
|
3
|
+
require 'json'
|
4
|
+
require 'net/http'
|
5
|
+
|
6
|
+
module Shodan
|
7
|
+
|
8
|
+
# The WebAPI class interfaces with the shodanhq.com/api
|
9
|
+
# It currently supports 2 methods:
|
10
|
+
# 1. search (query)
|
11
|
+
# 2. host (ip)
|
12
|
+
#
|
13
|
+
# Author:: achillean (mailto:jmath at surtri.com)
|
14
|
+
#
|
15
|
+
# :title:Shodan::WebAPI
|
16
|
+
class WebAPI
|
17
|
+
attr_accessor :api_key
|
18
|
+
attr_accessor :base_url
|
19
|
+
attr_accessor :dataloss
|
20
|
+
attr_accessor :exploitdb
|
21
|
+
attr_accessor :msf
|
22
|
+
|
23
|
+
def initialize(api_key)
|
24
|
+
@api_key = api_key
|
25
|
+
@base_url = "http://www.shodanhq.com/api/"
|
26
|
+
@dataloss = DatalossDB.new(self)
|
27
|
+
@exploitdb = ExploitDB.new(self)
|
28
|
+
@msf = Msf.new(self)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Internal method that sends out the HTTP request.
|
32
|
+
# Expects a webservice function (ex. 'search') name and a hash of arguments.
|
33
|
+
def request(func, args)
|
34
|
+
# Convert the argument hash into a string
|
35
|
+
args_string = args.map{|k, v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}"}.join("&")
|
36
|
+
|
37
|
+
# Craft the final request URL
|
38
|
+
url = "#{@base_url}#{func}?key=#{@api_key}&#{args_string}"
|
39
|
+
|
40
|
+
# Send the request
|
41
|
+
response = Net::HTTP.get_response(URI.parse(url))
|
42
|
+
|
43
|
+
# Convert the JSON data into a native Ruby hash
|
44
|
+
data = JSON.parse(response.body)
|
45
|
+
|
46
|
+
# Raise an error if something went wrong
|
47
|
+
if data.has_key? 'error'
|
48
|
+
raise data['error']
|
49
|
+
end
|
50
|
+
|
51
|
+
return data
|
52
|
+
end
|
53
|
+
|
54
|
+
# Get all available information on an IP.
|
55
|
+
#
|
56
|
+
# Arguments:
|
57
|
+
# ip - host IP (string)
|
58
|
+
#
|
59
|
+
# Returns a hash containing the host information
|
60
|
+
def host(ip)
|
61
|
+
return request('host', {:ip => ip})
|
62
|
+
end
|
63
|
+
|
64
|
+
# Perform a search on Shodan.
|
65
|
+
#
|
66
|
+
# Arguments:
|
67
|
+
# query - search query; same format as the website (string)
|
68
|
+
#
|
69
|
+
# Returns a hash containing the search results
|
70
|
+
def search(query, params={})
|
71
|
+
params[:q] = query
|
72
|
+
return request('search', params)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# The DatalossDB class shouldn't be used independently,
|
77
|
+
# as it depends on the WebAPI class.
|
78
|
+
#
|
79
|
+
# Author:: achillean (mailto:jmath at surtri.com)
|
80
|
+
#
|
81
|
+
# :title:Shodan::DatalossDB
|
82
|
+
class DatalossDB
|
83
|
+
attr_accessor :api
|
84
|
+
|
85
|
+
def initialize(api)
|
86
|
+
@api = api
|
87
|
+
end
|
88
|
+
|
89
|
+
# Search the Dataloss DB archive.
|
90
|
+
#
|
91
|
+
# Arguments:
|
92
|
+
# name -- Name of the affected company/ organisation
|
93
|
+
#
|
94
|
+
# arrest -- whether the incident resulted in an arrest
|
95
|
+
# breaches -- the type of breach that occurred (Hack, MissingLaptop etc.)
|
96
|
+
# country -- country where the incident took place
|
97
|
+
# ext -- whether an external, third party was affected
|
98
|
+
# ext_names -- the name of the third party company that was affected
|
99
|
+
# lawsuit -- whether the incident resulted in a lawsuit
|
100
|
+
# records -- the number of records that were lost/ stolen
|
101
|
+
# recovered -- whether the affected items were recovered
|
102
|
+
# sub_types -- the sub-categorization of the affected company/ organization
|
103
|
+
# source -- whether the incident occurred from inside or outside the organization
|
104
|
+
# stocks -- stock symbol of the affected company
|
105
|
+
# types -- the basic type of organization (government, business, educational)
|
106
|
+
# uid -- unique ID for the incident
|
107
|
+
def search(params={})
|
108
|
+
return @api.request('datalossdb/search', params)
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
# The ExploitDB class shouldn't be used independently,
|
114
|
+
# as it depends on the WebAPI class.
|
115
|
+
#
|
116
|
+
# Author:: achillean (mailto:jmath at surtri.com)
|
117
|
+
#
|
118
|
+
# :title:Shodan::ExploitDB
|
119
|
+
class ExploitDB
|
120
|
+
attr_accessor :api
|
121
|
+
|
122
|
+
def initialize(api)
|
123
|
+
@api = api
|
124
|
+
end
|
125
|
+
|
126
|
+
# Download the exploit code from the ExploitDB archive.
|
127
|
+
#
|
128
|
+
# Arguments:
|
129
|
+
# id -- ID of the ExploitDB entry
|
130
|
+
#
|
131
|
+
# Returns:
|
132
|
+
# A hash with the following fields:
|
133
|
+
# filename -- Name of the file
|
134
|
+
# content-type -- Mimetype
|
135
|
+
# data -- Contents of the file
|
136
|
+
def download(id)
|
137
|
+
return @api.request('exploitdb/download', {:id => "#{id}"})
|
138
|
+
end
|
139
|
+
|
140
|
+
# Search the ExploitDB archive.
|
141
|
+
#
|
142
|
+
# Arguments:
|
143
|
+
# query -- Search terms
|
144
|
+
#
|
145
|
+
# Optional arguments:
|
146
|
+
# author -- Name of the exploit submitter
|
147
|
+
# platform -- Target platform (e.g. windows, linux, hardware etc.)
|
148
|
+
# port -- Service port number
|
149
|
+
# type -- Any, dos, local, papers, remote, shellcode and webapps
|
150
|
+
#
|
151
|
+
# Returns:
|
152
|
+
# A dictionary with 2 main items: matches (list) and total (int).
|
153
|
+
# Each item in 'matches' is a dictionary with the following elements:
|
154
|
+
#
|
155
|
+
# id
|
156
|
+
# author
|
157
|
+
# date
|
158
|
+
# description
|
159
|
+
# platform
|
160
|
+
# port
|
161
|
+
# type
|
162
|
+
def search(query, params={})
|
163
|
+
params[:q] = query
|
164
|
+
return @api.request('exploitdb/search', params)
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
# The Msf class shouldn't be used independently,
|
170
|
+
# as it depends on the WebAPI class.
|
171
|
+
#
|
172
|
+
# Author:: achillean (mailto:jmath at surtri.com)
|
173
|
+
#
|
174
|
+
# :title:Shodan::Msf
|
175
|
+
class Msf
|
176
|
+
attr_accessor :api
|
177
|
+
|
178
|
+
def initialize(api)
|
179
|
+
@api = api
|
180
|
+
end
|
181
|
+
|
182
|
+
# Download a metasploit module given the fullname (id) of it.
|
183
|
+
#
|
184
|
+
# Arguments:
|
185
|
+
# id -- fullname of the module (ex. auxiliary/admin/backupexec/dump)
|
186
|
+
#
|
187
|
+
# Returns:
|
188
|
+
# A dictionary with the following fields:
|
189
|
+
# # filename -- Name of the file
|
190
|
+
# content-type -- Mimetype
|
191
|
+
# data -- File content
|
192
|
+
def download(id)
|
193
|
+
return @api.request('msf/download', {:id => "#{id}"})
|
194
|
+
end
|
195
|
+
|
196
|
+
# Search for a metasploit module
|
197
|
+
def search(query, params={})
|
198
|
+
params[:q] = query
|
199
|
+
return @api.request('msf/search', params)
|
200
|
+
end
|
201
|
+
|
202
|
+
end
|
203
|
+
|
204
|
+
end
|
data/lib/shodan/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
module Shodan
|
2
|
-
Version = VERSION = '0.
|
3
|
-
end
|
1
|
+
module Shodan
|
2
|
+
Version = VERSION = '0.6.0'
|
3
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shodan
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 7
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 6
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.6.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- John Matherly
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2012-08-23 00:00:00 -07:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -24,7 +24,7 @@ dependencies:
|
|
24
24
|
requirement: &id001 !ruby/object:Gem::Requirement
|
25
25
|
none: false
|
26
26
|
requirements:
|
27
|
-
- -
|
27
|
+
- - ">="
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
hash: 11
|
30
30
|
segments:
|
@@ -47,9 +47,9 @@ files:
|
|
47
47
|
- README.md
|
48
48
|
- LICENSE
|
49
49
|
- HISTORY.md
|
50
|
-
- lib/shodan.rb
|
51
50
|
- lib/shodan/version.rb
|
52
51
|
- lib/shodan/api.rb
|
52
|
+
- lib/shodan.rb
|
53
53
|
has_rdoc: true
|
54
54
|
homepage: http://github.com/achillean/shodan-ruby
|
55
55
|
licenses: []
|