Shodan Search Tool w/My Ruby API Class

Today I just wanted to share a little something I made for Shodan. If you don't know whatShodan is, then I highly recommend you check them out and do some quick googling to see what others have done with its help. I initially tried using their published ruby gem and published API documentation but it failed miserably (likely could just be me, but seems their code is outdated with how their site provides output now, idk). I really like Shodan though so I decided to create my own version of their API so I could get started on making a cool search assistant I can run from the command line with some basic logging for analysis after. Once I finished redoing the API class, I made a little CLI based search tool to make quick Shodan research a snap and am now sharing with the rest of the world, hope its helpful for others.

Prerequisites:
sudo gem install colorize curb json nokogiri

NOTE: curb uses libcurl under the hood so you might need to install this if not already included on your OS 

Basic Help Menu:


You can run a basic Shodan search and display the results, which are also logged to the results folder.


The logged results are overwritten on each search so you need to rename it or move it if you want to use it later and plan to run multiple searches. 

I also made option for quick search which runs a Shodan search and returns the list of IP addresses from results, skipping all the details. I typically run a normal search, then a follow up quick search on same keywords to pass of lists to other tools in a speedy fashion while manual review is more involved with the full search results...

Shodan also offers up a nice search feature to search for exploits which leverages multiple exploit databases. I currently have the Exploit-db and Metasploit search engines available and fully working. This means you can easily search for known exploits with variety of keywords and get matching results displayed and logged for you. 

You can even download the exploit/poc code from search results by referencing the ID number from results.

ToDo List: 
Include options to search tool for premium search options (somewhat built into my API Class already but not in tool). Include a Gemfile for easy installs for bundler lovers. Also I have not uploaded things to Github yet as I fried my old box and lost a lot of stuff, working on recovery still but should have it updated soon. Until then you can find things on Pastebin, available for a long while...

My Shodan API Standalone Class:

Direct link: http://pastebin.com/q6LZJqcD


My Shodan API Search Tool, Source Code:

