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.
-
#interface ⇒ Object
Returns the value of attribute interface.
-
#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 100 101 |
# 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.interface = hash['DHCPINTERFACE'] || nil 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.
165 166 167 |
# File 'lib/rex/proto/dhcp/server.rb', line 165 def broadcasta @broadcasta end |
#context ⇒ Object
Returns the value of attribute context.
162 163 164 |
# File 'lib/rex/proto/dhcp/server.rb', line 162 def context @context end |
#current_ip ⇒ Object
Returns the value of attribute current_ip.
165 166 167 |
# File 'lib/rex/proto/dhcp/server.rb', line 165 def current_ip @current_ip end |
#dnsserv ⇒ Object
Returns the value of attribute dnsserv.
162 163 164 |
# File 'lib/rex/proto/dhcp/server.rb', line 162 def dnsserv @dnsserv end |
#domain_name ⇒ Object
Returns the value of attribute domain_name.
163 164 165 |
# File 'lib/rex/proto/dhcp/server.rb', line 163 def domain_name @domain_name end |
#end_ip ⇒ Object
Returns the value of attribute end_ip.
165 166 167 |
# File 'lib/rex/proto/dhcp/server.rb', line 165 def end_ip @end_ip end |
#give_hostname ⇒ Object
Returns the value of attribute give_hostname.
167 168 169 |
# File 'lib/rex/proto/dhcp/server.rb', line 167 def give_hostname @give_hostname end |
#interface ⇒ Object
Returns the value of attribute interface.
163 164 165 |
# File 'lib/rex/proto/dhcp/server.rb', line 163 def interface @interface end |
#ipstring ⇒ Object
Returns the value of attribute ipstring.
164 165 166 |
# File 'lib/rex/proto/dhcp/server.rb', line 164 def ipstring @ipstring end |
#leasetime ⇒ Object
Returns the value of attribute leasetime.
162 163 164 |
# File 'lib/rex/proto/dhcp/server.rb', line 162 def leasetime @leasetime end |
#listen_host ⇒ Object
Returns the value of attribute listen_host.
162 163 164 |
# File 'lib/rex/proto/dhcp/server.rb', line 162 def listen_host @listen_host end |
#listen_port ⇒ Object
Returns the value of attribute listen_port.
162 163 164 |
# File 'lib/rex/proto/dhcp/server.rb', line 162 def listen_port @listen_port end |
#myfilename ⇒ Object
Returns the value of attribute myfilename.
164 165 166 |
# File 'lib/rex/proto/dhcp/server.rb', line 164 def myfilename @myfilename end |
#netmaskn ⇒ Object
Returns the value of attribute netmaskn.
165 166 167 |
# File 'lib/rex/proto/dhcp/server.rb', line 165 def netmaskn @netmaskn end |
#proxy_auto_discovery ⇒ Object
Returns the value of attribute proxy_auto_discovery.
163 164 165 |
# File 'lib/rex/proto/dhcp/server.rb', line 163 def proxy_auto_discovery @proxy_auto_discovery end |
#pxealtconfigfile ⇒ Object
Returns the value of attribute pxealtconfigfile.
166 167 168 |
# File 'lib/rex/proto/dhcp/server.rb', line 166 def pxealtconfigfile @pxealtconfigfile end |
#pxeconfigfile ⇒ Object
Returns the value of attribute pxeconfigfile.
166 167 168 |
# File 'lib/rex/proto/dhcp/server.rb', line 166 def pxeconfigfile @pxeconfigfile end |
#pxepathprefix ⇒ Object
Returns the value of attribute pxepathprefix.
166 167 168 |
# File 'lib/rex/proto/dhcp/server.rb', line 166 def pxepathprefix @pxepathprefix end |
#pxereboottime ⇒ Object
Returns the value of attribute pxereboottime.
166 167 168 |
# File 'lib/rex/proto/dhcp/server.rb', line 166 def pxereboottime @pxereboottime end |
#relayip ⇒ Object
Returns the value of attribute relayip.
162 163 164 |
# File 'lib/rex/proto/dhcp/server.rb', line 162 def relayip @relayip end |
#reporter ⇒ Object
Returns the value of attribute reporter.
167 168 169 |
# File 'lib/rex/proto/dhcp/server.rb', line 167 def reporter @reporter end |
#router ⇒ Object
Returns the value of attribute router.
162 163 164 |
# File 'lib/rex/proto/dhcp/server.rb', line 162 def router @router end |
#served ⇒ Object
Returns the value of attribute served.
164 165 166 |
# File 'lib/rex/proto/dhcp/server.rb', line 164 def served @served end |
#served_hostname ⇒ Object
Returns the value of attribute served_hostname.
167 168 169 |
# File 'lib/rex/proto/dhcp/server.rb', line 167 def served_hostname @served_hostname end |
#served_over ⇒ Object
Returns the value of attribute served_over.
167 168 169 |
# File 'lib/rex/proto/dhcp/server.rb', line 167 def served_over @served_over end |
#serveOnce ⇒ Object
Returns the value of attribute serveOnce.
164 165 166 |
# File 'lib/rex/proto/dhcp/server.rb', line 164 def serveOnce @serveOnce end |
#serveOnlyPXE ⇒ Object
Returns the value of attribute serveOnlyPXE.
166 167 168 |
# File 'lib/rex/proto/dhcp/server.rb', line 166 def serveOnlyPXE @serveOnlyPXE end |
#servePXE ⇒ Object
Returns the value of attribute servePXE.
166 167 168 |
# File 'lib/rex/proto/dhcp/server.rb', line 166 def servePXE @servePXE end |
#sock ⇒ Object
Returns the value of attribute sock.
164 165 166 |
# File 'lib/rex/proto/dhcp/server.rb', line 164 def sock @sock end |
#start_ip ⇒ Object
Returns the value of attribute start_ip.
165 166 167 |
# File 'lib/rex/proto/dhcp/server.rb', line 165 def start_ip @start_ip end |
#thread ⇒ Object
Returns the value of attribute thread.
164 165 166 |
# File 'lib/rex/proto/dhcp/server.rb', line 164 def thread @thread end |
#url ⇒ Object
Returns the value of attribute url.
167 168 169 |
# File 'lib/rex/proto/dhcp/server.rb', line 167 def url @url end |
Instance Method Details
#dhcpoption(type, val = nil) ⇒ Object (protected)
191 192 193 194 195 196 197 198 199 200 |
# File 'lib/rex/proto/dhcp/server.rb', line 191 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
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 336 337 338 339 340 341 342 |
# File 'lib/rex/proto/dhcp/server.rb', line 203 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.
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/rex/proto/dhcp/server.rb', line 173 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
103 104 105 |
# File 'lib/rex/proto/dhcp/server.rb', line 103 def report(&block) self.reporter = block end |
#send_packet(ip, pkt) ⇒ Object
Send a single packet to the specified host
151 152 153 154 155 156 157 158 159 160 |
# File 'lib/rex/proto/dhcp/server.rb', line 151 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
134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/rex/proto/dhcp/server.rb', line 134 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
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/rex/proto/dhcp/server.rb', line 108 def start self.sock = Rex::Socket::Udp.create( 'LocalHost' => listen_host, 'LocalPort' => listen_port, 'Context' => context ) # Dynamically bind to interface if provided if interface && !interface.empty? self.sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_BINDTODEVICE, "#{interface}\0") end self.thread = Rex::ThreadFactory.spawn("DHCPServerMonitor", false) { monitor_socket } end |
#stop ⇒ Object
Stop the DHCP server
126 127 128 129 130 |
# File 'lib/rex/proto/dhcp/server.rb', line 126 def stop self.thread.kill self.served = {} self.sock.close rescue nil end |