class Uirusu::CLI::Application

Attributes

config[RW]

Public Class Methods

new() click to toggle source

Creates a new instance of the [Application] class

# File lib/uirusu/cli/application.rb, line 29
def initialize
        @options = {}
        @config = {}
        @hashes = Array.new
        @files_of_hashes = Array.new
        @sites = Array.new
        @uploads = Array.new
end

Public Instance Methods

create_config(file=CONFIG_FILE) click to toggle source

Create config skeleton

# File lib/uirusu/cli/application.rb, line 156
def create_config file=CONFIG_FILE
        f = File.expand_path(file)

        if File.exist?(f) == false
                File.open(f, 'w+') do |of|
                        of.write("virustotal: \n  api-key: \n  timeout: 25\n\n")
                end
                puts "[*] An empty #{f} has been created. Please edit and fill in the correct values."
        else
                puts "[!]  #{f} already exists. Please delete it if you wish to re-create it."
        end
end
load_config(file=CONFIG_FILE) click to toggle source

Loads the .uirusu config file for the api key

# File lib/uirusu/cli/application.rb, line 171
def load_config file=CONFIG_FILE

        @config = nil

        f = File.expand_path(file)

        if File.exist?(f)
                @config = YAML.load_file f
        else
                if ENV['UIRUSU_VT_API_KEY']
                        @config = {}
                        @config['virustotal'] = {}
                        @config['virustotal']['api-key'] = ENV['UIRUSU_VT_API_KEY']

                        if ENV['UIRUSU_VT_TIMEOUT']
                                @config['virustotal']['timeout'] = ENV['UIRUSU_VT_TIMEOUT']
                        else
                                @config['virustotal']['timeout'] = 25
                        end
                end
        end

        if @config == nil
                STDERR.puts "[!] #{CONFIG_FILE} does not exist. Please run #{APP_NAME} --create-config, to create it."
                exit
        end

        @options[:timeout] = @config['virustotal']['timeout'] if @config['virustotal']['timeout'] != nil
        @options["proxy"] = @config['virustotal']['proxy'] if @config['virustotal']['proxy'] != nil
        @options["ssl_ca_cert"] = @config['virustotal']['ssl_ca_cert'] if @config['virustotal']['ssl_ca_cert'] != nil
        @options["verify_ssl"] = @config['virustotal']['verify_ssl'] if @config['virustotal']['verify_ssl'] != nil

        process_ssl_proxy
end
main(args) click to toggle source

Main entry point for uirusu

# File lib/uirusu/cli/application.rb, line 296
def main(args)
        parse_options(args)
        load_config

        if @options['output'] == :stdout
                output_method = :to_stdout
        elsif @options['output'] == :json
                output_method = :to_json
        elsif @options['output'] == :yaml
                output_method = :to_yaml
        elsif @options['output'] == :xml
                output_method = :to_xml
        end

        if @options[:directory] != nil
                hashes = Uirusu::Scanner.scan(@options[:directory])

                hashes.each do |hash|
                        @hashes.push hash
                end
        end

        if @files_of_hashes != nil
                @files_of_hashes.each do |file|
                        f = File.open(file, 'r')

                        f.each do |hash|
                                hash.chomp!
                                @hashes.push hash
                        end
                end
        end

        if @hashes != nil
                @hashes.each_with_index do |hash, index|
                        if @options['rescan']
                                results = scan_and_wait(Uirusu::VTFile, hash, 5)
                        else
                                results = Uirusu::VTFile.query_report(@config['virustotal']['api-key'], hash)
                        end

                        result = Uirusu::VTResult.new(hash, results)
                        print result.send output_method if result != nil
                        sleep @options[:timeout] if index != @hashes.length - 1
                end
        end

        if @sites != nil
                @sites.each_with_index do |url, index|
                        results = scan_and_wait(Uirusu::VTUrl, url, 5)
                        result = Uirusu::VTResult.new(results[0], results[1])
                        print result.send output_method if result != nil
                        sleep @options[:timeout] if index != @sites.length - 1
                end
        end

        if @uploads != nil
                @uploads.each_with_index do |upload, index|
                        results = scan_and_wait(Uirusu::VTFile, upload, 5)
                        result = Uirusu::VTResult.new(results[0], results[1])
                        print result.send output_method if result != nil
                        sleep @options[:timeout] if index != @uploads.length - 1
                end
        end
end
parse_options(args) click to toggle source

Parses the command the line options and returns the parsed options hash

@return [Hash] of the parsed options

