Skip to content

Commit c7129e0

Browse files
committed
Land rapid7#5069, breaking up with old options
2 parents 3fbd4e2 + d67f7a2 commit c7129e0

24 files changed

+977
-769
lines changed

lib/msf/core/module.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
module Msf
55

6+
autoload :OptionContainer, 'msf/core/option_container'
7+
68
###
79
#
810
# The module base class is responsible for providing the common interface

lib/msf/core/opt.rb

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# -*- coding: binary -*-
2+
3+
module Msf
4+
5+
#
6+
# Builtin framework options with shortcut methods
7+
#
8+
# @example
9+
# register_options(
10+
# [
11+
# Opt::RHOST,
12+
# Opt::RPORT(21),
13+
# ]
14+
# )
15+
# register_advanced_options([Opt::Proxies])
16+
#
17+
module Opt
18+
19+
# @return [OptAddress]
20+
def self.CHOST(default=nil, required=false, desc="The local client address")
21+
Msf::OptAddress.new(__method__.to_s, [ required, desc, default ])
22+
end
23+
24+
# @return [OptPort]
25+
def self.CPORT(default=nil, required=false, desc="The local client port")
26+
Msf::OptPort.new(__method__.to_s, [ required, desc, default ])
27+
end
28+
29+
# @return [OptAddress]
30+
def self.LHOST(default=nil, required=true, desc="The listen address")
31+
Msf::OptAddress.new(__method__.to_s, [ required, desc, default ])
32+
end
33+
34+
# @return [OptPort]
35+
def self.LPORT(default=nil, required=true, desc="The listen port")
36+
Msf::OptPort.new(__method__.to_s, [ required, desc, default ])
37+
end
38+
39+
# @return [OptString]
40+
def self.Proxies(default=nil, required=false, desc="A proxy chain of format type:host:port[,type:host:port][...]")
41+
Msf::OptString.new(__method__.to_s, [ required, desc, default ])
42+
end
43+
44+
# @return [OptAddress]
45+
def self.RHOST(default=nil, required=true, desc="The target address")
46+
Msf::OptAddress.new(__method__.to_s, [ required, desc, default ])
47+
end
48+
49+
# @return [OptPort]
50+
def self.RPORT(default=nil, required=true, desc="The target port")
51+
Msf::OptPort.new(__method__.to_s, [ required, desc, default ])
52+
end
53+
54+
# These are unused but remain for historical reasons
55+
class << self
56+
alias builtin_chost CHOST
57+
alias builtin_cport CPORT
58+
alias builtin_lhost LHOST
59+
alias builtin_lport LPORT
60+
alias builtin_proxies Proxies
61+
alias builtin_rhost RHOST
62+
alias builtin_rport RPORT
63+
end
64+
65+
CHOST = CHOST()
66+
CPORT = CPORT()
67+
LHOST = LHOST()
68+
LPORT = LPORT()
69+
Proxies = Proxies()
70+
RHOST = RHOST()
71+
RPORT = RPORT()
72+
end
73+
74+
end

lib/msf/core/opt_address.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# -*- coding: binary -*-
2+
3+
module Msf
4+
5+
###
6+
#
7+
# Network address option.
8+
#
9+
###
10+
class OptAddress < OptBase
11+
def type
12+
return 'address'
13+
end
14+
15+
def valid?(value)
16+
return false if empty_required_value?(value)
17+
return false unless value.kind_of?(String) or value.kind_of?(NilClass)
18+
19+
if (value != nil and value.empty? == false)
20+
begin
21+
getaddr_result = ::Rex::Socket.getaddress(value, true)
22+
# Covers a wierdcase where an incomplete ipv4 address will have it's
23+
# missing octets filled in with 0's. (e.g 192.168 become 192.0.0.168)
24+
# which does not feel like a legit behaviour
25+
if value =~ /^\d{1,3}(\.\d{1,3}){1,3}$/
26+
return false unless value =~ Rex::Socket::MATCH_IPV4
27+
end
28+
rescue
29+
return false
30+
end
31+
end
32+
33+
return super
34+
end
35+
end
36+
37+
end

lib/msf/core/opt_address_range.rb

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# -*- coding: binary -*-
2+
3+
module Msf
4+
5+
###
6+
#
7+
# Network address range option.
8+
#
9+
###
10+
class OptAddressRange < OptBase
11+
def type
12+
return 'addressrange'
13+
end
14+
15+
def normalize(value)
16+
return nil unless value.kind_of?(String)
17+
if (value =~ /^file:(.*)/)
18+
path = $1
19+
return false if not File.exists?(path) or File.directory?(path)
20+
return File.readlines(path).map{ |s| s.strip}.join(" ")
21+
elsif (value =~ /^rand:(.*)/)
22+
count = $1.to_i
23+
return false if count < 1
24+
ret = ''
25+
count.times {
26+
ret << " " if not ret.empty?
27+
ret << [ rand(0x100000000) ].pack("N").unpack("C*").map{|x| x.to_s }.join(".")
28+
}
29+
return ret
30+
end
31+
return value
32+
end
33+
34+
def valid?(value)
35+
return false if empty_required_value?(value)
36+
return false unless value.kind_of?(String) or value.kind_of?(NilClass)
37+
38+
if (value != nil and value.empty? == false)
39+
normalized = normalize(value)
40+
return false if normalized.nil?
41+
walker = Rex::Socket::RangeWalker.new(normalized)
42+
if (not walker or not walker.valid?)
43+
return false
44+
end
45+
end
46+
47+
return super
48+
end
49+
end
50+
51+
end

lib/msf/core/opt_base.rb

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
# -*- coding: binary -*-
2+
require 'resolv'
3+
require 'msf/core'
4+
require 'rex/socket'
5+
6+
module Msf
7+
8+
###
9+
#
10+
# The base class for all options.
11+
#
12+
###
13+
class OptBase
14+
15+
#
16+
# Initializes a named option with the supplied attribute array.
17+
# The array is composed of three values.
18+
#
19+
# attrs[0] = required (boolean type)
20+
# attrs[1] = description (string)
21+
# attrs[2] = default value
22+
# attrs[3] = possible enum values
23+
# attrs[4] = Regex to validate the option
24+
#
25+
def initialize(in_name, attrs = [])
26+
self.name = in_name
27+
self.advanced = false
28+
self.evasion = false
29+
self.required = attrs[0] || false
30+
self.desc = attrs[1]
31+
self.default = attrs[2]
32+
self.enums = [ *(attrs[3]) ].map { |x| x.to_s }
33+
regex_temp = attrs[4] || nil
34+
if regex_temp
35+
# convert to string
36+
regex_temp = regex_temp.to_s if regex_temp.is_a? Regexp
37+
# remove start and end character, they will be added later
38+
regex_temp = regex_temp.sub(/^\^/, '').sub(/\$$/, '')
39+
# Add start and end marker to match the whole regex
40+
regex_temp = "^#{regex_temp}$"
41+
begin
42+
Regexp.compile(regex_temp)
43+
self.regex = regex_temp
44+
rescue RegexpError, TypeError => e
45+
raise("Invalid Regex #{regex_temp}: #{e}")
46+
end
47+
end
48+
end
49+
50+
#
51+
# Returns true if this is a required option.
52+
#
53+
def required?
54+
return required
55+
end
56+
57+
#
58+
# Returns true if this is an advanced option.
59+
#
60+
def advanced?
61+
return advanced
62+
end
63+
64+
#
65+
# Returns true if this is an evasion option.
66+
#
67+
def evasion?
68+
return evasion
69+
end
70+
71+
#
72+
# Returns true if the supplied type is equivalent to this option's type.
73+
#
74+
def type?(in_type)
75+
return (type == in_type)
76+
end
77+
78+
#
79+
# If it's required and the value is nil or empty, then it's not valid.
80+
#
81+
def valid?(value)
82+
if required?
83+
# required variable not set
84+
return false if (value == nil or value.to_s.empty?)
85+
end
86+
if regex
87+
if value.match(regex)
88+
return true
89+
else
90+
return false
91+
end
92+
end
93+
return true
94+
end
95+
96+
#
97+
# Returns true if the value supplied is nil and it's required to be
98+
# a valid value
99+
#
100+
def empty_required_value?(value)
101+
return (required? and value.nil?)
102+
end
103+
104+
#
105+
# Normalizes the supplied value to conform with the type that the option is
106+
# conveying.
107+
#
108+
def normalize(value)
109+
value
110+
end
111+
112+
#
113+
# Returns a string representing a user-friendly display of the chosen value
114+
#
115+
def display_value(value)
116+
value.to_s
117+
end
118+
119+
#
120+
# The name of the option.
121+
#
122+
attr_reader :name
123+
#
124+
# Whether or not the option is required.
125+
#
126+
attr_reader :required
127+
#
128+
# The description of the option.
129+
#
130+
attr_reader :desc
131+
#
132+
# The default value of the option.
133+
#
134+
attr_reader :default
135+
#
136+
# Storing the name of the option.
137+
#
138+
attr_writer :name
139+
#
140+
# Whether or not this is an advanced option.
141+
#
142+
attr_accessor :advanced
143+
#
144+
# Whether or not this is an evasion option.
145+
#
146+
attr_accessor :evasion
147+
#
148+
# The module or entity that owns this option.
149+
#
150+
attr_accessor :owner
151+
#
152+
# The list of potential valid values
153+
#
154+
attr_accessor :enums
155+
#
156+
# A optional regex to validate the option value
157+
#
158+
attr_accessor :regex
159+
160+
protected
161+
162+
attr_writer :required, :desc, :default # :nodoc:
163+
end
164+
165+
end
166+

lib/msf/core/opt_bool.rb

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# -*- coding: binary -*-
2+
3+
module Msf
4+
5+
###
6+
#
7+
# Boolean option.
8+
#
9+
###
10+
class OptBool < OptBase
11+
12+
TrueRegex = /^(y|yes|t|1|true)$/i
13+
14+
def type
15+
return 'bool'
16+
end
17+
18+
def valid?(value)
19+
return false if empty_required_value?(value)
20+
21+
if ((value != nil and
22+
(value.to_s.empty? == false) and
23+
(value.to_s.match(/^(y|yes|n|no|t|f|0|1|true|false)$/i) == nil)))
24+
return false
25+
end
26+
27+
true
28+
end
29+
30+
def normalize(value)
31+
if(value.nil? or value.to_s.match(TrueRegex).nil?)
32+
false
33+
else
34+
true
35+
end
36+
end
37+
38+
def is_true?(value)
39+
return normalize(value)
40+
end
41+
42+
def is_false?(value)
43+
return !is_true?(value)
44+
end
45+
46+
end
47+
48+
end

0 commit comments

Comments
 (0)