ronin 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +11 -0
- data/ChangeLog.md +28 -10
- data/Gemfile +1 -1
- data/README.md +14 -41
- data/Rakefile +3 -0
- data/gemspec.yml +14 -15
- data/lib/bond/completions/ronin.rb +0 -5
- data/lib/ronin/address.rb +17 -0
- data/lib/ronin/author.rb +1 -1
- data/lib/ronin/database/database.rb +11 -3
- data/lib/ronin/email_address.rb +33 -0
- data/lib/ronin/host_name.rb +57 -6
- data/lib/ronin/ip_address.rb +49 -5
- data/lib/ronin/license.rb +1 -1
- data/lib/ronin/mac_address.rb +39 -1
- data/lib/ronin/model/has_license.rb +12 -3
- data/lib/ronin/model/importable.rb +65 -0
- data/lib/ronin/repository.rb +12 -4
- data/lib/ronin/spec/database.rb +1 -1
- data/lib/ronin/tcp_port.rb +3 -12
- data/lib/ronin/udp_port.rb +2 -11
- data/lib/ronin/ui/cli/commands/emails.rb +0 -45
- data/lib/ronin/ui/cli/commands/hosts.rb +4 -31
- data/lib/ronin/ui/cli/commands/ips.rb +4 -30
- data/lib/ronin/ui/cli/commands/urls.rb +5 -45
- data/lib/ronin/ui/cli/model_command.rb +17 -27
- data/lib/ronin/ui/cli/resources_command.rb +25 -1
- data/lib/ronin/ui/cli/script_command.rb +1 -1
- data/lib/ronin/ui/console/context.rb +1 -1
- data/lib/ronin/url.rb +43 -1
- data/lib/ronin/version.rb +1 -1
- data/ronin.gemspec +1 -1
- data/spec/email_address_spec.rb +20 -0
- data/spec/host_name_spec.rb +20 -0
- data/spec/ip_address_spec.rb +104 -0
- data/spec/mac_address_spec.rb +20 -0
- data/spec/url_spec.rb +24 -0
- metadata +118 -155
- data/lib/ronin/network/mixins.rb +0 -27
- data/lib/ronin/network/mixins/esmtp.rb +0 -165
- data/lib/ronin/network/mixins/http.rb +0 -723
- data/lib/ronin/network/mixins/imap.rb +0 -151
- data/lib/ronin/network/mixins/pop3.rb +0 -141
- data/lib/ronin/network/mixins/smtp.rb +0 -159
- data/lib/ronin/network/mixins/tcp.rb +0 -331
- data/lib/ronin/network/mixins/telnet.rb +0 -199
- data/lib/ronin/network/mixins/udp.rb +0 -227
- data/lib/ronin/spec/ui/output.rb +0 -28
- data/lib/ronin/ui/output.rb +0 -21
- data/lib/ronin/ui/output/helpers.rb +0 -248
- data/lib/ronin/ui/output/output.rb +0 -146
- data/lib/ronin/ui/output/terminal.rb +0 -21
- data/lib/ronin/ui/output/terminal/color.rb +0 -118
- data/lib/ronin/ui/output/terminal/raw.rb +0 -103
- data/lib/ronin/ui/shell.rb +0 -92
- data/spec/ip_address.rb +0 -84
- data/spec/ui/output_spec.rb +0 -32
@@ -65,11 +65,9 @@ module Ronin
|
|
65
65
|
# @since 1.0.0
|
66
66
|
#
|
67
67
|
def execute
|
68
|
-
if options[:
|
69
|
-
import options[:import]
|
70
|
-
elsif options[:lookup]
|
68
|
+
if options[:lookup]
|
71
69
|
lookup options[:lookup]
|
72
|
-
|
70
|
+
else
|
73
71
|
super
|
74
72
|
end
|
75
73
|
end
|
@@ -94,30 +92,6 @@ module Ronin
|
|
94
92
|
print_info "Looked up #{host}"
|
95
93
|
end
|
96
94
|
|
97
|
-
#
|
98
|
-
# Extracts and saves IP Addresses from a file.
|
99
|
-
#
|
100
|
-
# @param [String] path
|
101
|
-
# The path of the file.
|
102
|
-
#
|
103
|
-
# @since 1.0.0
|
104
|
-
#
|
105
|
-
def import(path)
|
106
|
-
File.open(options[:import]) do |file|
|
107
|
-
file.each_line do |line|
|
108
|
-
IPAddr.extract(line) do |match|
|
109
|
-
ip = IPAddress.new(:address => match)
|
110
|
-
|
111
|
-
if ip.save
|
112
|
-
print_info "Imported #{ip}"
|
113
|
-
else
|
114
|
-
print_error "Unable to import #{match.dump}."
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
95
|
#
|
122
96
|
# Prints an IP Address.
|
123
97
|
#
|
@@ -133,11 +107,11 @@ module Ronin
|
|
133
107
|
|
134
108
|
indent do
|
135
109
|
if (org = ip.organization)
|
136
|
-
print_hash
|
110
|
+
print_hash 'Organization' => org
|
137
111
|
end
|
138
112
|
|
139
113
|
if (last_scanned_at = ip.last_scanned_at)
|
140
|
-
print_hash
|
114
|
+
print_hash 'Last Scanned' => last_scanned_at
|
141
115
|
end
|
142
116
|
|
143
117
|
unless ip.mac_addresses.empty?
|
@@ -62,46 +62,8 @@ module Ronin
|
|
62
62
|
:aliases => '-i',
|
63
63
|
:banner => 'FILE'
|
64
64
|
|
65
|
-
#
|
66
|
-
# Queries the {Ronin::URL} model.
|
67
|
-
#
|
68
|
-
# @since 1.0.0
|
69
|
-
#
|
70
|
-
def execute
|
71
|
-
if options[:import]
|
72
|
-
import options[:import]
|
73
|
-
elsif options.list?
|
74
|
-
super
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
65
|
protected
|
79
66
|
|
80
|
-
#
|
81
|
-
# Imports URLs from a file.
|
82
|
-
#
|
83
|
-
# @param [String] path
|
84
|
-
# The path to the file.
|
85
|
-
#
|
86
|
-
# @since 1.0.0
|
87
|
-
#
|
88
|
-
def import(path)
|
89
|
-
File.open(path) do |file|
|
90
|
-
file.each_line do |line|
|
91
|
-
line.strip!
|
92
|
-
next if line.empty?
|
93
|
-
|
94
|
-
url = URL.parse(line)
|
95
|
-
|
96
|
-
if url.save
|
97
|
-
print_info "Imported #{url}"
|
98
|
-
else
|
99
|
-
print_error "Could not import #{line.dump}."
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
67
|
#
|
106
68
|
# Prints a URL.
|
107
69
|
#
|
@@ -116,13 +78,11 @@ module Ronin
|
|
116
78
|
print_title url
|
117
79
|
|
118
80
|
indent do
|
119
|
-
print_hash
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
'Last Scanned' => url.last_scanned_at
|
125
|
-
)
|
81
|
+
print_hash 'Host' => url.host_name,
|
82
|
+
'Port' => url.port.number,
|
83
|
+
'Path' => url.path,
|
84
|
+
'Fragment' => url.fragment,
|
85
|
+
'Last Scanned' => url.last_scanned_at
|
126
86
|
|
127
87
|
unless url.query_params.empty?
|
128
88
|
params = {}
|
@@ -31,22 +31,6 @@ module Ronin
|
|
31
31
|
class_option :database, :type => :string,
|
32
32
|
:aliases => '-D'
|
33
33
|
|
34
|
-
#
|
35
|
-
# The model to query.
|
36
|
-
#
|
37
|
-
# @return [DataMapper::Resource]
|
38
|
-
# The model that will be queried.
|
39
|
-
#
|
40
|
-
# @since 1.1.0
|
41
|
-
#
|
42
|
-
# @api semipublic
|
43
|
-
#
|
44
|
-
def self.query_model
|
45
|
-
@query_model ||= if self.superclass < ModelCommand
|
46
|
-
self.superclass.query_model
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
34
|
#
|
51
35
|
# The query options for the command.
|
52
36
|
#
|
@@ -88,9 +72,9 @@ module Ronin
|
|
88
72
|
protected
|
89
73
|
|
90
74
|
#
|
91
|
-
# Sets the model to query.
|
75
|
+
# Sets or gets the model to query.
|
92
76
|
#
|
93
|
-
# @param [DataMapper::Resource] model
|
77
|
+
# @param [DataMapper::Resource, nil] model
|
94
78
|
# The model class.
|
95
79
|
#
|
96
80
|
# @return [DataMapper::Resource]
|
@@ -100,8 +84,14 @@ module Ronin
|
|
100
84
|
#
|
101
85
|
# @api semipublic
|
102
86
|
#
|
103
|
-
def self.model(model)
|
104
|
-
|
87
|
+
def self.model(model=nil)
|
88
|
+
if model
|
89
|
+
@model = model
|
90
|
+
else
|
91
|
+
@model ||= if superclass < ModelCommand
|
92
|
+
superclass.model
|
93
|
+
end
|
94
|
+
end
|
105
95
|
end
|
106
96
|
|
107
97
|
#
|
@@ -119,7 +109,7 @@ module Ronin
|
|
119
109
|
#
|
120
110
|
def self.query_option(name,options={})
|
121
111
|
query_options << name
|
122
|
-
class_option
|
112
|
+
class_option name, options
|
123
113
|
end
|
124
114
|
|
125
115
|
#
|
@@ -154,11 +144,11 @@ module Ronin
|
|
154
144
|
# @api semipublic
|
155
145
|
#
|
156
146
|
def query
|
157
|
-
unless self.class.
|
147
|
+
unless self.class.model
|
158
148
|
raise("query model not defined for #{self.class}")
|
159
149
|
end
|
160
150
|
|
161
|
-
query = self.class.
|
151
|
+
query = self.class.model.all
|
162
152
|
|
163
153
|
self.class.each_query_option do |name|
|
164
154
|
value = options[name]
|
@@ -166,10 +156,10 @@ module Ronin
|
|
166
156
|
# skip unset options
|
167
157
|
next if value.nil?
|
168
158
|
|
169
|
-
if
|
159
|
+
if model.properties.named?(name)
|
170
160
|
query = query.all(name => value)
|
171
|
-
elsif
|
172
|
-
query_method =
|
161
|
+
elsif model.respond_to?(name)
|
162
|
+
query_method = model.method(name)
|
173
163
|
|
174
164
|
query = case query_method.arity
|
175
165
|
when 0
|
@@ -180,7 +170,7 @@ module Ronin
|
|
180
170
|
query.send(name,*value)
|
181
171
|
end
|
182
172
|
else
|
183
|
-
raise("unknown query method or property #{name} for #{
|
173
|
+
raise("unknown query method or property #{name} for #{model}")
|
184
174
|
end
|
185
175
|
end
|
186
176
|
|
@@ -18,6 +18,7 @@
|
|
18
18
|
#
|
19
19
|
|
20
20
|
require 'ronin/ui/cli/model_command'
|
21
|
+
require 'ronin/model/importable'
|
21
22
|
|
22
23
|
module Ronin
|
23
24
|
module UI
|
@@ -40,11 +41,34 @@ module Ronin
|
|
40
41
|
# @api semipublic
|
41
42
|
#
|
42
43
|
def execute
|
43
|
-
|
44
|
+
if options[:import]
|
45
|
+
self.class.model.import(options[:import]) do |resource|
|
46
|
+
print_info "Imported #{resource}"
|
47
|
+
end
|
48
|
+
else
|
49
|
+
print_resources(query)
|
50
|
+
end
|
44
51
|
end
|
45
52
|
|
46
53
|
protected
|
47
54
|
|
55
|
+
#
|
56
|
+
# Sets the model used by the command.
|
57
|
+
#
|
58
|
+
# @see ModelCommand.model
|
59
|
+
#
|
60
|
+
# @since 1.3.0
|
61
|
+
#
|
62
|
+
def self.model(model=nil)
|
63
|
+
if (model && model < Model::Importable)
|
64
|
+
class_option :import, :type => :string,
|
65
|
+
:aliases => '-i',
|
66
|
+
:banner => 'FILE'
|
67
|
+
end
|
68
|
+
|
69
|
+
return super(model)
|
70
|
+
end
|
71
|
+
|
48
72
|
#
|
49
73
|
# Default method which will print every queried resource.
|
50
74
|
#
|
data/lib/ronin/url.rb
CHANGED
@@ -18,6 +18,7 @@
|
|
18
18
|
#
|
19
19
|
|
20
20
|
require 'ronin/model'
|
21
|
+
require 'ronin/model/importable'
|
21
22
|
require 'ronin/url_scheme'
|
22
23
|
require 'ronin/url_query_param'
|
23
24
|
require 'ronin/host_name'
|
@@ -25,6 +26,7 @@ require 'ronin/tcp_port'
|
|
25
26
|
require 'ronin/web_credential'
|
26
27
|
|
27
28
|
require 'dm-timestamps'
|
29
|
+
require 'uri/generic'
|
28
30
|
require 'uri/http'
|
29
31
|
require 'uri/https'
|
30
32
|
require 'uri/ftp'
|
@@ -37,6 +39,7 @@ module Ronin
|
|
37
39
|
class URL
|
38
40
|
|
39
41
|
include Model
|
42
|
+
include Model::Importable
|
40
43
|
include DataMapper::Timestamps
|
41
44
|
|
42
45
|
# Mapping of URL Schemes and URI classes
|
@@ -76,6 +79,45 @@ module Ronin
|
|
76
79
|
# Defines the created_at timestamp
|
77
80
|
timestamps :created_at
|
78
81
|
|
82
|
+
#
|
83
|
+
# Extracts URLs from the given text.
|
84
|
+
#
|
85
|
+
# @param [String] text
|
86
|
+
# The text to parse.
|
87
|
+
#
|
88
|
+
# @yield [url]
|
89
|
+
# The given block will be passed each extracted URL.
|
90
|
+
#
|
91
|
+
# @yieldparam [URL] url
|
92
|
+
# An extracted URL from the text.
|
93
|
+
#
|
94
|
+
# @return [Array<URL>]
|
95
|
+
# If no block is given, an Array of the extracted URLs is returned.
|
96
|
+
#
|
97
|
+
# @see http://rubydoc.info/stdlib/uri/URI#extract-class_method
|
98
|
+
# @see URL.parse
|
99
|
+
#
|
100
|
+
# @since 1.3.0
|
101
|
+
#
|
102
|
+
# @api public
|
103
|
+
#
|
104
|
+
def self.extract(text)
|
105
|
+
return enum_for(:extract,text).to_a unless block_given?
|
106
|
+
|
107
|
+
::URI.extract(text) do |uri|
|
108
|
+
uri = begin
|
109
|
+
::URI.parse(uri)
|
110
|
+
rescue URI::InvalidURIError
|
111
|
+
# URI.extract can parse URIs that URI.parse cannot handle
|
112
|
+
next
|
113
|
+
end
|
114
|
+
|
115
|
+
yield from(uri)
|
116
|
+
end
|
117
|
+
|
118
|
+
return nil
|
119
|
+
end
|
120
|
+
|
79
121
|
#
|
80
122
|
# Searches for all URLs using HTTP.
|
81
123
|
#
|
@@ -403,7 +445,7 @@ module Ronin
|
|
403
445
|
#
|
404
446
|
def to_uri
|
405
447
|
# map the URL scheme to a URI class
|
406
|
-
url_class = (
|
448
|
+
url_class = SCHEMES.fetch(self.scheme.name,::URI::Generic)
|
407
449
|
|
408
450
|
host = if self.host_name
|
409
451
|
self.host_name.address
|
data/lib/ronin/version.rb
CHANGED
data/ronin.gemspec
CHANGED
@@ -99,7 +99,7 @@ Gem::Specification.new do |gemspec|
|
|
99
99
|
end
|
100
100
|
|
101
101
|
if gemspec.respond_to?(:required_rubygems_version=)
|
102
|
-
gemspec.required_rubygems_version = metadata['
|
102
|
+
gemspec.required_rubygems_version = metadata['required_rubygems_version']
|
103
103
|
end
|
104
104
|
|
105
105
|
parse_versions = lambda { |versions|
|
data/spec/email_address_spec.rb
CHANGED
@@ -13,6 +13,26 @@ describe EmailAddress do
|
|
13
13
|
)
|
14
14
|
}
|
15
15
|
|
16
|
+
describe "extract" do
|
17
|
+
subject { described_class }
|
18
|
+
|
19
|
+
let(:email1) { subject.parse('foo@bar.com') }
|
20
|
+
let(:email2) { subject.parse('foo!bar@baz.com') }
|
21
|
+
let(:text) { "To: #{email1}, #{email2}." }
|
22
|
+
|
23
|
+
it "should extract multiple email addresses from text" do
|
24
|
+
subject.extract(text).should == [email1, email2]
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should yield the extracted email addresses if a block is given" do
|
28
|
+
emails = []
|
29
|
+
|
30
|
+
subject.extract(text) { |email| emails << email }
|
31
|
+
|
32
|
+
emails.should == [email1, email2]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
16
36
|
describe "parse" do
|
17
37
|
it "should parse email addresses" do
|
18
38
|
email_address = EmailAddress.parse(email)
|
data/spec/host_name_spec.rb
CHANGED
@@ -17,6 +17,26 @@ describe HostName do
|
|
17
17
|
subject.name.should == subject.address
|
18
18
|
end
|
19
19
|
|
20
|
+
describe "extract" do
|
21
|
+
subject { described_class }
|
22
|
+
|
23
|
+
let(:host1) { subject.parse('www.example.com') }
|
24
|
+
let(:host2) { subject.parse('1.2.3.4.in-addr.arpa') }
|
25
|
+
let(:text) { "Hosts: #{host1}, #{host2}." }
|
26
|
+
|
27
|
+
it "should extract multiple host-names from text" do
|
28
|
+
subject.extract(text).should == [host1, host2]
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should yield the extracted host-names if a block is given" do
|
32
|
+
hosts = []
|
33
|
+
|
34
|
+
subject.extract(text) { |host| hosts << host }
|
35
|
+
|
36
|
+
hosts.should == [host1, host2]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
20
40
|
describe "lookup" do
|
21
41
|
subject { HostName }
|
22
42
|
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ronin/ip_address'
|
3
|
+
|
4
|
+
describe IPAddress do
|
5
|
+
let(:example_domain) { 'localhost' }
|
6
|
+
let(:example_ip) { '127.0.0.1' }
|
7
|
+
|
8
|
+
subject { IPAddress.new(:address => example_ip) }
|
9
|
+
|
10
|
+
it "should require an address" do
|
11
|
+
ip_address = IPAddress.new
|
12
|
+
|
13
|
+
ip_address.should_not be_valid
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "extract" do
|
17
|
+
subject { IPAddress }
|
18
|
+
|
19
|
+
let(:ip1) { subject.parse('127.0.0.1') }
|
20
|
+
let(:ip2) { subject.parse('10.1.1.1') }
|
21
|
+
let(:text) { "Hosts: #{ip1}, #{ip2}" }
|
22
|
+
|
23
|
+
it "should extract multiple IP Addresses from text" do
|
24
|
+
subject.extract(text).should == [ip1, ip2]
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should yield the extracted IPs if a block is given" do
|
28
|
+
ip_addresses = []
|
29
|
+
|
30
|
+
subject.extract(text) { |ip| ip_addresses << ip }
|
31
|
+
|
32
|
+
ip_addresses.should == [ip1, ip2]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "lookup" do
|
37
|
+
subject { IPAddress }
|
38
|
+
|
39
|
+
let(:bad_domain) { '.bad.domain.com.' }
|
40
|
+
|
41
|
+
it "should lookup host-names to IP Addresses" do
|
42
|
+
ip_addresses = subject.lookup(example_domain)
|
43
|
+
addresses = ip_addresses.map { |ip| ip.address }
|
44
|
+
|
45
|
+
addresses.should include(example_ip)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should associate the IP addresses with the original host name" do
|
49
|
+
ip_addresses = subject.lookup(example_domain)
|
50
|
+
host_names = ip_addresses.map { |ip| ip.host_names[0].address }
|
51
|
+
|
52
|
+
host_names.should include(example_domain)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should return an empty Array for unknown domain names" do
|
56
|
+
ip_addresses = subject.lookup(bad_domain)
|
57
|
+
|
58
|
+
ip_addresses.should be_empty
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "#lookup!" do
|
63
|
+
let(:bad_ip) { '0.0.0.0' }
|
64
|
+
|
65
|
+
it "should reverse lookup the host-name for an IP Address" do
|
66
|
+
host_names = subject.lookup!
|
67
|
+
addresses = host_names.map { |host_name| host_name.address }
|
68
|
+
|
69
|
+
addresses.should include(example_domain)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should associate the host names with the original IP address" do
|
73
|
+
host_names = subject.lookup!
|
74
|
+
ip_addresses = host_names.map do |host_name|
|
75
|
+
host_name.ip_addresses[0].address
|
76
|
+
end
|
77
|
+
|
78
|
+
ip_addresses.should include(subject)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should return an empty Array for unknown domain names" do
|
82
|
+
ip_address = IPAddress.new(:address => bad_ip)
|
83
|
+
host_names = ip_address.lookup!
|
84
|
+
|
85
|
+
host_names.should be_empty
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "#version" do
|
90
|
+
let(:ipv4) { IPAddress.new(:address => '127.0.0.1') }
|
91
|
+
let(:ipv6) { IPAddress.new(:address => '::1') }
|
92
|
+
|
93
|
+
it "should only accept 4 or 6" do
|
94
|
+
ip_address = IPAddress.new(:address => '1.1.1.1', :version => 7)
|
95
|
+
|
96
|
+
ip_address.should_not be_valid
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should default to the version of the address" do
|
100
|
+
ipv4.version.should == 4
|
101
|
+
ipv6.version.should == 6
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|