Create basic packet manipulation modules

Create Rex::Proto::DNS::Packet and Packet::Raw to allow common
parsing, validation, and raw data operations across both Rex and
Msf namespaces.

The modules contain class methods and do not need to be mixed in
to use their functionality Packet.method is enough, and reduces GC
strain since new objects are not constantly being instantiated, and
these modules contain no internal state.

Clean up UDP socket leak from Rex::Proto::DNS::Server under certain
conditions.

Create Msf::Exploit::DNS::Common mixin to provide descendants with
access to Packet and the hostname Regex.

-----

Testing:
  Tested running the RC provided in the pull request
  Manual testing in IRB/Pry while porting PoC for CVE-2015-7547
This commit is contained in:
RageLtMan
2016-02-29 02:52:41 -05:00
parent 2679c26e88
commit 2347c8df99
8 changed files with 178 additions and 18 deletions
+1
View File
@@ -15,5 +15,6 @@ module Exploit::Remote::DNS
end
end
require 'msf/core/exploit/dns/common'
require 'msf/core/exploit/dns/client'
require 'msf/core/exploit/dns/server'
+1
View File
@@ -13,6 +13,7 @@ module Msf
module Exploit::Remote::DNS
module Client
include Common
include Exploit::Remote::Udp
include Exploit::Remote::Tcp
+22
View File
@@ -0,0 +1,22 @@
# -*- coding: binary -*-
require 'msf/core'
require 'rex/proto/dns'
module Msf
###
#
# This module exposes methods for querying a remote DNS service
#
###
module Exploit::Remote::DNS
module Common
MATCH_HOSTNAME = Rex::Proto::DNS::Constants::MATCH_HOSTNAME
Packet = Rex::Proto::DNS::Packet
end
end
end
+5 -3
View File
@@ -12,10 +12,9 @@ module Msf
###
module Exploit::Remote::DNS
module Server
include Common
include Exploit::Remote::SocketServer
MATCH_HOSTNAME = Rex::Proto::DNS::Constants::MATCH_HOSTNAME
#
# Initializes an exploit module that serves DNS requests
#
@@ -156,7 +155,10 @@ module Server
# @param destroy [TrueClass,FalseClass] Dereference the server object
def stop_service(destroy = false)
self.service.stop unless self.service.nil?
self.service = nil if destroy
if destroy
@dns_resolver = nil
self.service = nil
end
end
#
+1
View File
@@ -12,5 +12,6 @@ end
end
end
require 'rex/proto/dns/packet'
require 'rex/proto/dns/resolver'
require 'rex/proto/dns/server'
+136
View File
@@ -0,0 +1,136 @@
require 'net/dns'
require 'resolv'
module Rex
module Proto
module DNS
module Packet
#
# Reconstructs a packet with both standard DNS libraries
# Ensures that headers match the payload
#
# @param packet [String, Net::DNS::Packet] Data to be validated
#
# @return [Net::DNS::Packet]
def self.validate(packet)
Net::DNS::Packet.parse(
Resolv::DNS::Message.decode(
packet.respond_to?(:data) ? packet.data : packet
).encode
)
end
#
# Reads a packet into the Net::DNS::Packet format
#
# @param data [String, Net::DNS::Packet, Resolv::DNS::Message] Input data
#
# @return [Net::DNS::Packet]
def self.encode_net(packet)
return packet if packet.respond_to?(:data)
Net::DNS::Packet.parse(
packet.respond_to?(:decode) ? packet.encode : packet
)
end
# Reads a packet into the Resolv::DNS::Message format
#
# @param data [String, Net::DNS::Packet, Resolv::DNS::Message] Input data
#
# @return [Resolv::DNS::Message]
def self.encode_res(packet)
return packet if packet.respond_to?(:decode)
Resolv::DNS::Message.decode(
packet.respond_to?(:data) ? packet.data : packet
)
end
# Reads a packet into the raw String format
#
# @param data [String, Net::DNS::Packet, Resolv::DNS::Message] Input data
#
# @return [Resolv::DNS::Message]
def self.encode_raw(packet)
return packet unless packet.respond_to?(:decode) or packet.respond_to?(:data)
packet.respond_to?(:data) ? packet.data : packet.encode
end
module Raw
#
# Convert data to big endian unsigned short
#
# @param data [Fixnum, Float, Array] Input for conversion
#
# @return [String] Raw output
def self.to_dw(data)
[data].flatten.pack('S>*')
end
#
# Convert data from big endian unsigned short
#
# @param data [String] Input for conversion
#
# @return [Array] Integer array output
def self.from_dw(data)
data.unpack('S>*')
end
#
# Convert data to big endian unsigned int
#
# @param data [Fixnum, Float, Array] Input for conversion
#
# @return [String] Raw output
def self.to_dd(data)
[data].flatten.pack('I>*')
end
#
# Convert data from big endian unsigned int
#
# @param data [String] Input for conversion
#
# @return [Array] Integer array output
def self.from_dd(data)
data.unpack('I>*')
end
#
# Convert data to big endian unsigned long
#
# @param data [Fixnum, Float, Array] Input for conversion
#
# @return [String] Raw output
def self.to_dl(data)
[data].flatten.pack('L>*')
end
#
# Convert data from big endian unsigned long
#
# @param data [String] Input for conversion
#
# @return [Array] Integer array output
def self.from_dl(data)
data.unpack('L>*')
end
#
# Returns request ID from raw packet skipping parsing
#
# @param data [String] Request data
#
# @return [Fixnum] Request ID
def self.request_id(data)
self.from_dw(data[0..1])[0]
end
end
end
end
end
end
+8 -10
View File
@@ -213,18 +213,16 @@ class Server
#
# @param flush_cache [TrueClass,FalseClass] Flush eDNS cache on stop
def stop(flush_cache = false)
if self.udp_sock
self.listener_thread.kill
ensure_close = [self.udp_sock, self.tcp_sock].compact
begin
self.listener_thread.kill if self.listener_thread.respond_to?(:kill)
self.listener_thread = nil
@udp_sock = nil
ensure
while csock = ensure_close.shift
csock.stop if csock.respond_to?(:stop)
csock.close unless csock.closed?
end
end
if self.tcp_sock
self.tcp_sock.stop if self.tcp_sock.respond_to?(:stop)
self.tcp_sock.close if self.tcp_sock.respond_to?(:close)
@tcp_sock = nil
end
self.cache.stop(flush_cache)
end
@@ -35,7 +35,6 @@ class Metasploit3 < Msf::Auxiliary
def run
begin
start_service
primer
service.wait
ensure
stop_service(true)
@@ -46,7 +45,7 @@ class Metasploit3 < Msf::Auxiliary
# Creates Proc to handle incoming requests
#
def on_dispatch_request(cli,data)
req = Net::DNS::Packet.parse(data)
req = Packet.encode_net(data)
peer = "#{cli.peerhost}:#{cli.peerport}"
asked = req.question.map(&:qName).join(', ')
vprint_status("Received request for #{asked} from #{peer}")
@@ -66,7 +65,7 @@ class Metasploit3 < Msf::Auxiliary
end unless service.cache.nil?
# Forward remaining requests, cache responses
if forward.question.count > 0 and service.fwd_res
forwarded = service.fwd_res.send(service.validate_packet(forward))
forwarded = service.fwd_res.send(Packet.validate(forward))
forwarded.answer.each do |ans|
vprint_status("Caching response #{ans.name}:#{ans.address} #{ans.type}")
service.cache.cache_record(ans)
@@ -76,14 +75,14 @@ class Metasploit3 < Msf::Auxiliary
req = forwarded
end
req.header.qr = 1 # Set response bit
service.send_response(cli, service.validate_packet(req).data)
service.send_response(cli, Packet.validate(req).data)
end
#
# Creates Proc to handle outbound responses
#
def on_send_response(cli,data)
res = Net::DNS::Packet.parse(data)
res = Packet.encode_net(data)
peer = "#{cli.peerhost}:#{cli.peerport}"
asked = res.question.map(&:qName).join(', ')
vprint_status("Sending response for #{asked} to #{peer}")