Data hosted with ♥ by Pastebin.com - Download Raw - See Original
  1. #!/usr/bin/env ruby
  2. #
  3. # Shodan API Search Assistant
  4. # By: Hood3dRob1n
  5. #
  6.  
  7. ########### ENTER API KEY HERE  ###########
  8. APIKEY='YOURAPIKEYNEEDS2GORIGHTINHEREYO!' #
  9. ###########################################
  10.  
  11. ##### STD GEMS #######
  12. require 'fileutils'  #
  13. require 'optparse'   #
  14. require 'resolv'     #
  15. #### NON-STD GEMS ####
  16. require 'rubygems'   #
  17. require 'colorize'   #
  18. require 'curb'       #
  19. require 'json'       #
  20. require 'nokogiri'   #
  21. ######################
  22.  
  23. HOME=File.expand_path(File.dirname(__FILE__))
  24. RESULTS = HOME + '/results/'
  25.  
  26. # Banner
  27. def banner
  28.   puts
  29.   puts "Shodan API Search Assistant".light_green
  30.   puts "By".light_green + ": Hood3dRob1n".white
  31. end
  32.  
  33. # Clear Terminal
  34. def cls
  35.   if RUBY_PLATFORM =~ /win32|win64|\.NET|windows|cygwin|mingw32/i
  36.     system('cls')
  37.   else
  38.     system('clear')
  39.   end
  40. end
  41.  
  42. # Custom ShodanAPI Class :)
  43. # The pre-built option is broken and doesn't work in several places....
  44. # So we re-wrote it!
  45. class ShodanAPI
  46.   # Initialize ShodanAPI via passed API Key
  47.   def initialize(apikey)
  48.     @url="http://www.shodanhq.com/api/"
  49.     if shodan_connect(apikey)
  50.         @key=apikey
  51.     end
  52.   end
  53.  
  54.   # Check API Key against API Info Query
  55.   # Return True on success, False on Error or Failure
  56.   def shodan_connect(apikey)
  57.     url = @url + "info?key=#{apikey}"
  58.     begin
  59.       c = Curl::Easy.perform(url)
  60.       if c.body_str =~ /"unlocked_left": \d+"telnet": .+"plan":".+""https": .+"unlocked": .+/i
  61.         results = JSON.parse(c.body_str)
  62.         @plan = results['plan']
  63.         @unlocked = results['unlocked']
  64.         @unlocks = results['unlocked_left']
  65.         @https = results['https']
  66.         @telnet = results['telnet']
  67.         return true
  68.       elsif c.body_str =~ /"error""API access denied"/i
  69.         puts "Access Denied using API Key '#{apikey}'".light_red +"!".white
  70.         puts "Check Key & Try Again".light_red + "....".white
  71.         return false
  72.       else
  73.         puts "Unknown Problem with Connection to Shodan API".light_green + "!".white
  74.         return false
  75.       end
  76.     rescue => e
  77.       puts "Problem with Connection to Shodan API".light_red +"!".white
  78.       puts "\t=> #{e}"
  79.       return false
  80.     end
  81.   end
  82.  
  83.   # Just checks our key is working (re-using shodan_connect so updates @unlocks)
  84.   # Returns True or False
  85.   def connected?
  86.     if shodan_connect(@key)
  87.       return true
  88.     else
  89.       return  false
  90.     end
  91.   end
  92.  
  93.   # Return the number of unlocks remaining
  94.   def unlocks
  95.     if shodan_connect(@key)
  96.       return @unlocks.to_i
  97.     else
  98.       return nil
  99.     end
  100.   end
  101.  
  102.   # Check if HTTPS is Enabled
  103.   def https?
  104.     if shodan_connect(@key)
  105.       if @https
  106.         return true
  107.       else
  108.         return false
  109.       end
  110.     else
  111.       return false
  112.     end
  113.   end
  114.  
  115.   # Check if Telnet is Enabled
  116.   def telnet?
  117.     if shodan_connect(@key)
  118.       if @telnet
  119.         return true
  120.       else
  121.         return false
  122.       end
  123.     else
  124.       return false
  125.     end
  126.   end
  127.  
  128.   # Actually display Basic Info for current API Key
  129.   def info
  130.     url = @url + 'info?key=' + @key
  131.     begin
  132.       c = Curl::Easy.perform(url)
  133.       results = JSON.parse(c.body_str)
  134.       puts
  135.       puts "Shodan API Key Confirmed".light_green + "!".white
  136.       puts "API Key".light_green + ": #{@key}".white
  137.       puts "Plan Type".light_green + ": #{results['plan']}".white
  138.       puts "Unlocked".light_green + ": #{results['unlocked']}".white
  139.       puts "Unlocks Remaining".light_green + ": #{results['unlocked_left']}".white
  140.       puts "HTTPS Enabled".light_green + ": #{results['https']}".white
  141.       puts "Telnet Enabled".light_green + ": #{results['telnet']}".white
  142.       return true
  143.     rescue => e
  144.       puts "Problem with Connection to Shodan API".light_red +"!".white
  145.       puts "\t=> #{e}".white
  146.       return false
  147.     end
  148.   end
  149.  
  150.   # Lookup all available information for a specific IP address
  151.   # Returns results hash or nil
  152.   def host(ip)
  153.     url = @url + 'host?ip=' + ip + '&key=' + @key
  154.     begin
  155.       c = Curl::Easy.perform(url)
  156.       results = JSON.parse(c.body_str)
  157.       return results
  158.     rescue => e
  159.       puts "Problem running Host Search".light_red + "!".white
  160.       puts "\t=> #{e}".white
  161.       return nil
  162.     end
  163.   end
  164.  
  165.   # Returns the number of devices that a search query found
  166.   # Unrestricted usage of all advanced filters
  167.   # Return results count or nil on failure
  168.   def count(string)
  169.     url = @url + 'count?q=' + string + '&key=' + @key
  170.     begin
  171.       c = Curl::Easy.perform(url)
  172.       results = JSON.parse(c.body_str)
  173.       return results['total']
  174.     rescue => e
  175.       puts "Problem grabbing results count".light_red + "!".white
  176.       puts "\t=> #{e}".white
  177.       return nil
  178.     end
  179.   end
  180.  
  181.   # Search Shodan for devices using a search query
  182.   # Returns results hash or nil
  183.   def search(string, filters={})
  184.     prem_filters =  [ 'city''country''geo''net''before','after''org''isp''title''html' ]
  185.     cheap_filters = [ 'hostname''os''port' ]
  186.     url = @url + 'search?q=' + string
  187.     if not filters.empty?
  188.       filters.each do |k, v|
  189.         if cheap_filters.include?(k)
  190.           url +' ' + k + ":\"#{v}\""
  191.         end
  192.         if prem_filters.include?(k)
  193.           if @unlocks.to_i > 1
  194.             url +' ' + k + ":\"#{v}\""
  195.             @unlocks = @unlocks.to_i - 1 # Remove an unlock for use of filter
  196.           else
  197.             puts "Not Enough Unlocks Left to run Premium Filter Search".light_red + "!".white
  198.             puts "Try removing '#{k}' filter and trying again".light_red + "....".white
  199.             return nil
  200.           end
  201.         end
  202.       end
  203.     end
  204.     url +'&key=' + @key
  205.     begin
  206.       c = Curl::Easy.perform(url)
  207.       results = JSON.parse(c.body_str)
  208.       return results
  209.     rescue => e
  210.       puts "Problem running Shodan Search".light_red + "!".white
  211.       puts "\t=> #{e}".white
  212.       return nil
  213.     end
  214.   end
  215.  
  216.   # Quick Search Shodan for devices using a search query
  217.   # Results are limited to only the IP addresses
  218.   # Returns results array or nil
  219.   def quick_search(string, filters={})
  220.     prem_filters =  [ 'city''country''geo''net''before','after''org''isp''title''html' ]
  221.     cheap_filters = [ 'hostname''os''port' ]
  222.     url = @url + 'search?q=' + string
  223.     if not filters.empty?
  224.       filters.each do |k, v|
  225.         if cheap_filters.include?(k)
  226.           url +' ' + k + ":\"#{v}\""
  227.         end
  228.         if prem_filters.include?(k)
  229.           if @unlocks.to_i > 1
  230.             url +' ' + k + ":\"#{v}\""
  231.             @unlocks = @unlocks.to_i - 1
  232.           else
  233.             puts "Not Enough Unlocks Left to run Premium Filter Search".light_red + "!".white
  234.             puts "Try removing '#{k}' filter and trying again".light_red + "....".white
  235.             return nil
  236.           end
  237.         end
  238.       end
  239.     end
  240.     url +'&key=' + @key
  241.     begin
  242.       ips=[]
  243.       c = Curl::Easy.perform(url)
  244.       results = JSON.parse(c.body_str)
  245.       results['matches'].each do |host|
  246.        ips << host['ip']
  247.       end
  248.       return ips
  249.     rescue => e
  250.       puts "Problem running Shodan Quick Search".light_red +"!".white
  251.       puts "\t=> #{e}".white
  252.       return nil
  253.     end
  254.   end
  255.  
  256.   # Perform Shodan Exploit Search as done on Web
  257.   # Provide Search String and source
  258.   # Source can be: metasploit, exploitdb, or cve
  259.   # Returns results hash array on success: { downloadID => { link => description } }
  260.   # Returns nil on failure
  261.   def sploit_search(string, source)
  262.     sources = [ "metasploit""exploitdb""cve" ]
  263.     if sources.include?(source.downcase)
  264.       sploits = 'https://exploits.shodan.io/?q=' + string + ' source:"' + source.downcase + '"'
  265.       begin
  266.         results={}
  267.         c = Curl::Easy.perform(sploits)
  268.         page = Nokogiri::HTML(c.body_str) # Parsable doc object now
  269.         # Enumerate target section, parse out link & description
  270.         page.css('div[class="search-result well"]').each do|linematch|
  271.           if linematch.to_s =~ /<div class="search-result well">\s+<a href="(.+)"\s/
  272.             link=$1
  273.           end
  274.           if linematch.to_s =~ /class="title">(.+)\s+<\/a>/
  275.             desc=$1.gsub('<em>''').gsub('</em>''')
  276.           end
  277.           case source.downcase
  278.           when 'cve'
  279.             dl_id = 'N/A for CVE Search'
  280.           when 'exploitdb'
  281.             dl_id = link.split('/')[-1] unless link.nil?
  282.           when 'metasploit'
  283.             dl_id = link.sub('http://www.metasploit.com/','').sub(/\/$/'') unless link.nil?
  284.           end
  285.           results.store(dl_id, { link => desc}) unless (link.nilorlink == '') or (desc.nilor desc == '') or (dl_id.nilor dl_id =='N/A for CVE Search')
  286.         end
  287.         return results
  288.       rescue Curl::Err::ConnectionFailedError => e
  289.         puts "Shitty connection yo".light_red + ".....".white
  290.         return nil
  291.       rescue => e
  292.         puts "Unknown connection problem".light_red + ".....".white
  293.         puts "\t=> #{e}".white
  294.         return nil
  295.       end
  296.     else
  297.       puts "Invalid Search Source Requested".light_red + "!".white
  298.       return nil
  299.     end
  300.   end
  301.  
  302.   # Download Exploit Code from Exploit-DB or MSF Github Page
  303.   # By passing in the Download ID (which can be seen in sploit_search() results)
  304.   # Return { 'Download' => dl_link, 'Viewing' => v_link, 'Exploit' => c.body_str }
  305.   # or nil on failure
  306.   def sploit_download(id, source)
  307.     sources = [ "metasploit""exploitdb" ]
  308.     if sources.include?(source.downcase)
  309.       case source.downcase
  310.       when 'exploitdb'
  311.         dl_link = "http://www.exploit-db.com/download/#{id}/"
  312.         v_link = "http://www.exploit-db.com/exploits/#{id}/"
  313.       when 'metasploit'
  314.         dl_link = "https://raw.github.com/rapid7/metasploit-framework/master/#{id.sub('/exploit/', '/exploits/')}.rb"
  315.         v_link = "http://www.rapid7.com/db/#{id}/"
  316.       end
  317.       begin
  318.         c = Curl::Easy.perform(dl_link)
  319.         page = Nokogiri::HTML(c.body_str) # Parsable doc object now
  320.         results = { 'Download' => dl_link, 'Viewing' => v_link,'Exploit' => c.body_str }
  321.         return results
  322.       rescue Curl::Err::ConnectionFailedError => e
  323.         puts "Shitty connection yo".light_red + ".....".white
  324.         return false
  325.       rescue => e
  326.         puts "Unknown connection problem".light_red + ".....".white
  327.         puts "#{e}".light_red
  328.         return false
  329.       end
  330.     else
  331.       puts "Invalid Download Source Requested".light_red + "!".white
  332.       return false
  333.     end
  334.   end
  335. end
  336.  
  337. ### MAIN ###
  338. options = {}
  339. optparse = OptionParser.new do |opts|
  340.   opts.banner = "Usage:".light_green + "#{$0} ".white + "[".light_green + "OPTIONS".white + "]".light_green
  341.   opts.separator ""
  342.   opts.separator "EX:".light_green + " #{$0} -s cisco-ios".white
  343.   opts.separator "EX:".light_green + " #{$0} -h 217.140.75.46".white
  344.   opts.separator "EX:".light_green + " #{$0} --quick-search IIS/5.1".white
  345.   opts.separator "EX:".light_green + " #{$0} -S exploitdb -x udev".white
  346.   opts.separator "EX:".light_green + " #{$0} -d 8678 -S exploitdb".white
  347.   opts.separator "EX:".light_green + " #{$0} --source metasploit --exploit-search udev".white
  348.   opts.separator "EX:".light_green + " #{$0} -S metasploit -d modules/exploit/linux/local/udev_netlink".white
  349.   opts.separator ""
  350.   opts.separator "Options: ".light_green
  351.   opts.on('-q''--quick-search STRING'"\n\tShodan Quick Search".white) do |search_str|
  352.     options[:method] = 3 # 1=> Normal, 2=> IP, 3=> Quick, 4=>Exploit Search, 5=>Exploit Download
  353.     options[:search] = search_str.chomp
  354.   end
  355.   opts.on('-s''--shodan-search STRING'"\n\tShodan Search".white)do |search_str|
  356.     options[:method] = 1 # 1=> Normal, 2=> IP, 3=> Quick, 4=>Exploit Search, 5=>Exploit Download
  357.     options[:search] = search_str.chomp
  358.   end
  359.   opts.on('-h''--host-search HOST'"\n\tShodan Host Search against IP".white) do |search_str|
  360.     options[:method] = 2 # 1=> Normal, 2=> IP, 3=> Quick, 4=>Exploit Search, 5=>Exploit Download
  361.     if search_str.chomp =~ /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/
  362.       options[:search] = search_str.chomp
  363.     else
  364.       begin
  365.         ip = Resolv.getaddress(search_str.chomp) # Resolve Host Domain to IP
  366.         options[:search] = ip
  367.       rescue Resolv::ResolvError => e
  368.         cls
  369.         banner
  370.         puts
  371.         puts "Unable to Resolve Host to IP".light_red + "!".white
  372.         puts
  373.         puts opts
  374.         puts
  375.         exit 69;
  376.       end
  377.     end
  378.   end
  379.   opts.on('-S''--source SOURCE'"\n\tSet Exploit Source: exploitdb or metasploit".white) do |source|
  380.     sources=["metasploit""exploitdb"]
  381.     if sources.include?(source.downcase.chomp)
  382.       options[:source] = source.downcase.chomp
  383.     else
  384.       cls
  385.       banner
  386.       puts
  387.       puts "Invalid Search Source Requested".light_red + "!".white
  388.       puts "\t=> #{source}".light_red
  389.       puts
  390.       puts opts
  391.       puts
  392.       exit 69;
  393.     end
  394.   end
  395.   opts.on('-x''--exploit-search STRING'"\n\tShodan Exploit Search for String (requires -S)".white) do |search_str|
  396.     options[:method] = 4 # 1=> Normal, 2=> IP, 3=> Quick, 4=>Exploit Search, 5=>Exploit Download
  397.     options[:search] = search_str.chomp
  398.   end
  399.   opts.on('-d''--download-id ID'"\n\tDownload Exploit by Exploit ID (requires -S)".white) do |search_str|
  400.     options[:method] = 5 # 1=> Normal, 2=> IP, 3=> Quick, 4=>Exploit-DB Search, 5=>Exploit-DB Download
  401.     options[:search] = search_str.chomp
  402.   end
  403.   opts.on('-H''--help'"\n\tHelp Menu".white) do
  404.     cls
  405.     banner
  406.     puts
  407.     puts opts
  408.     puts
  409.     exit 69;
  410.   end
  411. end
  412. begin
  413.   foo = ARGV[0] || ARGV[0] = "-H"
  414.   optparse.parse!
  415.   mandatory = [:method,:search]
  416.   missing = mandatory.select{ |param| options[param].nil}
  417.   if not missing.empty?
  418.     cls
  419.     banner
  420.     puts
  421.     puts "Missing options: ".red + " #{missing.join(', ')}".white  
  422.     puts optparse
  423.     exit 666;
  424.   end
  425. rescue OptionParser::InvalidOptionOptionParser::MissingArgument,OptionParser::AmbiguousOption
  426.   cls
  427.   banner
  428.   puts
  429.   puts $!.to_s.red
  430.   puts
  431.   puts optparse
  432.   puts
  433.   exit 666;  
  434. end
  435.  
  436. banner
  437. shodan = ShodanAPI.new(APIKEY)
  438. if shodan.connected?
  439.   # Display Basic API Key Info
  440.   shodan.info
  441.   puts
  442.  
  443.   # Create Results Dir if it doesnt exist
  444.   Dir.mkdir(RESULTS) unless File.exists?(RESULTS) andFile.directory?(RESULTS)
  445.  
  446.   # Now run as requested....
  447.   case options[:method].to_i
  448.   when 1
  449.     results = shodan.search(options[:search].to_s)
  450.     if not results.nil?
  451.       puts "Shodan Search".light_green + ": #{options[:search].to_s}".white
  452.       f=File.open(RESULTS + "shodan_search_results.txt"'w+')
  453.       f.puts "Shodan Search: #{options[:search].to_s}"
  454.       puts "Total Results Found".light_green + ": #{results['total']}".white
  455.       f.puts "Total Results Found: #{results['total']}"
  456.       results['countries'].each do |country|
  457.         puts "  #{country['name']}".light_green + ": #{country['count']}".white
  458.         f.puts "  #{country['name']}: #{country['count']}"
  459.       end
  460.       puts
  461.       f.puts
  462.       results['matches'].each do |host|
  463.         puts "Host IP".light_green + ": #{host['ip']}".white
  464.         f.puts "Host IP: #{host['ip']}"
  465.         puts "#{host['data']}".white
  466.         f.puts host['data']
  467.       end
  468.       f.puts
  469.       f.close
  470.     else
  471.       puts "No Results Found for ".light_red + "#{string}".white + " via Shodan Search".light_red + "!".white
  472.     end
  473.     puts
  474.   when 2
  475.     # Check Host Results
  476.     results = shodan.host(options[:search].to_s)
  477.     if not results.nil?
  478.       f=File.open(RESULTS + "shodan_host_search_results.txt"'w+')
  479.       puts "Host IP".light_green + ": #{results['ip']}".white unlessresults['ip'].nil?
  480.       f.puts "Host IP: #{results['ip']}" unless results['ip'].nil?
  481.       puts "ISP".light_green + ": #{results['data'][0]['isp']}".white unless results['data'][0]['isp'].nil?
  482.       f.puts "ISP: #{results['data'][0]['isp']}" unlessresults['data'][0]['isp'].nil?
  483.       puts "Hostname(s)".light_green + ": #{results['hostnames'].join(',')}".white unlessresults['hostnames'].empty?
  484.       f.puts "Hostname(s): #{results['hostnames'].join(',')}" unlessresults['hostnames'].empty?
  485.       puts "Host OS".light_green + ": #{results['os']}".white unlessresults['os'].nil?
  486.       f.puts "Host OS: #{results['os']}" unless results['os'].nil?
  487.       puts "Country".light_green + ": #{results['country_name']}".white unless results['country_name'].nil?
  488.       f.puts "Country: #{results['country_name']}" unlessresults['country_name'].nil?
  489.       puts "City".light_green + ": #{results['city']}".white unlessresults['city'].nil?
  490.       f.puts "City: #{results['city']}" unless results['city'].nil?
  491.       puts "Longitude".light_green + ": #{results['longitude']}".white unless results['longitude'].nilorresults['longitude'].nil?
  492.       f.puts "Longitude: #{results['longitude']}" unlessresults['longitude'].nilor results['longitude'].nil?
  493.       puts "Latitude".light_green + ": #{results['latitude']}".whiteunless results['longitude'].nilor results['longitude'].nil?
  494.       f.puts "Latitude: #{results['latitude']}" unlessresults['longitude'].nilor results['longitude'].nil?
  495.       f.puts
  496.       puts
  497.       # We need to split and re-pair up the ports & banners as ports comes after banners in results iteration
  498.       ban=nil
  499.       port_banners={}
  500.       results['data'][0].each do |k, v|
  501.         if k == 'port'
  502.           port=v
  503.           if not ban.nil?
  504.             port_banners.store(port, ban) # store them in hash so we pair them up properly
  505.             ban=nil
  506.           end
  507.         elsif k == 'banner'
  508.           ban=v
  509.         end
  510.       end
  511.       # Now we can display them in proper pairs
  512.       port_banners.each do |port, ban|
  513.         puts "Port".light_green + ": #{port}".white
  514.         f.puts "Port: #{port}"
  515.         puts "Banner".light_green + ": \n#{ban}".white
  516.         f.puts "Banner: \n#{ban}"
  517.       end
  518.       f.puts
  519.       f.close
  520.     else
  521.       puts "No results found for host".light_red + "!".white
  522.     end
  523.     puts
  524.   when 3
  525.     # Perform Quick Shodan Search
  526.     string = options[:search].to_s
  527.     ips = shodan.quick_search(string)
  528.     if not ips.nil?
  529.       puts "Shodan Search".light_green + ": #{string}".white
  530.       puts "Total Results".light_green + ": #{ips.size}".white
  531.       puts "IP Addresses Returned".light_green + ": ".white
  532.       f=File.open(RESULTS + 'quick_search-ips.lst''w+')
  533.       ips.each {|x| puts "  #{x}".white; f.puts x }
  534.       f.close
  535.     else
  536.       puts "No Results Found for ".light_red + "#{string}".white + " via Shodan Quick Search".light_red + "!".white
  537.     end
  538.     puts
  539.   when 4
  540.     # Search for Exploits
  541.     string = options[:search].to_s
  542.     source = options[:source].to_s
  543.     results = shodan.sploit_search(string, source)
  544.     if not results.nil?
  545.       f=File.open(RESULTS + "shodan_#{source}_search_results.txt",'w+')
  546.       puts "Shodan Exploit Search".light_green + ": #{string}".white
  547.       f.puts "Shodan Exploit Search: #{string}"
  548.       results.each do |id, stuff|
  549.         puts "ID".light_green + ": #{id}".white unless id.nil?
  550.         f.puts "ID: #{id}" unless id.nil?
  551.         stuff.each do |link, desc|
  552.           puts "View".light_green + ": #{link.sub('http://www.metasploit.com/', 'http://www.rapid7.com/db/')}".white unless link.nil?
  553.           f.puts "View: #{link.sub('http://www.metasploit.com/', 'http://www.rapid7.com/db/')}" unless link.nil?
  554.           if not link.niland source.downcase == 'metasploit'
  555.             puts "Github Link".light_green + ": https://raw.github.com/rapid7/metasploit-framework/master/#{link.sub('http://www.metasploit.com/', '').sub('/exploit/', '/exploits/').sub(/\/$/, '')}.rb".white
  556.             f.puts "Github Link: https://raw.github.com/rapid7/metasploit-framework/master/#{link.sub('http://www.metasploit.com/', '').sub('/exploit/', '/exploits/').sub(/\/$/, '')}.rb"
  557.           end
  558.           puts "Exploit Description".light_green + ": \n#{desc}".white unless desc.nil?
  559.           f.puts "Exploit Description: \n#{desc}" unless desc.nil?
  560.           f.puts
  561.           puts
  562.         end
  563.       end
  564.       f.close
  565.     else
  566.       puts "No Results Found for ".light_red + "#{string}".white + " via Shodan Exploit Search".light_red + "!".white
  567.     end
  568.     puts
  569.   when 5
  570.     # Now download one of the exploits you found....
  571.     id=options[:search].to_s
  572.     source = options[:source].to_s
  573.     results = shodan.sploit_download(id, source)
  574.     if not results.nil?
  575.       downloads = RESULTS + 'downloads/'
  576.       Dir.mkdir(downloads) unless File.exists?(downloads) andFile.directory?(downloads)
  577.       f=File.open(downloads + "#{source}-#{id}.code"'w+')
  578.       results.each do |k, v|
  579.         if k == 'Exploit'
  580.           puts "Saved to".light_green + ": #{downloads}#{source}-#{id}.code".white
  581.           puts "#{k}".light_green + ": \n#{v}".white
  582.           f.puts v
  583.         else
  584.           puts "#{k}".light_green + ": #{v}".white
  585.         end
  586.       end
  587.       f.close
  588.     else
  589.       puts "No Download Results Found for ID".light_red + "#: #{id}".white
  590.     end
  591.   end
  592. else
  593.   exit 666;
  594. end
  595. #EOF
Direct Link: http://pastebin.com/B0SdmmrX

Helpful for me, hope it is for you too!

Until next time, Enjoy!

Comments

  1. Ahrefs
    While we’re on the topic of SEO, I wanted to mention Ahrefs. Ahrefs is a tool that allows you to do keyword research to ensure you’re targeting the best keywords with the highest traffic and lowest difficulty to rank for.
    While this tool isn’t free or cheap, they do offer a free two-week trial. Alternatively, you can use their competitors like Moz or SEMrush (who also have free trials, hint hint). Whichever one you choose, if you’re serious about ranking on Google, I highly recommend a keyword research tool. Without them, you only have access to Google Keyword Planner, which doesn’t really help you find the right keywords.

    ReplyDelete

Post a Comment