Shodan

Shodanz

This section comes from Shodan Pentesting Guide on TurgenSec blog written by noraj.

Installation

In a virtual ruby environment like rbenv:

$ gem install shodanz

Then the API key will always be initialized like that in our code:

require 'shodanz'

api = Shodanz.client.new(key: 'YOUR_API_KEY')

For production projects you may prefer read the API key via the environment variable SHODAN_API_KEY.

Examples

# Search Shodan
results = api.host_search('apache')

# Show results
puts "Results found: #{results['total']}"
results['matches'].each do |result|
  puts "IP: #{result['ip_str']}"
  puts result['data'] + "\n"
end

Example of output:

IP: 154.218.139.58
HTTP/1.1 200 OK
Date: Tue, 28 Jan 2020 22:13:53 GMT
Server: Apache
Upgrade: h2
Connection: Upgrade, close
Last-Modified: Wed, 26 Apr 2017 08:03:47 GMT
ETag: "52e-54e0d47a39ec0"
Accept-Ranges: bytes
Content-Length: 1326
Vary: Accept-Encoding
Content-Type: text/html


IP: 132.148.235.102
HTTP/1.1 200 OK
Date: Tue, 28 Jan 2020 22:13:53 GMT
Server: Apache
Upgrade: h2,h2c
Connection: Upgrade
Last-Modified: Fri, 10 May 2019 09:10:49 GMT
ETag: "a4edb-7ab-58884f152c219"
Accept-Ranges: bytes
Content-Length: 1963
Vary: Accept-Encoding,User-Agent
Content-Type: text/html


IP: 112.126.140.94
HTTP/1.1 404 Not Found
Date: Tue, 28 Jan 2020 22:13:34 GMT
Server: Apache
X-Powered-By: PHP/5.2.17
X-UA-Compatible: IE=EmulateIE7
Transfer-Encoding: chunked
Content-Type: text/html

Available ports of a host

# Lookup the host
host = api.host('1.1.1.1')

# Print general info
puts "
IP: #{host['ip_str']}
Organization: #{host['org'] || 'n/a'}
Operating System: #{host['os'] || 'n/a'}
"

# Print all banners
host['data'].each do |item|
  puts "
Port: #{item['port'] || 'n/a'}
Banner: #{item['data'] || 'n/a'}

"
end

Example of output:

IP: 1.1.1.1
Organization: Mountain View Communications
Operating System: n/a

Port: 443
Banner: HTTP/1.1 403 Forbidden
Server: cloudflare
Date: Tue, 28 Jan 2020 18:34:35 GMT
Content-Type: text/html
Content-Length: 553
Connection: keep-alive
CF-RAY: 55c50fb4e8149d5a-AMS




Port: 80
Banner: HTTP/1.1 409 Conflict
Date: Tue, 28 Jan 2020 17:26:54 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: close
Set-Cookie: __cfduid=d189a930262f96d94a707a90d853a56bd1580232414; expires=Thu, 27-Feb-20 17:26:54 GMT; path=/; domain=.www.1yhaoduo.com; HttpOnly; SameSite=Lax
Cache-Control: max-age=6
Expires: Tue, 28 Jan 2020 17:27:00 GMT
X-Frame-Options: SAMEORIGIN
Vary: Accept-Encoding
Server: cloudflare
CF-RAY: 55c4ac8fba63801a-SAN




Port: 53
Banner:
Recursion: enabled
Resolver ID: AMS

Displaying stats

# The list of properties we want summary information on
FACETS = {
  'org': 3,
  'domain': 5,
  'port': 5,
  'asn': 5,
  'country': 10,
}

FACET_TITLES = {
  'org': 'Top 3 Organizations',
  'domain': 'Top 5 Domains',
  'port': 'Top 5 Ports',
  'asn': 'Top 5 Autonomous Systems',
  'country': 'Top 10 Countries',
}

# Query
query = 'apache 2.4'

# Count results
result = api.host_count(query, facets: FACETS)
puts 'Shodan Summary Information'
puts "Query: #{query}"
puts "Total Results: #{result['total']}\n"

# Print the summary info from the facets
result['facets'].each do |facet, _v|
  puts FACET_TITLES[facet]

  result['facets'][facet].each do |term|
    puts "#{term['value']}: #{term['count']}"
  end

  # Print an empty line between summary info
  puts ''
end

Example of output:

Shodan Summary Information
Query: apache 2.4
Total Results: 63939

Liquid Web, L.L.C: 23126
Amazon.com: 7843
Hetzner Online GmbH: 1798


amazonaws.com: 10398
telecom.net.ar: 1609
your-server.de: 1232
t-ipconnect.de: 629
vultr.com: 450


80: 21131
443: 19772
8080: 3023
10000: 1672
8081: 1372


as53824: 13810
as32244: 9316
as16509: 6138
as24940: 1740
as7303: 1410


US: 30877
DE: 5781
CN: 4432
BR: 2949
AR: 1757
JP: 1472
GB: 1168
IN: 1030
FR: 720
CA: 613

Async support with the stream API

require 'async'
require 'shodanz'

api = Shodanz.client.new(key: 'YOUR_API_KEY')

# Asynchronously stream banner info from shodan and check any
# IP addresses against the experimental honeypot scoring service.
api.streaming_api.banners do |banner|
  if ip = banner['ip_str']
    Async do
      score = api.rest_api.honeypot_score(ip).wait
      puts "#{ip} has a #{score * 100}% chance of being a honeypot"
    rescue Shodanz::Errors::RateLimited
      sleep rand
      retry
    rescue # any other errors
      next
    end
  end
end

Warning: Freelancer API plan or better required for using the stream API, developer or free plan won't work.

Note: this async example comes from the shodanz documentation.

Useful methods

# Returns all the protocols that can be used when launching an Internet scan
api.protocols

# Returns a list of port numbers that the Shodan crawlers are looking for
api.ports

# Returns information about the Shodan account linked to this API key
api.profile

# Look up the IP address for the provided list of hostnames
api.resolve('archlinux.org', 'blackarch.org')

# Look up the hostnames that have been defined for the given list of IP addresses
api.reverse_lookup('138.201.81.199', '176.31.253.211')

# Get your current IP address as seen from the Internet
api.my_ip

# Calculates a honeypot probability score ranging from 0 (not a honeypot) to 1.0 (is a honeypot)
api.honeypot_score('1.1.1.1')

# API Plan Information
api.info

Exploits API

puts client.exploit_count(port: 22, page: 1)
puts client.exploit_search('rce couchdb', type: 'remote', platform: 'linux', author: 'Metasploit')

You can find more examples here or read the shodanz API documentation.

Last updated