Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
beefproject
GitHub Repository: beefproject/beef
Path: blob/master/extensions/dns/model.rb
1154 views
1
#
2
# Copyright (c) 2006-2025 Wade Alcorn - [email protected]
3
# Browser Exploitation Framework (BeEF) - https://beefproject.com
4
# See the file 'doc/COPYING' for copying permission
5
#
6
module BeEF
7
module Core
8
module Models
9
module Dns
10
# Represents an individual DNS rule.
11
class Rule < BeEF::Core::Model
12
# Hooks the model's "save" event. Validates pattern/response and generates a rule identifier.
13
before_save :check_rule
14
self.table_name = 'dns_rules'
15
serialize :response, type: Array
16
17
private
18
19
def check_rule
20
validate_pattern(pattern)
21
self.callback = format_callback(resource.constantize, response)
22
rescue InvalidDnsPatternError, UnknownDnsResourceError, InvalidDnsResponseError => e
23
print_error e.message
24
throw :halt
25
26
# self.id = BeEF::Core::Crypto.dns_rule_id
27
end
28
29
# Verifies that the given pattern is valid (i.e. non-empty, no null's or printable characters).
30
def validate_pattern(pattern)
31
raise InvalidDnsPatternError unless BeEF::Filters.is_non_empty_string?(pattern) &&
32
!BeEF::Filters.has_null?(pattern) &&
33
!BeEF::Filters.has_non_printable_char?(pattern)
34
end
35
36
# Strict validator which ensures that only an appropriate response is given.
37
#
38
# @param resource [Resolv::DNS::Resource::IN] resource record type
39
# @param response [String, Symbol, Array] response to include in callback
40
#
41
# @return [String] string representation of callback that can safely be eval'd
42
def format_callback(resource, response)
43
sym_regex = /^:?(NoError|FormErr|ServFail|NXDomain|NotImp|Refused|NotAuth)$/i
44
45
if resource == Resolv::DNS::Resource::IN::A
46
if response.is_a?(String) && BeEF::Filters.is_valid_ip?(response, :ipv4)
47
format "t.respond!('%s')", response
48
elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response.to_s =~ sym_regex
49
format 't.fail!(:%s)', response.to_sym
50
elsif response.is_a?(Array)
51
str1 = "t.respond!('%s');"
52
str2 = ''
53
54
response.each do |r|
55
raise InvalidDnsResponseError, 'A' unless BeEF::Filters.is_valid_ip?(r, :ipv4)
56
57
str2 << format(str1, r)
58
end
59
60
str2
61
else
62
raise InvalidDnsResponseError, 'A'
63
end
64
elsif resource == Resolv::DNS::Resource::IN::AAAA
65
if response.is_a?(String) && BeEF::Filters.is_valid_ip?(response, :ipv6)
66
format "t.respond!('%s')", response
67
elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response =~ sym_regex
68
format 't.fail!(:%s)', response.to_sym
69
elsif response.is_a?(Array)
70
str1 = "t.respond!('%s');"
71
str2 = ''
72
73
response.each do |r|
74
raise InvalidDnsResponseError, 'AAAA' unless BeEF::Filters.is_valid_ip?(r, :ipv6)
75
76
str2 << format(str1, r)
77
end
78
79
str2
80
else
81
raise InvalidDnsResponseError, 'AAAA'
82
end
83
elsif resource == Resolv::DNS::Resource::IN::CNAME
84
if response.is_a?(String) && BeEF::Filters.is_valid_domain?(response)
85
format "t.respond!(Resolv::DNS::Name.create('%s'))", response
86
elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response =~ sym_regex
87
format 't.fail!(:%s)', response.to_sym
88
else
89
raise InvalidDnsResponseError, 'CNAME'
90
end
91
elsif resource == Resolv::DNS::Resource::IN::MX
92
if response[0].is_a?(Integer) &&
93
BeEF::Filters.is_valid_domain?(response[1])
94
95
data = { preference: response[0], exchange: response[1] }
96
format "t.respond!(%<preference>d, Resolv::DNS::Name.create('%<exchange>s'))", data
97
elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response =~ sym_regex
98
format 't.fail!(:%s)', response.to_sym
99
else
100
raise InvalidDnsResponseError, 'MX'
101
end
102
elsif resource == Resolv::DNS::Resource::IN::NS
103
if response.is_a?(String) && BeEF::Filters.is_valid_domain?(response)
104
format "t.respond!(Resolv::DNS::Name.create('%s'))", response
105
elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response =~ sym_regex
106
format 't.fail!(:%s)', response.to_sym
107
elsif response.is_a?(Array)
108
str1 = "t.respond!(Resolv::DNS::Name.create('%s'))"
109
str2 = ''
110
111
response.each do |r|
112
raise InvalidDnsResponseError, 'NS' unless BeEF::Filters.is_valid_domain?(r)
113
114
str2 << format(str1, r)
115
end
116
117
str2
118
else
119
raise InvalidDnsResponseError, 'NS'
120
end
121
elsif resource == Resolv::DNS::Resource::IN::PTR
122
if response.is_a?(String) && BeEF::Filters.is_valid_domain?(response)
123
format "t.respond!(Resolv::DNS::Name.create('%s'))", response
124
elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response =~ sym_regex
125
format 't.fail!(:%s)', response.to_sym
126
else
127
raise InvalidDnsResponseError, 'PTR'
128
end
129
elsif resource == Resolv::DNS::Resource::IN::SOA
130
if response.is_a?(Array)
131
unless BeEF::Filters.is_valid_domain?(response[0]) &&
132
BeEF::Filters.is_valid_domain?(response[1]) &&
133
response[2].is_a?(Integer) &&
134
response[3].is_a?(Integer) &&
135
response[4].is_a?(Integer) &&
136
response[5].is_a?(Integer) &&
137
response[6].is_a?(Integer)
138
139
raise InvalidDnsResponseError, 'SOA'
140
end
141
142
data = {
143
mname: response[0],
144
rname: response[1],
145
serial: response[2],
146
refresh: response[3],
147
retry: response[4],
148
expire: response[5],
149
minimum: response[6]
150
}
151
152
format "t.respond!(Resolv::DNS::Name.create('%<mname>s'), " +
153
"Resolv::DNS::Name.create('%<rname>s'), " +
154
'%<serial>d, ' +
155
'%<refresh>d, ' +
156
'%<retry>d, ' +
157
'%<expire>d, ' +
158
'%<minimum>d)',
159
data
160
elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response =~ sym_regex
161
format 't.fail!(:%s)', response.to_sym
162
else
163
raise InvalidDnsResponseError, 'SOA'
164
end
165
elsif resource == Resolv::DNS::Resource::IN::WKS
166
if response.is_a?(Array)
167
if !BeEF::Filters.is_valid_ip?(resource[0]) &&
168
resource[1].is_a?(Integer) &&
169
resource[2].is_a?(Integer) && !resource.is_a?(String)
170
raise InvalidDnsResponseError, 'WKS'
171
end
172
173
data = {
174
address: response[0],
175
protocol: response[1],
176
bitmap: response[2]
177
}
178
179
format "t.respond!('%<address>s', %<protocol>d, %<bitmap>d)", data
180
elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response =~ sym_regex
181
format 't.fail!(:%s)', response.to_sym
182
else
183
raise InvalidDnsResponseError, 'WKS'
184
end
185
else
186
raise UnknownDnsResourceError
187
end
188
end
189
190
# Raised when an invalid pattern is given.
191
class InvalidDnsPatternError < StandardError
192
DEFAULT_MESSAGE = 'Failed to add DNS rule with invalid pattern'
193
194
def initialize(message = nil)
195
super(message || DEFAULT_MESSAGE)
196
end
197
end
198
199
# Raised when a response is not valid for the given DNS resource record.
200
class InvalidDnsResponseError < StandardError
201
def initialize(message = nil)
202
str = 'Failed to add DNS rule with invalid response for %s resource record', message
203
message = format str, message unless message.nil?
204
super(message)
205
end
206
end
207
208
# Raised when an unknown DNS resource record is given.
209
class UnknownDnsResourceError < StandardError
210
DEFAULT_MESSAGE = 'Failed to add DNS rule with unknown resource record'
211
212
def initialize(message = nil)
213
super(message || DEFAULT_MESSAGE)
214
end
215
end
216
end
217
end
218
end
219
end
220
end
221
222