dronebl.rb 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 90452b68385bacb07b71362b95b43f9529c52a00
4
+ data.tar.gz: c6c7ac736b865e46054effce79866393a7174280
5
+ SHA512:
6
+ metadata.gz: cac7d9db9af50b35875a0a596d33e53c4b6c66d82ed3dd09a3cc0b08c02eb3486b7551cc27422e5d081734f92575b79725fa7d793a73a66ed800ff3951d1ad88
7
+ data.tar.gz: afc184cfb563ca52072ae0f87285abd8d1b4344206940966acb47acf13025ec08843594dcba74c813e027608f7930df8b30371cb105fb1a0632dc700558f3f35
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env ruby
2
+ # copyright Rylee Fowler 2014
3
+ # see LICENSE for more details
4
+ require 'dronebl-client'
5
+ require 'resolv'
6
+ require 'optparse'
7
+ class Options
8
+ attr_reader :read_from_stdin, :key_str, :key_file, :type, :comment, :dry_run,
9
+ :use_key_file, :no_check
10
+ attr_accessor :ips
11
+ def initialize
12
+ @key_file = File.expand_path "~/.droneblkey"
13
+ @ips = []
14
+ @opt_parser = OptionParser.new do |opts|
15
+ opts.banner = "Usage: #{$0} [options]"
16
+ opts.separator ""
17
+ opts.separator "Options available:"
18
+ opts.on('-I', '--ips [ip1,ip2,ip3,...]', Array, 'Read in IPs.') do |list|
19
+ @ips += list
20
+ end
21
+ opts.on('-s', '--stdin', 'Read a newline-delimited list of IPs from STDIN') do
22
+ @read_from_stdin = true
23
+ end
24
+ opts.on('-k', '--key KEY', String, 'Use KEY as your DroneBL RPC2 key') do |key|
25
+ @key_str = key
26
+ end
27
+ opts.on('-f', '--keyfile [FILE_PATH]', 'Read the file at FILE_PATH and use it as the RPC key') do |path|
28
+ @use_key_file = true
29
+ @key_file = path
30
+ end
31
+ opts.on('-t', '--type TYPE', String, 'Mark submissions as TYPE. Valid types can be seen with the "-T" option.') do |type|
32
+ @type = type
33
+ end
34
+ opts.on('-T', '--show-types', 'Show all valid types for IP submission') do
35
+ puts DroneBL::TYPES.map { |k, v| "#{k} : #{v}"}.join("\n")
36
+ exit
37
+ end
38
+ opts.on('-c', '--comment [COMMENT]', String, 'Attach the given comment to the listings.') do |comment|
39
+ @comment = comment
40
+ opts.on('-u', '--use-key-file', 'Use the default key file located at ~/.droneblkey') do
41
+ @use_key_file = true
42
+ end
43
+ opts.on('-d', '--dry-run', 'Prints the query to be run to STDOUT instead of sending it as a query to the DroneBL RPC service.') do
44
+ @dry_run = true
45
+ end
46
+ #opts.on('-n', '--no-pre-check', 'Do not check DroneBL for the IPs you are about to submit with a lookup before doing it.') do
47
+ # @no_check = true
48
+ end
49
+ opts.on_tail("-h", "--help", "Show this message") do
50
+ puts opts
51
+ exit
52
+ end
53
+
54
+ end
55
+ end
56
+ def parse! args
57
+ @opt_parser.parse! args
58
+
59
+ if (@use_key_file && !(File.exists? @key_file)) && @key_str.nil?
60
+ abort "No key string provided and #{@key_file} does not exist -- unable to authenticate. See http://dronebl.org/rpckey_signup if you need a key."
61
+ end
62
+ if @use_key_file && !@key_str.nil?
63
+ abort "Trying to use both --use-key-file and --key -- make up your mind!"
64
+ end
65
+ if (@use_key_file)
66
+ DroneBL::key = File.read(File.expand_path(@key_file)).chomp
67
+ else
68
+ DroneBL::key = @key_str
69
+ end
70
+ if @ips.nil? || (@ips.empty? && !@read_from_stdin)
71
+ abort 'No IPs given and you\'re not reading from stdin -- this is a noop!'
72
+ end
73
+ if @type.nil?
74
+ abort "No type given! Please read #{$0} --help again."
75
+ end
76
+
77
+ end
78
+ end
79
+
80
+ # end option parsing logic, begin program logic
81
+ opts = Options.new
82
+ opts.parse! ARGV
83
+
84
+ if opts.read_from_stdin
85
+ while line = STDIN.gets
86
+ opts.ips << line.chomp
87
+ end
88
+ end
89
+
90
+ opts.ips.uniq!
91
+
92
+ prevalid, ipv6 = opts.ips.partition { |ip| (ip.match(Resolv::IPv6::Regex).nil?) }
93
+ valid, invalid = prevalid.partition { |ip| !(ip.match(Resolv::IPv4::Regex).nil?) }
94
+ invalid += ipv6
95
+ if @dry_run
96
+ puts DroneBL::gen_add_query valid
97
+ puts "#{valid.count} IPs will be added as type #{opts.type}#{" with comment 'opts.comment if opts.comment}'"}."
98
+ else
99
+ response = DroneBL::add(valid, type, comment)
100
+ print_table response, opts.long_types
101
+ puts "#{valid.count} IPs looked up. #{response.map { |r| r['ip'] }.uniq.length} unique IPs found in response."
102
+ end
103
+ unless invalid.empty?
104
+ puts "IPs not valid for lookup: "
105
+ invalid.each { |ip| puts ip }
106
+ end
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env ruby
2
+ # copyright Rylee Fowler 2014
3
+ # see LICENSE for more details
4
+ require 'dronebl-client'
5
+ require 'resolv'
6
+ require 'optparse'
7
+ class Options
8
+ attr_reader :read_from_stdin, :key_str, :key_file, :get_archived, :long_types,
9
+ :dry_run, :time_format, :use_key_file
10
+ attr_accessor :ips
11
+ def initialize
12
+ @key_file = File.expand_path "~/.droneblkey"
13
+ @ips = []
14
+ @opt_parser = OptionParser.new do |opts|
15
+ opts.banner = "Usage: #{$0} [options]"
16
+ opts.separator ""
17
+ opts.separator "Options available:"
18
+ opts.on('-I', '--ips [ip1,ip2,ip3,...]', Array, 'Read in IPs.') do |list|
19
+ @ips += list
20
+ end
21
+ opts.on('-s', '--stdin', 'Read a newline-delimited list of IPs from STDIN') do
22
+ @read_from_stdin = true
23
+ end
24
+ opts.on('-k', '--key KEY', String, 'Use KEY as your DroneBL RPC2 key') do |key|
25
+ @key_str = key
26
+ end
27
+ opts.on('-f', '--keyfile [FILE_PATH]', 'Read the file at FILE_PATH and use it as the RPC key') do |path|
28
+ @use_key_file = true
29
+ @key_file = path
30
+ end
31
+ opts.on('-a', '--get-archived', 'Get *all* DroneBL listings of the given IP, not just active ones.') do
32
+ @get_archived = true
33
+ end
34
+ opts.on('-T', '--time-format [FORMAT]', String, 'Interpolate the time format string with FORMAT instead of the default. See `man 3 strftime` for format specifiers.') do |fmt|
35
+ @time_format = fmt
36
+ end
37
+ opts.on('-u', '--use-key-file', 'Use the default key file located at ~/.droneblkey') do
38
+ @use_key_file = true
39
+ end
40
+ opts.on('-L', '--long-types', 'Print type definitions instead of numeric types for record matches.') do
41
+ @long_types = true
42
+ end
43
+ opts.on('-d', '--dry-run', 'Prints the query to be run to STDOUT instead of sending it as a query to the DroneBL RPC service.') do
44
+ @dry_run = true
45
+ end
46
+ opts.on_tail("-h", "--help", "Show this message") do
47
+ puts opts
48
+ exit
49
+ end
50
+
51
+ end
52
+ end
53
+ def parse! args
54
+ @opt_parser.parse! args
55
+
56
+ if (@use_key_file && !(File.exists? @key_file)) && @key_str.nil?
57
+ abort "No key string provided and #{@key_file} does not exist -- unable to authenticate. See http://dronebl.org/rpckey_signup if you need a key."
58
+ end
59
+ if @use_key_file && !@key_str.nil?
60
+ abort "Trying to use both --use-key-file and --key -- make up your mind!"
61
+ end
62
+ if (@use_key_file)
63
+ DroneBL::key = File.read(File.expand_path(@key_file)).chomp
64
+ else
65
+ DroneBL::key = @key_str
66
+ end
67
+ if @ips.nil? || (@ips.empty? && !@read_from_stdin)
68
+ puts @ips
69
+ abort 'No IPs given and you\'re not reading from stdin -- this is a noop!'
70
+ end
71
+ end
72
+ end
73
+
74
+ # end option parsing logic, begin program logic
75
+ def print_table data, long_types=false
76
+ typelen = 5
77
+ if long_types
78
+ typelen = 35
79
+ end
80
+ puts [
81
+ 'IP'.ljust(15),
82
+ 'Currently listed?'.rjust(17),
83
+ 'Type'.rjust(typelen),
84
+ 'Comment'.ljust(25),
85
+ 'ID'.ljust(9),
86
+ 'Time'.ljust(25)
87
+ ].join '|'
88
+ puts '-' * 135
89
+ data.each do |data|
90
+ puts [
91
+ data['ip'].ljust(15),
92
+ (data['listed'] == '1' ? 'YES' : 'NO').rjust(17),
93
+ (long_types ? DroneBL::TYPES[data['type']] : data['type']).rjust(typelen),
94
+ data['comment'].ljust(25),
95
+ data['id'].ljust(9),
96
+ Time.at(data['timestamp'].to_i).to_s.ljust(25)
97
+ ].join '|'
98
+ end
99
+ puts '=' * 135
100
+ end
101
+ opts = Options.new
102
+ opts.parse! ARGV
103
+
104
+ if opts.read_from_stdin
105
+ while line = STDIN.gets
106
+ opts.ips << line.chomp
107
+ end
108
+ end
109
+
110
+ opts.ips.uniq!
111
+
112
+ prevalid, ipv6 = opts.ips.partition { |ip| (ip.match(Resolv::IPv6::Regex).nil?) }
113
+ valid, invalid = prevalid.partition { |ip| !(ip.match(Resolv::IPv4::Regex).nil?) }
114
+ invalid += ipv6
115
+ if @dry_run
116
+ puts DroneBL::gen_lookup_query valid
117
+ puts "#{valid.count} IPs will be looked up."
118
+ else
119
+ response = DroneBL::lookup(valid)
120
+ print_table response, opts.long_types
121
+ puts "#{valid.count} IPs looked up. #{response.map { |r| r['ip'] }.uniq.length} unique IPs found in response."
122
+ end
123
+ unless invalid.empty?
124
+ puts "IPs not valid for lookup: "
125
+ invalid.each { |ip| puts ip }
126
+ end
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env ruby
2
+ # copyright Rylee Fowler 2014
3
+ # see LICENSE for more details
4
+
5
+ require 'nokogiri'
6
+ require 'httparty'
7
+ module DroneBL
8
+ include HTTParty
9
+ format :xml
10
+ base_uri 'http://dronebl.org'
11
+ TYPES = {"1"=>"Testing class.",
12
+ "2"=>"Sample data",
13
+ "3"=>"IRC spam drone",
14
+ "5"=>"Bottler (experimental)",
15
+ "6"=>"Unknown worm or spambot",
16
+ "7"=>"DDoS drone",
17
+ "8"=>"Open SOCKS proxy",
18
+ "9"=>"Open HTTP proxy",
19
+ "10"=>"Proxychain",
20
+ "11"=>"Web Page Proxy",
21
+ "13"=>"Automated dictionary attacks",
22
+ "14"=>"Open WINGATE proxy",
23
+ "15"=>"Compromised router / gateway",
24
+ "16"=>"Autorooting worms",
25
+ "17"=>"Automatically determined botnet IP",
26
+ "255"=>"Uncategorized threat class"}
27
+ class << self
28
+ attr_accessor :key
29
+ def parse_response xml
30
+ # This giant mess of hax is needed because the DroneBL response to queries
31
+ # is encased in CDATA for whatever reason.
32
+ resp = Nokogiri::XML(xml).at("response")
33
+ if resp['type'].downcase == 'error'
34
+ abort "call failed: '#{resp.css('message').text}' data: '#{resp.css('data').text}'"
35
+ end
36
+ Nokogiri::XML("<?xml version='1.0'>\n<results>#{resp.text}</results>").css("result").map(&:to_h) # thanks to jhass in #ruby on freenode
37
+ end
38
+
39
+ def gen_lookup_query ips, archived=false
40
+ <<EOF
41
+ <?xml version='1.0'?>
42
+ <request key='#{key}'>
43
+ #{ips.map { |ip| "<lookup ip='#{ip}' listed='#{archived ? 2 : 1}'>"}.join("\n")}
44
+ </request>
45
+ EOF
46
+ end
47
+ def gen_add_query ips, type, comment=''
48
+ "<?xml version='1.0'?>
49
+ <request key='#{key}'>
50
+ #{ips.map { |ip| "<add ip='#{ip}' type='#{type}'#{ " comment='#{comment}'" unless comment.empty?}>"}.join("\n")}
51
+ </request>"
52
+ end
53
+
54
+ def lookup ips, archived=false
55
+ query = gen_lookup_query ips, archived
56
+ parse_response post('/RPC2', {:body => query }).body
57
+ end
58
+
59
+ def add ips, type, comment=''
60
+ query = gen_add_query ips, type, comment
61
+ parse_response post('/RPC2', {:body => query }).body
62
+ end
63
+
64
+ end
65
+ end
metadata ADDED
@@ -0,0 +1,50 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dronebl.rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Rylee Fowler
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-04 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: 'NOTE: You should create a ~/.droneblkey file with your key inside of
14
+ it for the best experience with this.'
15
+ email: rylee@rylee.me
16
+ executables:
17
+ - dronebl-query
18
+ - dronebl-add
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - bin/dronebl-add
23
+ - bin/dronebl-query
24
+ - lib/dronebl-client.rb
25
+ homepage: http://github.com/rylai-/dronebl.rb
26
+ licenses:
27
+ - MIT
28
+ metadata: {}
29
+ post_install_message:
30
+ rdoc_options: []
31
+ require_paths:
32
+ - lib
33
+ required_ruby_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ required_rubygems_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ requirements: []
44
+ rubyforge_project:
45
+ rubygems_version: 2.2.2
46
+ signing_key:
47
+ specification_version: 4
48
+ summary: Interface to the DroneBL RPC2 service
49
+ test_files: []
50
+ has_rdoc: