Class: Rex::Proto::DHCP::Server
- Inherits:
-
Object
- Object
- Rex::Proto::DHCP::Server
- Includes:
- Socket
- Defined in:
- lib/rex/proto/dhcp/server.rb
Overview
DHCP Server class not completely configurable - written specifically for a PXE server
-
scriptjunkie
extended to support testing/exploiting CVE-2011-0997
-
apconole@yahoo.com
Instance Attribute Summary collapse
-
#broadcasta ⇒ Object
Returns the value of attribute broadcasta.
-
#context ⇒ Object
Returns the value of attribute context.
-
#current_ip ⇒ Object
Returns the value of attribute current_ip.
-
#dnsserv ⇒ Object
Returns the value of attribute dnsserv.
-
#domain_name ⇒ Object
Returns the value of attribute domain_name.
-
#end_ip ⇒ Object
Returns the value of attribute end_ip.
-
#give_hostname ⇒ Object
Returns the value of attribute give_hostname.
-
#ipstring ⇒ Object
Returns the value of attribute ipstring.
-
#leasetime ⇒ Object
Returns the value of attribute leasetime.
-
#listen_host ⇒ Object
Returns the value of attribute listen_host.
-
#listen_port ⇒ Object
Returns the value of attribute listen_port.
-
#myfilename ⇒ Object
Returns the value of attribute myfilename.
-
#netmaskn ⇒ Object
Returns the value of attribute netmaskn.
-
#proxy_auto_discovery ⇒ Object
Returns the value of attribute proxy_auto_discovery.
-
#pxealtconfigfile ⇒ Object
Returns the value of attribute pxealtconfigfile.
-
#pxeconfigfile ⇒ Object
Returns the value of attribute pxeconfigfile.
-
#pxepathprefix ⇒ Object
Returns the value of attribute pxepathprefix.
-
#pxereboottime ⇒ Object
Returns the value of attribute pxereboottime.
-
#relayip ⇒ Object
Returns the value of attribute relayip.
-
#reporter ⇒ Object
Returns the value of attribute reporter.
-
#router ⇒ Object
Returns the value of attribute router.
-
#served ⇒ Object
Returns the value of attribute served.
-
#served_hostname ⇒ Object
Returns the value of attribute served_hostname.
-
#served_over ⇒ Object
Returns the value of attribute served_over.
-
#serveOnce ⇒ Object
Returns the value of attribute serveOnce.
-
#serveOnlyPXE ⇒ Object
Returns the value of attribute serveOnlyPXE.
-
#servePXE ⇒ Object
Returns the value of attribute servePXE.
-
#sock ⇒ Object
Returns the value of attribute sock.
-
#start_ip ⇒ Object
Returns the value of attribute start_ip.
-
#thread ⇒ Object
Returns the value of attribute thread.
-
#url ⇒ Object
Returns the value of attribute url.
Instance Method Summary collapse
- #dhcpoption(type, val = nil) ⇒ Object protected
-
#dispatch_request(from, buf) ⇒ Object
protected
Dispatch a packet that we received.
-
#initialize(hash, context = {}) ⇒ Server
constructor
A new instance of Server.
-
#monitor_socket ⇒ Object
protected
See if there is anything to do..
- #report(&block) ⇒ Object
-
#send_packet(ip, pkt) ⇒ Object
Send a single packet to the specified host.
-
#set_option(opts) ⇒ Object
Set an option.
-
#start ⇒ Object
Start the DHCP server.
-
#stop ⇒ Object
Stop the DHCP server.
Constructor Details
#initialize(hash, context = {}) ⇒ Server
Returns a new instance of Server.
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/rex/proto/dhcp/server.rb', line 23 def initialize(hash, context = {}) self.listen_host = '0.0.0.0' # clients don't already have addresses. Needs to be 0.0.0.0 self.listen_port = 67 # mandatory (bootps) self.context = context self.sock = nil self.myfilename = hash['FILENAME'] || "" self.myfilename << ("\x00" * (128 - self.myfilename.length)) source = hash['SRVHOST'] || Rex::Socket.source_address self.ipstring = Rex::Socket.addr_aton(source) ipstart = hash['DHCPIPSTART'] if ipstart self.start_ip = Rex::Socket.addr_atoi(ipstart) else # Use the first 3 octets of the server's IP to construct the # default range of x.x.x.32-254 self.start_ip = "#{self.ipstring[0..2]}\x20".unpack("N").first end self.current_ip = start_ip ipend = hash['DHCPIPEND'] if ipend self.end_ip = Rex::Socket.addr_atoi(ipend) else # Use the first 3 octets of the server's IP to construct the # default range of x.x.x.32-254 self.end_ip = "#{self.ipstring[0..2]}\xfe".unpack("N").first end # netmask netmask = hash['NETMASK'] || "255.255.255.0" self.netmaskn = Rex::Socket.addr_aton(netmask) # router router = hash['ROUTER'] || source self.router = Rex::Socket.addr_aton(router) # dns dnsserv = hash['DNSSERVER'] || source self.dnsserv = Rex::Socket.addr_aton(dnsserv) # broadcast if hash['BROADCAST'] self.broadcasta = Rex::Socket.addr_aton(hash['BROADCAST']) else self.broadcasta = Rex::Socket.addr_itoa( self.start_ip | (Rex::Socket.addr_ntoi(self.netmaskn) ^ 0xffffffff) ) end self.served = {} self.serveOnce = hash.include?('SERVEONCE') self.servePXE = (hash.include?('PXE') or hash.include?('FILENAME') or hash.include?('PXEONLY')) self.serveOnlyPXE = hash.include?('PXEONLY') # Always assume we don't give out hostnames ... self.give_hostname = false self.served_over = 0 if (hash['HOSTNAME']) self.give_hostname = true self.served_hostname = hash['HOSTNAME'] if ( hash['HOSTSTART'] ) self.served_over = hash['HOSTSTART'].to_i end end self.leasetime = 600 self.relayip = "\x00\x00\x00\x00" # relay ip - not currently supported self.pxeconfigfile = "update2" self.pxealtconfigfile = "update0" self.pxepathprefix = "" self.pxereboottime = 2000 self.domain_name = hash['DOMAINNAME'] || nil self.url = hash['URL'] if hash.include?('URL') end |
Instance Attribute Details
#broadcasta ⇒ Object
Returns the value of attribute broadcasta.
158 159 160 |
# File 'lib/rex/proto/dhcp/server.rb', line 158 def broadcasta @broadcasta end |
#context ⇒ Object
Returns the value of attribute context.
155 156 157 |
# File 'lib/rex/proto/dhcp/server.rb', line 155 def context @context end |
#current_ip ⇒ Object
Returns the value of attribute current_ip.
158 159 160 |
# File 'lib/rex/proto/dhcp/server.rb', line 158 def current_ip @current_ip end |
#dnsserv ⇒ Object
Returns the value of attribute dnsserv.
155 156 157 |
# File 'lib/rex/proto/dhcp/server.rb', line 155 def dnsserv @dnsserv end |
#domain_name ⇒ Object
Returns the value of attribute domain_name.
156 157 158 |
# File 'lib/rex/proto/dhcp/server.rb', line 156 def domain_name @domain_name end |
#end_ip ⇒ Object
Returns the value of attribute end_ip.
158 159 160 |
# File 'lib/rex/proto/dhcp/server.rb', line 158 def end_ip @end_ip end |
#give_hostname ⇒ Object
Returns the value of attribute give_hostname.
160 161 162 |
# File 'lib/rex/proto/dhcp/server.rb', line 160 def give_hostname @give_hostname end |
#ipstring ⇒ Object
Returns the value of attribute ipstring.
157 158 159 |
# File 'lib/rex/proto/dhcp/server.rb', line 157 def ipstring @ipstring end |
#leasetime ⇒ Object
Returns the value of attribute leasetime.
155 156 157 |
# File 'lib/rex/proto/dhcp/server.rb', line 155 def leasetime @leasetime end |
#listen_host ⇒ Object
Returns the value of attribute listen_host.
155 156 157 |
# File 'lib/rex/proto/dhcp/server.rb', line 155 def listen_host @listen_host end |
#listen_port ⇒ Object
Returns the value of attribute listen_port.
155 156 157 |
# File 'lib/rex/proto/dhcp/server.rb', line 155 def listen_port @listen_port end |
#myfilename ⇒ Object
Returns the value of attribute myfilename.
157 158 159 |
# File 'lib/rex/proto/dhcp/server.rb', line 157 def myfilename @myfilename end |
#netmaskn ⇒ Object
Returns the value of attribute netmaskn.
158 159 160 |
# File 'lib/rex/proto/dhcp/server.rb', line 158 def netmaskn @netmaskn end |
#proxy_auto_discovery ⇒ Object
Returns the value of attribute proxy_auto_discovery.
156 157 158 |
# File 'lib/rex/proto/dhcp/server.rb', line 156 def proxy_auto_discovery @proxy_auto_discovery end |
#pxealtconfigfile ⇒ Object
Returns the value of attribute pxealtconfigfile.
159 160 161 |
# File 'lib/rex/proto/dhcp/server.rb', line 159 def pxealtconfigfile @pxealtconfigfile end |
#pxeconfigfile ⇒ Object
Returns the value of attribute pxeconfigfile.
159 160 161 |
# File 'lib/rex/proto/dhcp/server.rb', line 159 def pxeconfigfile @pxeconfigfile end |
#pxepathprefix ⇒ Object
Returns the value of attribute pxepathprefix.
159 160 161 |
# File 'lib/rex/proto/dhcp/server.rb', line 159 def pxepathprefix @pxepathprefix end |
#pxereboottime ⇒ Object
Returns the value of attribute pxereboottime.
159 160 161 |
# File 'lib/rex/proto/dhcp/server.rb', line 159 def pxereboottime @pxereboottime end |
#relayip ⇒ Object
Returns the value of attribute relayip.
155 156 157 |
# File 'lib/rex/proto/dhcp/server.rb', line 155 def relayip @relayip end |
#reporter ⇒ Object
Returns the value of attribute reporter.
160 161 162 |
# File 'lib/rex/proto/dhcp/server.rb', line 160 def reporter @reporter end |
#router ⇒ Object
Returns the value of attribute router.
155 156 157 |
# File 'lib/rex/proto/dhcp/server.rb', line 155 def router @router end |
#served ⇒ Object
Returns the value of attribute served.
157 158 159 |
# File 'lib/rex/proto/dhcp/server.rb', line 157 def served @served end |
#served_hostname ⇒ Object
Returns the value of attribute served_hostname.
160 161 162 |
# File 'lib/rex/proto/dhcp/server.rb', line 160 def served_hostname @served_hostname end |
#served_over ⇒ Object
Returns the value of attribute served_over.
160 161 162 |
# File 'lib/rex/proto/dhcp/server.rb', line 160 def served_over @served_over end |
#serveOnce ⇒ Object
Returns the value of attribute serveOnce.
157 158 159 |
# File 'lib/rex/proto/dhcp/server.rb', line 157 def serveOnce @serveOnce end |
#serveOnlyPXE ⇒ Object
Returns the value of attribute serveOnlyPXE.
159 160 161 |
# File 'lib/rex/proto/dhcp/server.rb', line 159 def serveOnlyPXE @serveOnlyPXE end |
#servePXE ⇒ Object
Returns the value of attribute servePXE.
159 160 161 |
# File 'lib/rex/proto/dhcp/server.rb', line 159 def servePXE @servePXE end |
#sock ⇒ Object
Returns the value of attribute sock.
157 158 159 |
# File 'lib/rex/proto/dhcp/server.rb', line 157 def sock @sock end |
#start_ip ⇒ Object
Returns the value of attribute start_ip.
158 159 160 |
# File 'lib/rex/proto/dhcp/server.rb', line 158 def start_ip @start_ip end |
#thread ⇒ Object
Returns the value of attribute thread.
157 158 159 |
# File 'lib/rex/proto/dhcp/server.rb', line 157 def thread @thread end |
#url ⇒ Object
Returns the value of attribute url.
160 161 162 |
# File 'lib/rex/proto/dhcp/server.rb', line 160 def url @url end |
Instance Method Details
#dhcpoption(type, val = nil) ⇒ Object (protected)
184 185 186 187 188 189 190 191 192 193 |
# File 'lib/rex/proto/dhcp/server.rb', line 184 def dhcpoption(type, val = nil) ret = '' ret << [type].pack('C') if val ret << [val.length].pack('C') + val end ret end |
#dispatch_request(from, buf) ⇒ Object (protected)
Dispatch a packet that we received
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 |
# File 'lib/rex/proto/dhcp/server.rb', line 196 def dispatch_request(from, buf) type = buf.unpack('C').first if (type != Constants::Request) #dlog("Unknown DHCP request type: #{type}") return end # parse out the members _hwtype = buf[1,1] hwlen = buf[2,1].unpack("C").first _hops = buf[3,1] _txid = buf[4..7] _elapsed = buf[8..9] _flags = buf[10..11] clientip = buf[12..15] _givenip = buf[16..19] _nextip = buf[20..23] _relayip = buf[24..27] _clienthwaddr = buf[28..(27+hwlen)] servhostname = buf[44..107] _filename = buf[108..235] magic = buf[236..239] if (magic != Constants::DHCPMagic) #dlog("Invalid DHCP request - bad magic.") return end = 0 pxeclient = false # options parsing loop spot = 240 while (spot < buf.length - 3) optionType = buf[spot,1].unpack("C").first break if optionType == 0xff optionLen = buf[spot + 1,1].unpack("C").first optionValue = buf[(spot + 2)..(spot + optionLen + 1)] spot = spot + optionLen + 2 if optionType == 53 = optionValue.unpack("C").first elsif optionType == 150 or (optionType == 60 and optionValue.include? "PXEClient") pxeclient = true end end # don't serve if only serving PXE and not PXE request return if pxeclient == false and self.serveOnlyPXE == true # prepare response pkt = [Constants::Response].pack('C') pkt << buf[1..7] #hwtype, hwlen, hops, txid pkt << "\x00\x00\x00\x00" #elapsed, flags pkt << clientip # if this is somebody we've seen before, use the saved IP if self.served.include?( buf[28..43] ) pkt << Rex::Socket.addr_iton(self.served[buf[28..43]][0]) else # otherwise go to next ip address self.current_ip += 1 if self.current_ip > self.end_ip self.current_ip = self.start_ip end self.served.merge!( buf[28..43] => [ self.current_ip, == Constants::DHCPRequest ] ) pkt << Rex::Socket.addr_iton(self.current_ip) end pkt << self.ipstring #next server ip pkt << self.relayip pkt << buf[28..43] #client hw address pkt << servhostname pkt << self.myfilename pkt << magic pkt << "\x35\x01" #Option if == Constants::DHCPDiscover #DHCP Discover - send DHCP Offer pkt << [Constants::DHCPOffer].pack('C') # check if already served an Ack based on hw addr (MAC address) # if serveOnce & PXE, don't reply to another PXE request # if serveOnce & ! PXE, don't reply to anything if self.serveOnce == true and self.served.has_key?(buf[28..43]) and self.served[buf[28..43]][1] and (pxeclient == false or self.servePXE == false) return end elsif == Constants::DHCPRequest #DHCP Request - send DHCP ACK pkt << [Constants::DHCPAck].pack('C') # now we ignore their discovers (but we'll respond to requests in case a packet was lost) if ( self.served_over != 0 ) # NOTE: this is sufficient for low-traffic net # for high-traffic, this will probably lead to # hostname collision self.served_over += 1 end else return # ignore unknown DHCP request end # Options! pkt << dhcpoption(Constants::OpProxyAutodiscovery, self.proxy_auto_discovery) if self.proxy_auto_discovery pkt << dhcpoption(Constants::OpDHCPServer, self.ipstring) pkt << dhcpoption(Constants::OpLeaseTime, [self.leasetime].pack('N')) pkt << dhcpoption(Constants::OpSubnetMask, self.netmaskn) pkt << dhcpoption(Constants::OpRouter, self.router) pkt << dhcpoption(Constants::OpDns, self.dnsserv) pkt << dhcpoption(Constants::OpDomainName, self.domain_name) if self.domain_name if self.servePXE # PXE options pkt << dhcpoption(Constants::OpPXEMagic, Constants::PXEMagic) # We already got this one, serve localboot file if self.serveOnce == true and self.served.has_key?(buf[28..43]) and self.served[buf[28..43]][1] and pxeclient == true pkt << dhcpoption(Constants::OpPXEConfigFile, self.pxealtconfigfile) else # We are handing out an IP and our PXE attack if(self.reporter) self.reporter.call(buf[28..43],self.ipstring) end pkt << dhcpoption(Constants::OpPXEConfigFile, self.pxeconfigfile) end pkt << dhcpoption(Constants::OpPXEPathPrefix, self.pxepathprefix) pkt << dhcpoption(Constants::OpPXERebootTime, [self.pxereboottime].pack('N')) if ( self.give_hostname == true ) send_hostname = self.served_hostname if ( self.served_over != 0 ) # NOTE : see above comments for the 'uniqueness' of this value send_hostname += self.served_over.to_s end pkt << dhcpoption(Constants::OpHostname, send_hostname) end end pkt << dhcpoption(Constants::OpURL, self.url) if self.url pkt << dhcpoption(Constants::OpEnd) pkt << ("\x00" * 32) #padding # And now we mark as requested self.served[buf[28..43]][1] = true if == Constants::DHCPRequest send_packet(nil, pkt) end |
#monitor_socket ⇒ Object (protected)
See if there is anything to do.. If so, dispatch it.
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/rex/proto/dhcp/server.rb', line 166 def monitor_socket while true rds = [@sock] wds = [] eds = [@sock] r,_,_ = ::IO.select(rds,wds,eds,1) if (r != nil and r[0] == self.sock) buf,host,port = self.sock.recvfrom(65535) # Lame compatabilitiy :-/ from = [host, port] dispatch_request(from, buf) end end end |
#report(&block) ⇒ Object
101 102 103 |
# File 'lib/rex/proto/dhcp/server.rb', line 101 def report(&block) self.reporter = block end |
#send_packet(ip, pkt) ⇒ Object
Send a single packet to the specified host
144 145 146 147 148 149 150 151 152 153 |
# File 'lib/rex/proto/dhcp/server.rb', line 144 def send_packet(ip, pkt) port = 68 # bootpc if ip self.sock.sendto( pkt, ip, port ) else if not self.sock.sendto( pkt, '255.255.255.255', port ) self.sock.sendto( pkt, self.broadcasta, port ) end end end |
#set_option(opts) ⇒ Object
Set an option
127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/rex/proto/dhcp/server.rb', line 127 def set_option(opts) = [ :serveOnce, :pxealtconfigfile, :servePXE, :relayip, :leasetime, :dnsserv, :pxeconfigfile, :pxepathprefix, :pxereboottime, :router, :proxy_auto_discovery, :give_hostname, :served_hostname, :served_over, :serveOnlyPXE, :domain_name, :url ] opts.each_pair { |k,v| next if not v if .include?(k) self.instance_variable_set("@#{k}", v) end } end |
#start ⇒ Object
Start the DHCP server
106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/rex/proto/dhcp/server.rb', line 106 def start self.sock = Rex::Socket::Udp.create( 'LocalHost' => listen_host, 'LocalPort' => listen_port, 'Context' => context ) self.thread = Rex::ThreadFactory.spawn("DHCPServerMonitor", false) { monitor_socket } end |
#stop ⇒ Object
Stop the DHCP server
119 120 121 122 123 |
# File 'lib/rex/proto/dhcp/server.rb', line 119 def stop self.thread.kill self.served = {} self.sock.close rescue nil end |