shodan 0.6.1 → 1.0.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  Visit the official Shodan API documentation at:
2
2
 
3
- [http://docs.shodanhq.com](http://docs.shodanhq.com)
3
+ [https://developer.shodan.io](https://developer.shodan.io)
4
4
 
5
5
  ## Installation
6
6
 
@@ -14,19 +14,32 @@ Before you can use the API, you need to have an API key.
14
14
 
15
15
  [Get your API key here](http://www.shodanhq.com/api_doc)
16
16
 
17
- Setup the SHODAN WebAPI:
17
+ Setup the Shodan wrapper object:
18
18
 
19
19
  require 'shodan'
20
20
 
21
- api = Shodan::WebAPI.new(MY_API_KEY)
21
+ api = Shodan::Shodan.new(MY_API_KEY)
22
22
 
23
23
  Print a list of cisco-ios devices:
24
24
 
25
25
  result = api.search("cisco-ios")
26
26
  result['matches'].each{ |host|
27
- puts host['ip']
27
+ puts host['ip_str']
28
28
  }
29
29
 
30
+ Print the 2nd page of results for the cisco-ios query:
31
+
32
+ result = api.search("cisco-ios", :page => 2)
33
+ result['matches'].each{ |host|
34
+ puts host['ip_str']
35
+ }
36
+
37
+ Find out how many results there are for "apache" and also return the top 5 organizations for the results:
38
+
39
+ result = api.count("apache", :facets => 'org:5')
40
+ puts "Total number of results: #{result['total']}"
41
+ puts result['facets']
42
+
30
43
  Get all the information SHODAN has on the IP 217.140.75.46:
31
44
 
32
45
  host = api.host('217.140.75.46')
@@ -41,7 +54,3 @@ To properly handle potential errors, you should wrap all requests in a try/excep
41
54
  else
42
55
  puts "Unknown error"
43
56
  end
44
-
45
- ## Articles
46
-
47
- * [Perl, Python and Ruby API libraries](http://www.surtri.com/2010/10/20/perl-python-ruby-api/)
@@ -1,3 +1,4 @@
1
1
  # Import all the SHODAN classes
2
2
  require 'shodan/api'
3
+ require 'shodan/shodan'
3
4
  require 'shodan/version'
@@ -0,0 +1,147 @@
1
+ require 'rubygems'
2
+ require 'cgi'
3
+ require 'json'
4
+ require 'openssl'
5
+ require 'net/http'
6
+
7
+ module Shodan
8
+
9
+ # The Shodan class interfaces with the official Shodan API.
10
+ # For more information on the API, please visit https://developer.shodan.io
11
+ #
12
+ # Author:: achillean (mailto:jmath@shodan.io)
13
+ #
14
+ # :title:Shodan::Shodan
15
+ class Shodan
16
+ attr_accessor :api_key
17
+ attr_accessor :base_url
18
+ attr_accessor :exploits
19
+
20
+ def initialize(api_key)
21
+ @api_key = api_key
22
+ @base_url = "https://api.shodan.io/"
23
+ @base_url_exploits = "https://exploits.shodan.io/api/"
24
+
25
+ @exploits = Exploits.new(self)
26
+ end
27
+
28
+ # Internal method that sends out the HTTP request.
29
+ # Expects a webservice function (ex. 'search') name and a hash of arguments.
30
+ def request(type, func, args)
31
+ if type == "exploits"
32
+ base_url = @base_url_exploits
33
+ else
34
+ base_url = @base_url
35
+ end
36
+
37
+ # Convert the argument hash into a string
38
+ args_string = args.map{|k, v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}"}.join("&")
39
+
40
+ # Craft the final request URL
41
+ url = "#{base_url}#{func}?key=#{@api_key}&#{args_string}"
42
+
43
+ # Send the request
44
+ puts url
45
+ uri = URI.parse(url)
46
+ http = Net::HTTP.new(uri.host, uri.port)
47
+ http.use_ssl = true
48
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
49
+ response = http.get(uri.request_uri)
50
+
51
+ # Convert the JSON data into a native Ruby hash
52
+ data = JSON.parse(response.body)
53
+
54
+ # Raise an error if something went wrong
55
+ if data.has_key? 'error'
56
+ raise data['error']
57
+ end
58
+
59
+ return data
60
+ end
61
+
62
+ # Get all available information on an IP.
63
+ #
64
+ # Arguments:
65
+ # ip - host IP (string)
66
+ #
67
+ # Returns a hash containing the host information
68
+ def host(ip)
69
+ return request('shodan', "shodan/host/#{ip}", {})
70
+ end
71
+
72
+ # Perform a search on Shodan.
73
+ #
74
+ # Arguments:
75
+ # query - search query; same format as the website (string)
76
+ #
77
+ # Returns a hash containing the search results
78
+ def search(query, params={})
79
+ params[:query] = query
80
+ return request('shodan', 'shodan/host/search', params)
81
+ end
82
+
83
+ # Find how many results there are for a search term.
84
+ #
85
+ # Arguments:
86
+ # query - search query; same format as the website (string)
87
+ #
88
+ # Returns a hash containing the total number of search results
89
+ def count(query, params={})
90
+ params[:query] = query
91
+ return request('shodan', 'shodan/host/count', params)
92
+ end
93
+
94
+ # Returns information about the current API key.
95
+ def info()
96
+ return request('shodan', 'api-info', {})
97
+ end
98
+ end
99
+
100
+ # The Exploits class shouldn't be used independently,
101
+ # as it depends on the Shodan class.
102
+ #
103
+ # Author:: achillean (mailto:jmath@shodan.io)
104
+ #
105
+ # :title:Shodan::Exploits
106
+ class Exploits
107
+ attr_accessor :api
108
+
109
+ def initialize(api)
110
+ @api = api
111
+ end
112
+
113
+ # Search the Shodan Exploits archive for exploits.
114
+ #
115
+ # Arguments:
116
+ # query -- Search terms
117
+ #
118
+ # Optional arguments:
119
+ # facets -- A comma-separated list of properties to get summary information on.
120
+ # page -- The page number to page through results 100 exploits at a time.
121
+ #
122
+ # Returns:
123
+ # A dictionary with 2 main items: matches (list) and total (int).
124
+ # Please visit https://developer.shodan.io/api/exploit-specification for up-to-date information on what an Exploit result contains.
125
+ def search(query, params={})
126
+ params[:query] = query
127
+ return @api.request('exploits', 'search', params)
128
+ end
129
+
130
+ # Search the Shodan Exploits archive for exploits but don't return results, only the number of matches.
131
+ #
132
+ # Arguments:
133
+ # query -- Search terms
134
+ #
135
+ # Optional arguments:
136
+ # facets -- A comma-separated list of properties to get summary information on.
137
+ #
138
+ # Returns:
139
+ # A dictionary with 1 main item: total (int).
140
+ def count(query, params={})
141
+ params[:query] = query
142
+ return @api.request('exploits', 'count', params)
143
+ end
144
+
145
+ end
146
+
147
+ end
@@ -1,3 +1,3 @@
1
1
  module Shodan
2
- Version = VERSION = '0.6.1'
2
+ Version = VERSION = '1.0.0'
3
3
  end
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # shodan_ips.py
4
+ # Search SHODAN and print a list of IPs matching the query
5
+ #
6
+ # Author: achillean
7
+ $:.unshift File.dirname(__FILE__)
8
+
9
+ require 'rubygems'
10
+ require 'shodan'
11
+
12
+ # Configuration
13
+ API_KEY = "eMLZhC7qt42If0U8ndfZTlCWSSxFYass"
14
+
15
+ # Input validation
16
+ if ARGV.length == 0
17
+ puts "Usage: ./shodan_ips.rb <search query>"
18
+ exit 1
19
+ end
20
+
21
+ begin
22
+ # Setup the API
23
+ api = Shodan::Shodan.new(API_KEY)
24
+
25
+ # Perform the search
26
+ query = ARGV.join(" ")
27
+
28
+ puts "\n--- shodan.info ---"
29
+ result = api.info()
30
+ puts result
31
+
32
+ puts '--- shodan.search ---'
33
+ result = api.search(query, :page => 2, :facets => 'port')
34
+
35
+ # Loop through the matches and print the IPs
36
+ puts "Total: #{result['total']}"
37
+ result['matches'][0..10].each{ |host|
38
+ puts host['ip_str']
39
+ }
40
+
41
+ puts "\nTop 10 Ports"
42
+ result['facets']['port'].each{ |facet|
43
+ puts "#{facet['value']}: #{facet['count']}"
44
+ }
45
+
46
+ puts "\n--- shodan.count ---"
47
+ result = api.count(query, :facets => 'port')
48
+
49
+ # Loop through the matches and print the IPs
50
+ puts "Total: #{result['total']}"
51
+ result['matches'][0..10].each{ |host|
52
+ puts host['ip_str']
53
+ }
54
+
55
+ puts "\nTop 10 Ports"
56
+ result['facets']['port'].each{ |facet|
57
+ puts "#{facet['value']}: #{facet['count']}"
58
+ }
59
+
60
+ puts "\n--- shodan.host ---"
61
+ result = api.host('217.140.75.46')
62
+ puts result
63
+ rescue Exception => e
64
+ puts "Error: #{e.to_s}"
65
+ exit 1
66
+ end
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # shodan_ips.py
4
+ # Search SHODAN and print a list of IPs matching the query
5
+ #
6
+ # Author: achillean
7
+ $:.unshift File.dirname(__FILE__)
8
+
9
+ require 'rubygems'
10
+ require 'shodan'
11
+
12
+ # Configuration
13
+ API_KEY = "eMLZhC7qt42If0U8ndfZTlCWSSxFYass"
14
+
15
+ # Input validation
16
+ if ARGV.length == 0
17
+ puts "Usage: ./shodan_ips.rb <search query>"
18
+ exit 1
19
+ end
20
+
21
+ begin
22
+ # Setup the API
23
+ api = Shodan::Shodan.new(API_KEY)
24
+
25
+ # Perform the search
26
+ query = ARGV.join(" ")
27
+
28
+ puts "\n--- shodan.exploits.search ---"
29
+ result = api.exploits.search(query, :page => 2, :facets => 'author')
30
+
31
+ # Loop through the matches and print the IPs
32
+ puts "Total: #{result['total']}"
33
+ result['matches'][0..10].each{ |exploit|
34
+ puts exploit
35
+ }
36
+
37
+ puts result['facets']
38
+
39
+ puts "\n--- shodan.exploits.search ---"
40
+ result = api.exploits.count(query, :facets => 'author')
41
+
42
+ # Loop through the matches and print the IPs
43
+ puts "Total: #{result['total']}"
44
+ result['matches'][0..10].each{ |exploit|
45
+ puts exploit
46
+ }
47
+
48
+ puts result['facets']
49
+ rescue Exception => e
50
+ puts "Error: #{e.to_s}"
51
+ exit 1
52
+ end
metadata CHANGED
@@ -1,88 +1,75 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: shodan
3
- version: !ruby/object:Gem::Version
4
- hash: 5
5
- prerelease: false
6
- segments:
7
- - 0
8
- - 6
9
- - 1
10
- version: 0.6.1
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - John Matherly
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2012-08-26 00:00:00 -07:00
19
- default_executable:
20
- dependencies:
21
- - !ruby/object:Gem::Dependency
12
+ date: 2014-02-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
22
15
  name: json
23
- prerelease: false
24
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
25
17
  none: false
26
- requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- hash: 11
30
- segments:
31
- - 1
32
- - 4
33
- - 6
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
34
21
  version: 1.4.6
35
22
  type: :runtime
36
- version_requirements: *id001
37
- description: " A Ruby library to interact with the SHODAN API.\n"
38
- email: jmath@surtri.com
39
- executables: []
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.4.6
30
+ description: ! ' A Ruby library to interact with the Shodan API.
40
31
 
32
+ '
33
+ email: jmath@shodan.io
34
+ executables: []
41
35
  extensions: []
42
-
43
- extra_rdoc_files:
36
+ extra_rdoc_files:
44
37
  - LICENSE
45
38
  - README.md
46
- files:
39
+ files:
47
40
  - README.md
48
41
  - LICENSE
49
42
  - HISTORY.md
43
+ - lib/test_exploits.rb
44
+ - lib/test.rb
45
+ - lib/shodan.rb
50
46
  - lib/shodan/version.rb
47
+ - lib/shodan/shodan.rb
51
48
  - lib/shodan/api.rb
52
- - lib/shodan.rb
53
- has_rdoc: true
54
49
  homepage: http://github.com/achillean/shodan-ruby
55
50
  licenses: []
56
-
57
51
  post_install_message:
58
- rdoc_options:
52
+ rdoc_options:
59
53
  - --charset=UTF-8
60
- require_paths:
54
+ require_paths:
61
55
  - lib
62
- required_ruby_version: !ruby/object:Gem::Requirement
56
+ required_ruby_version: !ruby/object:Gem::Requirement
63
57
  none: false
64
- requirements:
65
- - - ">="
66
- - !ruby/object:Gem::Version
67
- hash: 3
68
- segments:
69
- - 0
70
- version: "0"
71
- required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
63
  none: false
73
- requirements:
74
- - - ">="
75
- - !ruby/object:Gem::Version
76
- hash: 3
77
- segments:
78
- - 0
79
- version: "0"
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
80
68
  requirements: []
81
-
82
69
  rubyforge_project:
83
- rubygems_version: 1.3.7
70
+ rubygems_version: 1.8.23
84
71
  signing_key:
85
72
  specification_version: 3
86
- summary: A Ruby library to interact with the SHODAN API.
73
+ summary: A Ruby library to interact with the Shodan API.
87
74
  test_files: []
88
-
75
+ has_rdoc: