Advertisement
Guest User

Shodan API Search Tool, by HR

a guest
Jan 8th, 2014
34,794
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Ruby 20.91 KB | None | 0 0
  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.nil? or link == '') or (desc.nil? or desc == '') or (dl_id.nil? or 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::InvalidOption, OptionParser::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) and File.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 unless results['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']}" unless results['data'][0]['isp'].nil?
  483.       puts "Hostname(s)".light_green + ": #{results['hostnames'].join(',')}".white unless results['hostnames'].empty?
  484.       f.puts "Hostname(s): #{results['hostnames'].join(',')}" unless results['hostnames'].empty?
  485.       puts "Host OS".light_green + ": #{results['os']}".white unless results['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']}" unless results['country_name'].nil?
  489.       puts "City".light_green + ": #{results['city']}".white unless results['city'].nil?
  490.       f.puts "City: #{results['city']}" unless results['city'].nil?
  491.       puts "Longitude".light_green + ": #{results['longitude']}".white unless results['longitude'].nil? or results['longitude'].nil?
  492.       f.puts "Longitude: #{results['longitude']}" unless results['longitude'].nil? or results['longitude'].nil?
  493.       puts "Latitude".light_green + ": #{results['latitude']}".white unless results['longitude'].nil? or results['longitude'].nil?
  494.       f.puts "Latitude: #{results['latitude']}" unless results['longitude'].nil? or 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.nil? and 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) and File.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
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement