#!/usr/bin/env ruby # # Searchsploit Ruby Edition # By: Hood3dRob1n # # Exploit-db now on Github: # git clone https://github.com/offensive-security/exploit-database.git # # The original bash searchsploit script is too simple sometimes (and a little outdated) # Plus, who doesn't like logging and a little color to mix things up ;p # ######## Exploit-DB Files.csv Path ######## CSV='/home/clue/fun/exploit-db/files.csv' # ########################################### ###### STD GEMS ###### require 'optparse' # require 'fileutils' # #### NON-STD GEMS #### require 'rubygems' # require 'colorize' # ###################### # Catch System Interupts trap("SIGINT") { puts "\n\nWARNING".light_red + "!".white + " CTRL".light_red + "+".white + "C Detected".light_red + ",".white + " Shutting things down".light_red + "....\n\n".white exit 666; } # 0x1337 Banner def banner puts puts "Searchsploit-rb".light_red + ": An Exploit-DB Search Tool".white end # Clear def cls if RUBY_PLATFORM =~ /win32|win64|\.NET|windows|W0W64/i system('cls') else system('clear') end end # Update to latest and greatest # Leverages the new Github format # Much better than older HTTP downloads! def git_update(dir=nil) if dir.nil? directory = "#{CSV.split("/")[0..-2].join("/")}/" else directory = dir end Dir.chdir(directory) do system('git pull') end end # Fetch fresh copy of exploit-db # Git clones to local dir # renames: exploit-db def git_get(dir=nil) if dir.nil? directory = Dir.pwd else directory = dir end Dir.chdir(directory) do system('git clone https://github.com/offensive-security/exploit-database.git exploit-db') end end # Search based on initial full CSV results set against platform # This initializes our array to perform further searchs # We wind down narrowing results as we go # Due to poor CSV consistency this is the best I could come up with def search_platform(query) platform_results=[] IO.foreach(CSV) do |line| line = line.unpack('C*').pack('U*') if !line.valid_encoding? # Thanks Stackoverflow :) if line =~ /(\".+,.+\")/ # Deal with annoying commans within quotes as they shouldn't be used to split on (ahrg) crappy_csv_line = $1 not_as_crappy_csv_line = crappy_csv_line.sub(",", "") workable_csv_line = line.sub!("#{crappy_csv_line}","#{not_as_crappy_csv_line}").split(",") else workable_csv_line = line.split(",") end foo = workable_csv_line - workable_csv_line.slice(0,5) foobar = foo - workable_csv_line.slice(-1, 1) - workable_csv_line.slice(-2, 1) if query == 'nil' platform_results << line else platform_results << line if "#{foobar.join(",")}" =~ /#{query}/i end end return platform_results end # Search based on TYPE # Returns an array with the results def search_type(exploits_array, query) search_results=[] exploits_array.each do |line| line = line.unpack('C*').pack('U*') if !line.valid_encoding? if line =~ /(\".+,.+\")/ crappy_csv_line = $1 not_as_crappy_csv_line = crappy_csv_line.sub(",", "") workable_csv_line = line.sub!("#{crappy_csv_line}","#{not_as_crappy_csv_line}").split(",") else workable_csv_line = line.split(",") end foo = workable_csv_line - workable_csv_line.slice(0,5) foobar = foo - workable_csv_line.slice(-1, 1) - workable_csv_line.slice(-2, 1) if query == 'nil' search_results << line else if not workable_csv_line[-2].nil? search_results << line if "#{workable_csv_line[-2].downcase}" =~ /#{query}/i end end end return search_results end # Search based on Author # Returns an array with the results def search_author(exploits_array, query) search_results=[] exploits_array.each do |line| line = line.unpack('C*').pack('U*') if !line.valid_encoding? if line =~ /(\".+,.+\")/ crappy_csv_line = $1 not_as_crappy_csv_line = crappy_csv_line.sub(",", "") workable_csv_line = line.sub!("#{crappy_csv_line}","#{not_as_crappy_csv_line}").split(",") else workable_csv_line = line.split(",") end foo = workable_csv_line - workable_csv_line.slice(0,5) foobar = foo - workable_csv_line.slice(-1, 1) - workable_csv_line.slice(-2, 1) if query == 'nil' search_results << line else if not workable_csv_line[4].nil? search_results << line if "#{workable_csv_line[4].downcase}" =~ /#{query}/i end end end return search_results end # Search based on PORT # Returns an array with the results def search_port(exploits_array, query) search_results=[] exploits_array.each do |line| line = line.unpack('C*').pack('U*') if !line.valid_encoding? if line =~ /(\".+,.+\")/ crappy_csv_line = $1 not_as_crappy_csv_line = crappy_csv_line.sub(",", "") workable_csv_line = line.sub!("#{crappy_csv_line}","#{not_as_crappy_csv_line}").split(",") else workable_csv_line = line.split(",") end foo = workable_csv_line - workable_csv_line.slice(0,5) foobar = foo - workable_csv_line.slice(-1, 1) - workable_csv_line.slice(-2, 1) if query == 'nil' search_results << line else if not workable_csv_line[-1].nil? search_results << line if "#{workable_csv_line[-1].downcase}" =~ /#{query}/i end end end return search_results end # Search based on SEARCH Term # Returns an array with the results def search_search(exploits_array, query) search_results=[] exploits_array.each do |line| line = line.unpack('C*').pack('U*') if !line.valid_encoding? if query == 'nil' search_results << line else search_results << line if line =~ /#{query}/i end end return search_results end # Parse remaining exploits in array # Print out the search results for user # log to file as well if requested def display_results(remaining_exploits) rezsize = remaining_exploits.size puts "[".light_green + "*".white + "] ".light_green + "Saved #{rezsize} Result(s) to: #{@out}".white if @log puts "[".light_green + "*".white + "] ".light_green + "Displaying Result(s):".white if @verbose and @log puts "[".light_green + "*".white + "] ".light_green + "Found #{rezsize} Result(s):".white if @verbose and not @log remaining_exploits.each do |line| if line =~ /(\".+,.+\")/ crappy_csv_line = $1 not_as_crappy_csv_line = crappy_csv_line.sub(",", "") workable_csv_line = line.sub!("#{crappy_csv_line}","#{not_as_crappy_csv_line}").split(",") else workable_csv_line = line.split(",") end foo = workable_csv_line - workable_csv_line.slice(0,5) foobar = foo - workable_csv_line.slice(-1, 1) - workable_csv_line.slice(-2, 1) if @log f = File.open("#{@out}", 'a+') f.puts "Description: #{workable_csv_line[2]}" f.puts "Location: #{CSV.split("/")[0..-2].join("/")}/#{workable_csv_line[1]}" f.puts "Exploit ID: #{workable_csv_line[0]}" f.puts "Platform: #{foobar.join(",")}" f.puts "Type: #{workable_csv_line[-2]}" if not "#{workable_csv_line[-1].chomp}".to_i == 0 f.puts "Port: #{workable_csv_line[-1].chomp}" end f.puts "Author: #{workable_csv_line[4]}" f.puts "Submit: #{workable_csv_line[3]}" f.puts f.close end if @verbose puts "[".light_green + "*".white + "] ".light_green + "Description: ".light_red + "#{workable_csv_line[2]}".white puts "[".light_green + "*".white + "] ".light_green + "Location: ".light_red + "#{CSV.split("/")[0..-2].join("/")}/#{workable_csv_line[1]}".white puts "[".light_green + "*".white + "] ".light_green + "Exploit ID: ".light_red + "#{workable_csv_line [0]}".white puts "[".light_green + "*".white + "] ".light_green + "Platform: ".light_red + "#{foobar.join(",")}".white puts "[".light_green + "*".white + "] ".light_green + "Type: ".light_red + "#{workable_csv_line[-2]}".white if not "#{workable_csv_line[-1].chomp}".to_i == 0 "[".light_green + "*".white + "] ".light_green + "Port: ".light_red + "#{workable_csv_line[-1].chomp}".white end puts "[".light_green + "*".white + "] ".light_green + "Author: ".light_red + "#{workable_csv_line[4]}".white puts "[".light_green + "*".white + "] ".light_green + "Submit: ".light_red + "#{workable_csv_line[3]}\n".white end end puts "[".light_blue + "*".white + "] ".light_blue + "Search Complete!".white puts "[".light_blue + "*".white + "] ".light_blue + "Hope you found what you needed....".white puts "[".light_blue + "*".white + "] ".light_blue + "Good Bye!".white end ### MAIN ### options = {} optparse = OptionParser.new do |opts| opts.banner = "Usage:".light_green + " #{$0} ".white + "[".light_green + "OPTIONS".white + "]".light_green opts.separator "" opts.separator "EX:".light_green + " #{$0} --update".white opts.separator "EX:".light_green + " #{$0} -t webapps -s vBulletin".white opts.separator "EX:".light_green + " #{$0} --search=\"Linux Kernel 2.6\"".white opts.separator "EX:".light_green + " #{$0} -a \"JoinSe7en\" -s \"MyBB\"".white opts.separator "EX:".light_green + " #{$0} -t remote -s \"SQL Injection\"".white opts.separator "EX:".light_green + " #{$0} -p linux -t local -s UDEV -o search_results.txt".white opts.separator "" opts.separator "Options: ".light_green opts.on('-u', '--update', "\n\tUpdate Exploit-DB Working Archive to Latest & Greatest".white) do |host| options[:method] = 0 end opts.on('-p', '--platform ', "\n\tSystem Platform Type, options include: sco, bsdi/x86, openbsd, lin/amd64, plan9, bsd/x86, openbsd/x86, hardware, bsd, unix, lin/x86-64, netbsd/x86, linux, solaris, ultrix, arm, php, solaris/sparc, osX, os-x/ppc, cfm, generator, freebsd/x86, bsd/ppc, minix, unixware, freebsd/x86-64, cgi, hp-ux, multiple, win64, tru64, jsp, novell, linux/mips, solaris/x86, aix, windows, linux/ppc, irix, QNX, lin/x86, win32, linux/sparc, freebsd, asp, sco/x86".white) do |platform| options[:platform] = platform.downcase.chomp options[:method] = 1 end opts.on('-t', '--type ', "\n\tType of Exploit, options include:\n\tDoS, Remote, Local, WebApps, Papers or Shellcode".white) do |type| options[:type] = type.downcase.chomp options[:method] = 1 end opts.on('-a', '--author ', "\n\tRun Lookup based on Author Username".white) do |author| options[:author] = author.downcase.chomp options[:method] = 1 end opts.on('-s', '--search ', "\n\tSearch Term to look for in Exploit-DB Working Archive".white) do |search| options[:search] = search.downcase.chomp options[:method] = 1 end opts.on('-o', '--output ', "\n\tOutput File to Write Search Results to".white) do |output| @out = output.strip.chomp options[:log] = true end opts.on('-q', '--quiet', "\n\tSilence Output to Terminal for Search Results (when logging output)".white) do |output| options[:q] = true end opts.on('-h', '--help', "\n\tHelp Menu".white) do banner puts puts opts puts exit 69; end end begin foo = ARGV[0] || ARGV[0] = "--help" optparse.parse! mandatory = [:method] missing = mandatory.select{ |param| options[param].nil? } if not missing.empty? cls banner puts puts "Missing options: ".light_red + " #{missing.join(', ')}".white puts optparse exit 666; end rescue OptionParser::InvalidOption, OptionParser::MissingArgument, OptionParser::AmbiguousOption cls banner puts puts $!.to_s.light_red puts puts optparse puts exit 666; end #Make we have search values or set wildcards options[:platform] = '' if not options[:platform] options[:type] = '' if not options[:type] options[:author] = '' if not options[:author] options[:port] = '' if not options[:port] options[:search] = '' if not options[:search] if options[:log] @log=true else @log=false end if options[:q] @verbose=false else @verbose=true end cls banner puts if File.exists?(CSV) and File.directory?("#{CSV.split("/")[0..-2].join("/")}/platforms") if options[:method].to_i == 0 # Update puts "[".light_green + "*".white + "] ".light_green + "Found existing archive file: #{CSV}".white puts "[".light_green + "*".white + "] ".light_green + "Running update to get latest and greatest....".white git_update else puts "[".light_green + "*".white + "] ".light_green + "Found archive file: #{CSV}".white plresults = search_platform(options[:platform]) unless options[:platform].nil? tresults = search_type(plresults, options[:type]) unless options[:type].nil? aresults = search_author(tresults, options[:author]) unless options[:author].nil? presults = search_port(aresults, options[:port]) unless options[:port].nil? results = search_search(presults, options[:search]) unless options[:search].nil? display_results(results) end else # Not finding fi.es.csv and ./platforms/ puts "[".light_red + "X".white + "] ".light_red + "Problem locating archive file: #{CSV}!".white unless File.exists?(CSV) puts "[".light_red + "X".white + "] ".light_red + "Problem locating archives platforms dir: #{CSV.split("/")[0..-2].join("/")}/platforms!".white unless File.directory?("#{CSV.split("/")[0..-2].join("/")}/platforms") puts "[".light_red + "X".white + "] ".light_red + "Can't run searches without it...".white print "(".light_red + "Do You want to Git CLone it ".white + "(".light_yellow + "Y".white + "/".light_yellow + "N".white + ")".light_yellow + "?".white + ")> ".light_red answer = gets.chomp if answer.upcase == 'Y' or answer.upcase == 'YES' puts "[".light_blue + "*".white + "] ".light_blue + "Trying to git clone the exploit-db repo...".white if File.directory?("#{CSV.split("/")[0..-2].join("/")}/") and "#{CSV.split("/")[0..-2].join("/")}/" != '/' git_get("#{CSV.split("/")[0..-2].join("/")}/") else git_get end puts "[".light_blue + "*".white + "] ".light_blue + "Hopefully things went well with git clone installation...".white puts "[".light_blue + "*".white + "] ".light_blue + "Try editing source code to point to the new setup and run again...".white else puts "[".light_red + "X".white + "] ".light_red + "OK, try again when you get things figured out then.....".white end end puts "\n\n" #EOF