Class: Rex::Proto::DHCPv6::Packet
- Inherits:
-
BinData::Record
- Object
- BinData::Record
- Rex::Proto::DHCPv6::Packet
- Defined in:
- lib/rex/proto/dhcpv6/packet.rb
Overview
A DHCPv6 client/server message (RFC 8415 section 8): a one-byte message type, a three-byte transaction id, and a list of TLV options. This parses and builds the wire format and provides helpers for assembling the rogue server responses used to make a client adopt the attacker as its DNS server.
Relay messages (RELAY-FORW / RELAY-REPL) have a different layout and are not modelled here; only direct client/server messages are handled.
Defined Under Namespace
Classes: Dhcpv6Option
Class Method Summary collapse
-
.build_response(request:, server_duid:, dns_servers:, assigned_address: nil, preferred_lifetime: 300, valid_lifetime: 600, domain_list: nil) ⇒ Rex::Proto::DHCPv6::Packet?
Assemble a rogue server response that advertises the attacker as the client's DNS server.
-
.dns_servers_option(addresses) ⇒ Rex::Proto::DHCPv6::Packet::Dhcpv6Option
Build a DNS Recursive Name Server option (RFC 3646): the list of IPv6 addresses the client should use as DNS servers.
-
.domain_list_option(domains) ⇒ Rex::Proto::DHCPv6::Packet::Dhcpv6Option
Build a Domain Search List option (RFC 3646), DNS-name encoded.
-
.duid_ll(mac) ⇒ String
Build a DUID-LL (link-layer address DUID, RFC 8415 section 11.4).
-
.ia_na_option(iaid:, address:, preferred_lifetime:, valid_lifetime:, t1:, t2:) ⇒ Rex::Proto::DHCPv6::Packet::Dhcpv6Option
Build an IA_NA (Identity Association for Non-temporary Addresses) option carrying a single leased address, echoing the client's IAID.
-
.option(code, data) ⇒ Rex::Proto::DHCPv6::Packet::Dhcpv6Option
Build a DHCPv6 option.
-
.request_iaid(request) ⇒ Integer?
Read the IAID out of a request's IA_NA option, if any.
-
.response_type_for(request) ⇒ Integer?
The message type to answer a given request with (RFC 8415 section 18.3): a Solicit is answered with an Advertise, unless Rapid Commit is requested, in which case the exchange collapses to a Reply.
Instance Method Summary collapse
-
#find_option(code) ⇒ Rex::Proto::DHCPv6::Packet::Dhcpv6Option?
The first option with the given code, if present.
-
#rapid_commit? ⇒ Boolean
Whether the client asked for a Rapid Commit (a two-message Solicit/Reply exchange rather than the four-message default).
Class Method Details
.build_response(request:, server_duid:, dns_servers:, assigned_address: nil, preferred_lifetime: 300, valid_lifetime: 600, domain_list: nil) ⇒ Rex::Proto::DHCPv6::Packet?
Assemble a rogue server response that advertises the attacker as the client's DNS server. Echoes the client's transaction id and Client ID, and (for stateful requests carrying an IA_NA) leases the given address.
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/rex/proto/dhcpv6/packet.rb', line 140 def build_response(request:, server_duid:, dns_servers:, assigned_address: nil, preferred_lifetime: 300, valid_lifetime: 600, domain_list: nil) response_type = response_type_for(request) return nil if response_type.nil? opts = [ option(Constants::OptionCode::SERVERID, server_duid) ] client_id = request.find_option(Constants::OptionCode::CLIENTID) opts << option(Constants::OptionCode::CLIENTID, client_id.data.to_binary_s) unless client_id.nil? iaid = request_iaid(request) if !iaid.nil? && !assigned_address.nil? opts << ia_na_option( iaid: iaid, address: assigned_address, preferred_lifetime: preferred_lifetime, valid_lifetime: valid_lifetime, t1: preferred_lifetime / 2, t2: (preferred_lifetime * 4) / 5 ) end opts << dns_servers_option(dns_servers) opts << domain_list_option(domain_list) unless domain_list.nil? || domain_list.empty? opts << option(Constants::OptionCode::RAPID_COMMIT, '') if request.rapid_commit? new( msg_type: response_type, transaction_id: request.transaction_id.to_binary_s, options: opts.map { |o| { code: o.code, data: o.data.to_binary_s } } ) end |
.dns_servers_option(addresses) ⇒ Rex::Proto::DHCPv6::Packet::Dhcpv6Option
Build a DNS Recursive Name Server option (RFC 3646): the list of IPv6 addresses the client should use as DNS servers.
66 67 68 |
# File 'lib/rex/proto/dhcpv6/packet.rb', line 66 def dns_servers_option(addresses) option(Constants::OptionCode::DNS_SERVERS, addresses.map { |a| Rex::Socket.addr_aton(a) }.join) end |
.domain_list_option(domains) ⇒ Rex::Proto::DHCPv6::Packet::Dhcpv6Option
Build a Domain Search List option (RFC 3646), DNS-name encoded.
74 75 76 |
# File 'lib/rex/proto/dhcpv6/packet.rb', line 74 def domain_list_option(domains) option(Constants::OptionCode::DOMAIN_LIST, domains.map { |d| encode_dns_name(d) }.join) end |
.duid_ll(mac) ⇒ String
Build a DUID-LL (link-layer address DUID, RFC 8415 section 11.4).
57 58 59 |
# File 'lib/rex/proto/dhcpv6/packet.rb', line 57 def duid_ll(mac) [Constants::DuidType::LL, Constants::HARDWARE_TYPE_ETHERNET].pack('nn') + mac_to_bytes(mac) end |
.ia_na_option(iaid:, address:, preferred_lifetime:, valid_lifetime:, t1:, t2:) ⇒ Rex::Proto::DHCPv6::Packet::Dhcpv6Option
Build an IA_NA (Identity Association for Non-temporary Addresses) option carrying a single leased address, echoing the client's IAID.
88 89 90 91 92 93 94 |
# File 'lib/rex/proto/dhcpv6/packet.rb', line 88 def ia_na_option(iaid:, address:, preferred_lifetime:, valid_lifetime:, t1:, t2:) iaaddr = option( Constants::OptionCode::IAADDR, Rex::Socket.addr_aton(address) + [preferred_lifetime, valid_lifetime].pack('NN') ) option(Constants::OptionCode::IA_NA, [iaid, t1, t2].pack('NNN') + iaaddr.to_binary_s) end |
.option(code, data) ⇒ Rex::Proto::DHCPv6::Packet::Dhcpv6Option
Build a DHCPv6 option.
49 50 51 |
# File 'lib/rex/proto/dhcpv6/packet.rb', line 49 def option(code, data) Dhcpv6Option.new(code: code, data: data.b) end |
.request_iaid(request) ⇒ Integer?
Read the IAID out of a request's IA_NA option, if any.
100 101 102 103 104 105 |
# File 'lib/rex/proto/dhcpv6/packet.rb', line 100 def request_iaid(request) ia_na = request.find_option(Constants::OptionCode::IA_NA) return nil if ia_na.nil? ia_na.data.to_binary_s[0, 4].unpack1('N') end |
.response_type_for(request) ⇒ Integer?
The message type to answer a given request with (RFC 8415 section 18.3): a Solicit is answered with an Advertise, unless Rapid Commit is requested, in which case the exchange collapses to a Reply. Everything else we handle (Request/Renew/Rebind/Confirm/Information-Request) is answered with a Reply.
114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/rex/proto/dhcpv6/packet.rb', line 114 def response_type_for(request) case request.msg_type when Constants::MessageType::SOLICIT request.rapid_commit? ? Constants::MessageType::REPLY : Constants::MessageType::ADVERTISE when Constants::MessageType::REQUEST, Constants::MessageType::RENEW, Constants::MessageType::REBIND, Constants::MessageType::CONFIRM, Constants::MessageType::INFORMATION_REQUEST Constants::MessageType::REPLY end end |
Instance Method Details
#find_option(code) ⇒ Rex::Proto::DHCPv6::Packet::Dhcpv6Option?
Returns the first option with the given code, if present.
33 34 35 |
# File 'lib/rex/proto/dhcpv6/packet.rb', line 33 def find_option(code) .find { |opt| opt.code == code } end |
#rapid_commit? ⇒ Boolean
Returns whether the client asked for a Rapid Commit (a two-message Solicit/Reply exchange rather than the four-message default).
39 40 41 |
# File 'lib/rex/proto/dhcpv6/packet.rb', line 39 def rapid_commit? !find_option(Constants::OptionCode::RAPID_COMMIT).nil? end |