A hint: This file contains one or more very long lines, so maybe it is better readable using the pure text view mode that shows the contents as wrapped lines within the browser window.
1 2 class LoggingSQL < Logging 3 def initialize(f = STDOUT) 4 super 5 insert_request_config 6 end 7 8 def insert_request_config 9 # the config doesn't change between targets 10 11 foo = {} 12 req_config = {} 13 14 # when this is called by OutputSQL.new there won't be a user-agent set 15 # $CUSTOM_HEADERS["User-Agent"] isn't set until after the command option loop 16 $CUSTOM_HEADERS['User-Agent'] = $USER_AGENT unless $CUSTOM_HEADERS['User-Agent'] 17 18 if $USE_PROXY 19 req_config[:proxy] = { proxy_host: $PROXY_HOST, proxy_port: $PROXY_PORT } 20 req_config[:proxy][:proxy_user] = $PROXY_USER if $PROXY_USER 21 end 22 23 req_config[:headers] = {} unless $CUSTOM_HEADERS.empty? 24 $CUSTOM_HEADERS.each do |header_name, header_value| 25 req_config[:headers][header_name] = header_value.dup 26 end 27 foo[:request_config] = req_config 28 29 flatten_elements!(foo) 30 # pp foo[:request_config] 31 32 insert = [escape_for_sql(JSON.dump(foo[:request_config]))].join(',') 33 query = "INSERT IGNORE INTO request_configs (value) VALUES (#{insert});" 34 @f.puts query 35 ## 36 end 37 38 def flatten_elements!(obj) 39 if obj.class == Hash 40 obj.each_value { |x| flatten_elements!(x) } 41 elsif obj.class == Array 42 obj.flatten! 43 end 44 end 45 46 def escape_for_sql(s) 47 s = s.to_s 48 "'" + s.gsub("'"){"\\'"} + "'" 49 end 50 51 def create_tables 52 # Note that you may encounter the error "1709 - Index column size too large. The maximum column size is 767 bytes." 53 # when using MySQL <= 5.6 with the innodb engine and the utf8mb4 character set 54 # 55 # max_hostname_length = 253 56 # max_uri_prefix = 10 # covers https:// 57 # max_url_length = 2048 # old IE limit 58 max_target_length = 2048 59 60 # feel free to modify this 61 @f.puts 'CREATE TABLE plugins (plugin_id int NOT NULL AUTO_INCREMENT, name varchar(255) NOT NULL,PRIMARY KEY (plugin_id), UNIQUE (name));' 62 @f.puts "CREATE TABLE targets (target_id int NOT NULL AUTO_INCREMENT, target varchar(#{max_target_length}) NOT NULL, status varchar(10),PRIMARY KEY (target_id), UNIQUE (target, status) );" 63 @f.puts 'CREATE TABLE scans (scan_id int NOT NULL AUTO_INCREMENT, config_id INT NOT NULL, plugin_id INT NOT NULL, target_id INT NOT NULL, version varchar(255), os varchar(255), string varchar(1024), account varchar(1024), model varchar(1024), firmware varchar(1024), module varchar(1024), filepath varchar(1024), certainty varchar(10) ,PRIMARY KEY (scan_id));' 64 @f.puts 'CREATE TABLE request_configs (config_id int NOT NULL AUTO_INCREMENT, value TEXT NOT NULL, PRIMARY KEY (config_id) );' 65 66 # plugins table 67 @f.puts "INSERT INTO plugins (name) VALUES ('Custom-Plugin');" 68 @f.puts "INSERT INTO plugins (name) VALUES ('Grep');" 69 70 Plugin.registered_plugins.each do |n, _| 71 @f.puts "INSERT INTO plugins (name) VALUES (#{escape_for_sql(n)});" 72 end 73 end 74 75 def out(target, status, results) 76 # nice 77 foo = { target: target, http_status: status, plugins: {}, request_config: {} } 78 79 results.each do |plugin_name, plugin_results| 80 thisplugin = {} 81 82 next if plugin_results.empty? 83 # important info in brief mode is version, type and ? 84 # what's the highest probability for the match? 85 86 certainty = plugin_results.map { |x| x[:certainty] unless x[:certainty].class == Regexp }.flatten.compact.sort.uniq.last 87 88 version = plugin_results.map { |x| x[:version] unless x[:version].class == Regexp }.flatten.compact.sort.uniq 89 os = plugin_results.map { |x| x[:os] unless x[:os].class == Regexp }.flatten.compact.sort.uniq 90 string = plugin_results.map { |x| x[:string] unless x[:string].class == Regexp }.flatten.compact.sort.uniq 91 accounts = plugin_results.map { |x| x[:account] unless x[:account].class == Regexp }.flatten.compact.sort.uniq 92 model = plugin_results.map { |x| x[:model] unless x[:model].class == Regexp }.flatten.compact.sort.uniq 93 firmware = plugin_results.map { |x| x[:firmware] unless x[:firmware].class == Regexp }.flatten.compact.sort.uniq 94 modules = plugin_results.map { |x| x[:module] unless x[:module].class == Regexp }.flatten.compact.sort.uniq 95 filepath = plugin_results.map { |x| x[:filepath] unless x[:filepath].class == Regexp }.flatten.compact.sort.uniq 96 97 thisplugin[:certainty] = certainty if !certainty.nil? && certainty != 100 98 # empty arrays 99 thisplugin[:version] = version.empty? ? [] : version 100 thisplugin[:os] = os.empty? ? [] : os 101 thisplugin[:string] = string.empty? ? [] : string 102 thisplugin[:account] = accounts.empty? ? [] : accounts 103 thisplugin[:model] = model.empty? ? [] : model 104 thisplugin[:firmware] = firmware.empty? ? [] : firmware 105 thisplugin[:module] = modules.empty? ? [] : modules 106 thisplugin[:filepath] = filepath.empty? ? [] : filepath 107 108 foo[:plugins][plugin_name.to_sym] = thisplugin 109 end 110 111 flatten_elements!(foo) 112 113 i_target = escape_for_sql(foo[:target]) 114 insert = [escape_for_sql(foo[:http_status]), i_target].join(',') 115 query = "INSERT IGNORE INTO targets (status,target) VALUES (#{insert});" 116 @f.puts query 117 118 foo[:plugins].each do |x| 119 plugin_name = escape_for_sql(x.first.to_s) 120 insert = [escape_for_sql(x[1][:version].join(',').to_s), escape_for_sql(x[1][:os].join(',').to_s), 121 escape_for_sql(x[1][:string].join(',').to_s), escape_for_sql(x[1][:account].join(',').to_s), escape_for_sql(x[1][:model].join(',').to_s), 122 escape_for_sql(x[1][:firmware].join(',').to_s), escape_for_sql(x[1][:module].join(',').to_s), escape_for_sql(x[1][:filepath].join(',').to_s), 123 escape_for_sql(x[1][:certainty].to_s)].join(',') 124 125 query = "INSERT INTO scans (target_id, config_id, plugin_id, version, os, string, account, model, firmware, module, filepath, certainty) VALUES ( (SELECT target_id from targets WHERE target = #{i_target}),(SELECT MAX(config_id) from request_configs),(SELECT plugin_id from plugins WHERE name = #{plugin_name}), #{insert} );" 126 @f.puts query 127 end 128 end 129 end