# File lib/uirusu/cli/application.rb, line 41
def parse_options(args)
        begin
                @options['output']  = :stdout
                @options['verbose'] = false
                @options['rescan']  = false
                @options[:timeout]  = 25
                @options[:directory] = nil

                opts = OptionParser.new do |opt|
                        opt.banner = "#{APP_NAME} v#{VERSION}\nJacob Hammack\n#{HOME_PAGE}\n\n"
                        opt.banner << "Usage: #{APP_NAME} <options>"
                        opt.separator('')
                        opt.separator('File Options')

                        opt.on('-h HASH', '--search-hash HASH', 'Searches a single hash on virustotal.com') do |hash|
                                @hashes.push(hash)
                        end

                        opt.on('-r HASH[,HASH]', '--rescan-hash HASH[,HASH]', 'Requests a rescan of a single hash, or multiple hashes (comma separated), by virustotal.com') do |hash|
                                @options['rescan'] = true
                                @hashes.push(hash)
                        end

                        opt.on('-f FILE', '--search-hash-file FILE', 'Searches each hash in a file of hashes on virustotal.com') do |file|
                                if File.exist?(file)
                                        puts "[+] Adding file #{file}" if @options['verbose']
                                        @files_of_hashes.push(file)
                                else
                                        puts "[!] #{file} does not exist, please check your input!\n"
                                end
                        end

                        opt.on('-u FILE', '--upload-file FILE', 'Uploads a file to virustotal.com for analysis') do |file|
                                if File.exist?(file)
                                        puts "[+] Adding file #{file}" if @options['verbose']
                                        @uploads.push(file)
                                else
                                        puts "[!] #{file} does not exist, please check your input!\n"
                                end
                        end

                        opt.separator('')
                        opt.separator("Url Options")

                        opt.on('-s SITE', '--search-site SITE', 'Searches for a single url on virustotal.com') { |site|
                                @sites.push(site)
                        }

                        opt.separator('')
                        opt.separator('Output Options')

                        opt.on('-j', '--json-output', 'Print results as json to stdout') do
                                @options['output'] = :json
                        end

                        opt.on('-x', '--xml-output', 'Print results as xml to stdout') do
                                @options['output'] = :xml
                        end

                        opt.on('-y', '--yaml-output', 'Print results as yaml to stdout') do
                                @options['output'] = :yaml
                        end

                        opt.on('--stdout-output', 'Print results as normal text line to stdout, this is default') do
                                @options['output'] = :stdout
                        end

                        opt.separator ''
                        opt.separator 'Advanced Options'

                        opt.on('-c', '--create-config', 'Creates a skeleton config file to use') do
                                create_config
                                exit
                        end

                        opt.on('-d DIRECTORY', '--directory', 'Scans a directory recursively for files and submits the hashes') do |directory|
                                @options[:directory] = directory
                        end

                        opt.on('-p PROXY', '--proxy-server', 'Uses a specified proxy server') do |proxy|
                                @options['proxy'] = proxy
                        end

                        opt.on('--[no-]verbose', 'Print verbose information') do |v|
                                @options['verbose'] = v
                        end

                        opt.separator ''
                        opt.separator 'Other Options'

                        opt.on('-v', '--version', 'Shows application version information') do
                                puts "#{APP_NAME} - #{VERSION}"
                                exit
                        end

                        opt.on_tail('-?', '--help', 'Show this message') { |help|
                                puts opt.to_s + "\n"
                                exit
                        }
                end

                if ARGV.length != 0
                        opts.parse!
                else
                        puts opts.to_s + "\n"
                        exit
                end
        rescue OptionParser::MissingArgument
                puts opts.to_s + "\n"
                exit
        end
end
process_ssl_proxy() click to toggle source

Processes SSL and Proxy Related Options

# File lib/uirusu/cli/application.rb, line 208
def process_ssl_proxy
        if @options['proxy'] != nil
                puts "[DEBUG] Proxy enabled: #{@options['proxy']}"
                RestClient.proxy = @options['proxy']
        end
end
scan_and_wait(mod, resource, attempts) click to toggle source

Submits a file/url and waits for analysis to be complete and returns the results.

@param mod @param resource @param attempts

# File lib/uirusu/cli/application.rb, line 221
def scan_and_wait(mod, resource, attempts)
        method = nil
        retries = attempts

        if mod.name == "Uirusu::VTFile"
                STDERR.puts "[*] Attempting to rescan #{resource}" if  @options['verbose']
                method = @options['rescan'] ? mod.method(:rescan_file) : mod.method(:scan_file)
        else
                STDERR.puts "[*] Attempting to upload file #{resource}" if  @options['verbose']
                method = mod.method :scan_url
        end

        begin
                result = method.call(@config['virustotal']['api-key'], resource)
        rescue => e
                if @options['rescan']
                        STDERR.puts "[!] An error has occurred with the rescan request.  Retrying 60 seconds up #{retries} retries: #{e.message}\n" if  @options['verbose']
                else
                        STDERR.puts "[!] An error has occurred uploading the file. Retrying 60 seconds up #{retries} retries.\n" if  @options['verbose']
                end

                if retries >= 0
                        sleep 60
                        retries = retries - 1
                        retry
                end
        end

        begin

                # Convert all single result replies to an array.  This is because
                # rescan_file returns an array of results if more than one hash
                # is requested to be rescanned.
                result_array = result.is_a?(Array) ? result : [ result ]

                result_array.collect do |r|
                        if r['response_code'] == 1
                                STDERR.puts "[*] Attempting to parse the results for: #{r['resource']}" if @options['verbose']
                                results = mod.query_report(@config['virustotal']['api-key'], r['resource'])

                                while results['response_code'] != 1
                                        STDERR.puts "[*] File has not been analyized yet, waiting 60 seconds to try again" if  @options['verbose']
                                        sleep 60
                                        results = mod.query_report(@config['virustotal']['api-key'], r['resource'])
                                end

                                return r['resource'], results

                        elsif r['response_code'] == 0 and @options['rescan']
                                STDERR.puts "[!] Unknown Virustotal error for rescan of #{r['resource']}." if @options['verbose']
                                next

                        elsif r['response_code'] == -1 and @options['rescan']
                                STDERR.puts "[!] Virustotal does not have a sample of #{r['resource']}." if @options['verbose']
                                next

                        elsif r['response_code'] == -2
                                STDERR.puts "[!] Virustotal limits exceeded, ***do not edit the timeout values.***"
                                exit(1)
                        else
                                nil
                        end
                end
        rescue => e
                STDERR.puts "[!] An error has occurred retrieving the report. Retrying 60 seconds up #{retries} retries. #{e.message}\n" if  @options['verbose']
                if retries >= 0
                        sleep 60
                        retries = retries - 1
                        retry
                end
        end
end