Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
beefproject
GitHub Repository: beefproject/beef
Path: blob/master/extensions/s2c_dns_tunnel/dnsd.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 Extension
8
module ServerClientDnsTunnel
9
module RubyDNS
10
class Transaction
11
def fail!(rcode, domain)
12
append_question!
13
14
@answer.rcode = if rcode.is_a? Symbol
15
Resolv::DNS::RCode.const_get(rcode)
16
else
17
rcode.to_i
18
end
19
20
return unless rcode == :NXDomain
21
22
@answer.aa = 1
23
soa = Resolv::DNS::Resource::IN::SOA.new(Resolv::DNS::Name.create("ns.#{domain}"),
24
Resolv::DNS::Name.create("hostmaster.#{domain}"),
25
Time.now.strftime('%Y%m%d%H').to_i, 86_400, 7200, 3_600_000, 172_800)
26
@answer.add_authority(name, 3600, soa)
27
end
28
end
29
end
30
31
class Server < RubyDNS::Server
32
include Singleton
33
34
attr_accessor :messages
35
36
def initialize
37
super()
38
@lock = Mutex.new
39
end
40
41
# Starts the custom DNS server.
42
#
43
# @param options [Hash] server configuration options
44
# @option options [Array<Array>] :zone - zone manged by BeEF DNS server for data exfiltration
45
# @option options [Array<Array>] :listen - local interfaces to listen on
46
def run(options = {})
47
@lock.synchronize do
48
Thread.new do
49
EventMachine.next_tick do
50
listen = options[:listen] || nil
51
super(listen: listen)
52
53
@selfip = options[:listen][0][1]
54
@zone = options[:zone]
55
@messages = {}
56
end
57
end
58
end
59
end
60
61
# Entry point for processing incoming DNS requests.
62
#
63
# @param name [String] name of the resource record being looked up
64
# @param resource [Resolv::DNS::Resource::IN] query type (e.g. A, CNAME, NS, etc.)
65
# @param transaction [RubyDNS::Transaction] internal RubyDNS class detailing DNS question/answer
66
def process(name, resource, transaction)
67
@lock.synchronize do
68
print_debug "Received DNS request (name: #{name} type: #{format_resource(resource)})"
69
if (format_resource(resource) != 'A') || !name.match(/#{@zone}$/)
70
transaction.fail!(:Refused, @zone)
71
return
72
end
73
74
# Parce query name in accordance with Active Directory SRV resource records
75
cid = name.split('.')[2].split('-')[2].to_i
76
bit = name.split('.')[2].split('-')[3].to_i(16)
77
78
if @messages[cid].nil?
79
transaction.fail!(:NXDomain, @zone)
80
return
81
else
82
message = @messages[cid]
83
end
84
85
if message.length <= bit
86
transaction.fail!(:NXDomain, @zone)
87
return
88
end
89
90
# If the bit is equal to 1 we should return one of the BeEF's IP addresses
91
case message[bit]
92
when '1'
93
transaction.respond!(@selfip)
94
return
95
# If the bit is equal to 0 we should return NXDomain message
96
when '0'
97
transaction.fail!(:NXDomain, @zone)
98
return
99
end
100
end
101
end
102
103
private
104
105
# Helper method that formats the given resource class in a human-readable format.
106
#
107
# @param resource [Resolv::DNS::Resource::IN] resource class
108
# @return [String] resource name stripped of any module/class names
109
def format_resource(resource)
110
/::(\w+)$/.match(resource.name)[1]
111
end
112
end
113
end
114
end
115
end
116
117