NX


NX, short for “No eXecute” is known by several names. Intel calls it the “Execute Disable” or XD-bit, AMD calls it “Enhanced Virus Protection,” and it’s also been written as W^X, which translates to write or execute, but not both. Windows refers to NX as “Data Execution Prevention,” and the settings can be found using the System Control Panel applet, under Advanced, Performance Options. Non-executable stacks and heaps aren’t especially new–Sun’s Solaris operating system had a setting to enable a non-executable stack several years ago, and Open-BSD moved to NX in OpenBSD v3.3 and later. In the Windows line of operating systems, NX has been available since Windows Server 2003 shipped and then was back ported to Windows XP in Service Pack 2. An indication of just how far back NX was anticipated is that one of the flags to the VirtualProtect function, as it originally was documented back in 1993 when Windows NT 3.1 shipped, was a flag to set whether a page was executable. Since the x86 family of processors didn’t support NX on a per-page basis until recently, the flag had no effect on x86 processors until some additional work was done to detect NX support at the processor level (David Cutler, personal communication). It didn’t have an effect on some of the other processors originally supported by Windows NT, but it is interesting to note that the architects of the operating system anticipated this need.

By default the majority of core operating system components support NX. You can see if a component is protected by NX by looking at the process under Vista’s Task Manager; it’s an optional column named Data Execution Prevention. Although the dialog box that allows you to configure NX refers to software NX as a fallback, “software NX” doesn’t protect much more than exception handlers and only handles a small subset of the attacks that real hardware NX will stop.

The concept here is simple: If a page of memory, whether it is on the stack or the heap, is writable, we ought not be executing code from that page. The vast majority of the exploits seen today will execute code from either the heap or the stack. You might wonder why NX isn’t a sufficient protection against malware–take a look at “Bypassing Windows Hardware-enforced Data Execution Prevention” (Skape and Skywing 2005).

image from book
Bypassing NX

Basically, there have to be ways to enable executable memory. One example would be when we load a DLL after process initialization. The operating system has to allocate pages and write the instructions into process memory, and the system expects to be able to execute these instructions once done. If a piece of shell code could first cause VirtualProtect to be called with the correct parameters, NX is then defeated. As it turns out, calling VirtualProtect, with all the correct parameters in place, is a little difficult due to needing to write very arbitrary values–even on systems without ASLR protection. Matt Miller (aka Skape) of the Metasploit project, and Ken Johnson (aka Skywing) point out that it is also possible to call NtSetInformationProcess to disable NX for an entire process, unless the application has been compiled with /NXCOMPAT. This functionality allows for backward compatibility and allows an application to continue to work if it happens to load a DLL that isn’t compatible with NX protection.

image from book

As of this writing, the combination of NX and ASLR appears to stop all but the most contrived attacks, but we’re not foolish enough to claim that this set of protections is invincible (and most certainly not unbreakable). Let’s take a look at how this feature works in a small snippet of sample code:

 #include <stdio.h> #include <string.h> // win32_exec - EXITFUNC=seh CMD=calc.exe Size=164 // Encoder=PexFnstenvSub http://metasploit.com const unsigned char scode[] = "\x33\xc9\x83\xe9\xdd\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\xc9" "\xfa\x9b\x8c\x83\xeb\xfc\xe2\xf4\x35\x12\xdf\x8c\xc9\xfa\x10\xc9" "\xf5\x71\xe7\x89\xb1\xfb\x74\x07\x86\xe2\x10\xd3\xe9\xfb\x70\xc5" "\x42\xce\x10\x8d\x27\xcb\x5b\x15\x65\x7e\x5b\xf8\xce\x3b\x51\x81" "\xc8\x38\x70\x78\xf2\xae\xbf\x88\xbc\x1f\x10\xd3\xed\xfb\x70\xea" "\x42\xf6\xd0\x07\x96\xe6\x9a\x67\x42\xe6\x10\x8d\x22\x73\xc7\xa8" "\xcd\x39\xaa\x4c\xad\x71\xdb\xbc\x4c\x3a\xe3\x80\x42\xba\x97\x07" "\xb9\xe6\x36\x07\xa1\xf2\x70\x85\x42\x7a\x2b\x8c\xc9\xfa\x10\xe4" "\xf5\xa5\xaa\x7a\xa9\xac\x12\x74\x4a\x3a\xe0\xdc\xa1\x0a\x11\x88" "\x96\x92\x03\x72\x43\xf4\xcc\x73\x2e\x99\xfa\xe0\xaa\xd4\xfe\xf4" "\xac\xfa\x9b\x8c"; typedef void (*RunShell)( void ); int main( int argc, char* argv[] ) {    char StackBuf[256];    RunShell shell = (RunShell)(void*)StackBuf;    strcpy_s( StackBuf, sizeof( StackBuf ), (const char*)scode );    (*shell)();    return 0; }

This code sample is an example of just how ridiculously easy it has become for anyone to obtain completely arbitrary shell code to do whatever they like. All it took to get this string of shell code was to go to www.metasploit.com, download and install their framework, go to their Web interface, and then choose from the menu items. Once compiled and run, a running instance of the Windows calculator appears, and the application will then crash because the shell code is written to cause an exception once the command line is executed. Another option is to get the shell code to call ExitProcess, which will cause the application to exit cleanly.

If /NXCOMPAT is added to the linker options, and if the system supports NX (that is, DEP or XD), and if the demo code is run again from the development environment, a message is generated that looks like this:

Unhandled exception at 0x0012fe40 in NxTest.exe: 0xC0000005: Access violation.

A quick look at the exception address shows that 0x0012fe40 is the address of StackBuf; note that there are no extra instances of calc.exe on the desktop. The system may indicate that the processor doesn’t support NX when there is actually a new processor that should support NX. If that’s the case, check the BIOS options. It seems that many BIOS manufacturers are defaulting NX to off. If the computer does have NX disabled in the BIOS, talk to the hardware vendor and ask that it be enabled by default for all their computers. Even with hardware NX disabled there may still be the weaker software-based protection, which ironically enough, won’t stop the preceding example from executing because there is no buffer overrun corrupting an exception handler.

It’s also interesting to see what Windows Vista does with this application when it is simply invoked from the command line. It first tells us that NxTest.exe has stopped working. If we then tell it to close, a help balloon pops up telling us that Data Execution Prevention has stopped the program, which then explains that we’ve just stopped a security-related flaw.

You must test your application with NX enabled because your customers are going to do it for you, sometimes without knowing what the problem could be, which is that NX is enabled differently on the server line of operating systems and the desktop versions. On the client versions (Windows XP and Vista), the default is that only operating system components are opted in; no other applications have NX enabled. On the server, all applications, except those in the opt-out list, have NX enabled. While that’s the default, nervous security administrators could change the default for clients to the setting for the servers. If this isn’t tested, there could be seemingly random failures as people upgrade to hardware that supports NX and has it enabled in the BIOS.

Currently, the NXCOMPAT flag in the header is ignored by any Windows operating system prior to Windows Vista. On Windows Vista, if NXCOMPAT is set in the linker options, then the application will be running with NX regardless of the settings at OS level (once hardware NX is available). If NXCOMPAT:NO is set, then NX will not apply to the application. Just like ASLR and the heap settings, NXCOMPAT is set processwide; if you’re hosting third-party code, this may cause some problems and is another reason to find a way to get third-party code into its own process.

Performance and Compatibility Implications

There is no performance impact of using NX except when an exception is raised. There could, however, be compatibility problems, depending on how complex an application is. Note that applications using older versions of the ATL library may have problems with NX. It is quite typical for some applications that perform high-performance image manipulation and rendering to create assembler for the pixel pipeline on the fly. Some interpreted languages will also compile a script into assembler and execute it. If the application allows third-party plug-ins, but you can’t be sure that all of the plug-ins are compiled with NX (or are compatible), then you may not be able to immediately use NX in your application. If it is the application that is creating the assembler, the work-around is to obtain a memory page for this use and call VirtualProtect to enable execution on that page. If you can predict what assembler is needed, then it is best to write it into memory and disable write at the same time you enable execute on that page. If the application allows plug-ins, then the best approach is to get the plug-ins out of the process. Getting other people’s code out of your process may give you some stability benefits, assuming you write better code than they do!

Here’s a handy class that you can use if you do need to execute code on the fly and would like to do it safely:

 class WriteThenExecute { public:    WriteThenExecute() : pMemory( NULL ),                         fIsExecutable( false ),                         cbMemory( 0 )    {}    ~WriteThenExecute()    {          if( pMemory != NULL )             VirtualFree( pMemory, 0, MEM_RELEASE );    }    void Allocate(SIZE_T dwSize = 4096 )    {       pMemory = VirtualAlloc( NULL, dwSize,                               MEM_COMMIT, PAGE_READWRITE );       if( pMemory == NULL )           throw GetLastError();       cbMemory = dwSize;    }    void CopyBytes( BYTE* pBytes, SIZE_T cbBytes )    {       if( cbBytes <= cbMemory )       {            DWORD dwPrevProtect;            CopyMemory( pMemory, pBytes, cbBytes );            if( VirtualProtect( pMemory, cbMemory,                                PAGE_EXECUTE_READ, &dwPrevProtect ) )            {               fIsExecutable = true;               return;            }            throw GetLastError();        }        throw ERROR_INSUFFICIENT_BUFFER;    }    template< typename T >    T GetFunctionPtr()    {       if( fIsExecutable )            return reinterpret_cast< T >( pMemory );       return reinterpret_cast< T >( NULL );      }      private:      void* pMemory;      SIZE_T cbMemory;      bool fIsExecutable;   };    int main(void) {      WriteThenExecute foo;      BYTE DemoBuffer[64];      foo.Allocate();      foo.CopyBytes( DemoBuffer, sizeof( DemoBuffer ) );      BYTE* pDemo = foo.GetFunctionPtr< BYTE* >(); }

The small main function demonstrates how you might use this class. It’s fairly simple–just allocate as much memory as you’ll need, copy in the assembly you’d like to run, and then get a function pointer. There are a couple of issues we’d like to bring up before you put this code into production. First, some asserts ought to be sprinkled liberally through the code. Calling Allocate more than once will leak memory and corrupt internal state; calling CopyBytes more than once will cause you to throw an exception; and the default copy constructor and assignment operator ought to be declared private with no implementation, because copying this class to another instance would cause memory leaks or double-free conditions. Second, it’s also a good idea to make a Release, and possibly a MakeWritable method that would reset the class to its original state. Although we throw DWORDs as exceptions in many places in the examples, you shouldn’t do this in production code–use std::exception, or another dedicated exception class instead. Finally, the class isn’t thread safe.



Writing Secure Code for Windows Vista
Writing Secure Code for Windows Vista (Best Practices (Microsoft))
ISBN: 0735623937
EAN: 2147483647
Year: 2004
Pages: 122

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net