The Genius and Idiocy of the Distributed Common Object Model and DCE-RPC

The Genius and Idiocy of the Distributed Common Object Model and DCE-RPC

The Distributed Common Object Model (DCOM), DCE-RPC, NT's Threading and Process Architecture, and NT's Authentication Tokens are all interconnected . It helps to first understand the overall philosophy of COM in order to understand what sets COM apart from its Unix counterparts.

You should remember that Microsoft's position on software has always been to distribute binary packages for money and build an economy to support that. Therefore, every Microsoft software architecture supports this model. You can build a fairly complex application entirely by buying third-party COM modules from various vendors , throwing them into a directory structure, and then using Visual Basic script to tie them together.

COM objects can be written in any language COM supports and interoperate seamlessly. Most of COM's idiosyncrasies come forth as natural design decisions; for example, what is an integer to C++ may not be an integer to Visual Basic.

To dig deeper into COM, you should look at a typical Interface Description Language (IDL) file. We'll use a DCOM IDL file, which you will recognize later.

 [ uuid(e33c0cc4-0482-101a-bc0c-02608c6ba218),   version(1.0),   implicit_handle(handle_t rpc_binding) ] interface ??? {   typedef struct {     TYPE_2 element_1;     TYPE_3 element_2;   } TYPE_1; ...   short Function_00(         [in] long element_9,         [in] [unique] [string] wchar_t *element_10,         [in] [unique] TYPE_1 *element_11,         [in] [unique] TYPE_1 *element_12,         [in] [unique] TYPE_2 *element_13,         [in] long element_14,         [in] long element_15,        [out] [context_handle] void *element_16   ); 

What we've defined here is similar to a C++ class's header file. It simply says that these are the arguments (and return values) for a particular function in a particular interface as defined by that UUID . Anything that must be uniqueany name is a GUID in COM. This 128-bit number is supposed to be globally unique; i.e., there can be only one. Every time we see a reference to that particular UUID , we know we're talking about this exact interface.

Interface descriptions for COM objects can be arbitrarily complex. The compiler (and COM support) for the language is supposed to create a bit of code that can transform as long as the IDL specifies it into the format in which the language needs it to be represented. It is the same with characters , arrays, pointers stored with arrays, structures that have other arrays, and so on.

In practice, a number of shortcuts can be taken to maintain acceptable speed. By saying that a long will be 32 bits in little-endian order, transforming from C++ to another C++ COM object's representation is trivial.

A COM service can be called in two ways: It can be loaded directly into the process space as a DLL, or it can be launched as a service (by the Service Control Manager, a special process that runs as SYSTEM ). Running a COM server in another process ensures that your process will be stable and more secure, though much slower. In-Process calls, which require no transformation of data types, are literally one thousand times faster than calling a COM interface on the same machine but in a different process. Going to the same machine is usually at least ten times faster than going to a machine on the same network.

The important thing to Microsoft was that programmers could make a simple registry change or by changing one parameter in a program, that program would use a different process, or a different machine to make the same call.

For example, look at the AT service on NT. If you were to write a program to interact with AT and schedule commands, you could look up the interface definition for the AT service, make a DCOM call to bind to that interface, and then call a particular procedure on that interface. Of course, you'd need the IDL file to know how to transform your arguments before you sent the data between your process and the AT service's process. This same procedure would work even if the process were on another computer entirely. In that case, your DCOM libraries would connect to the remote computer's endpoint mapper (TCP port 135) and then ask it where the AT service was listening. The endpoint mapper (itself a DCOM service, but one that is always at a known port) would respond "The AT service is listening on the following named pipe RPC services, which you can connect to over ports 445 or 139. It is also listening on TCP port 1025 and UDP port 1034 for DCE-RPC calls." All of this would be transparent to the developer.

Now you know the genius of DCE-RPC and DCOM. You can sell binary DCOM packages or simply put up a network-accessible machine with those DCOM interfaces installed and let developers connect to them from Visual Basic, C++, or any other DCOM-\ enabled language. For extra speed, you can load the interfaces directly into your client process as a DLL. This paradigm is the basis of almost all the features that make Windows NT a distinctive server platform. "Rich clients ," "Remote manageability," and "Rapid Application Development" are all just the same thingDCOM.

But of course, this is also the idiocy of DCE-RPC and DCOM. One man's remote manageability is another man's remote vulnerability. As a hacker, your goal is to know the target systems better than their administrators do. With DCOM as a complex, impossible -to-understand basis for every aspect of a system's security, this is not hard to do.

In the next sections, we'll over a few of the basics for exploiting DCE-RPC and DCOM.

Recon

Two tools are available for basic remote DCE-RPC recon: Dave Aitel's SPIKE ( www.immunitysec.com/ ) and Todd Sabin's DCE-RPC tools (available from http:// razor .bindview.com/ ).

In this example, we'll use SPIKE's dcedump utility to view the DCE-RPC services (also known as DCOM interfaces) available remotely that are registered with the endpoint mapper. This is roughly the same as calling rpcdump -p on a Unix system.

 [dave@localhost dcedump]$ ./dcedump 192.168.1.108  head -20 DCE-RPC tester. TcpConnected Entrynum=0   annotation= uuid=4f82f460-0e21-11cf-909e-00805f48a135 , version=4 Executable on NT: inetinfo.exe ncacn_np:\WIN2KSRV[\PIPE\NNTPSVC] Entrynum=1   annotation= uuid=906b0ce0-c70b-1067-b317-00dd010662da , version=1 Executable on NT: msdtc.exe ncalrpc[LRPC000001f4.00000001] Entrynum=2   annotation= uuid=906b0ce0-c70b-1067-b317-00dd010662da , version=1 Executable on NT: msdtc.exe ncacn_ip_tcp:192.168.1.108[1025] ... 

As you can see, here we have three different interfaces and three different ways to connect to them. We can further examine the interface that the endpoint mapper provides with SPIKE's interface ids (ifids) utility. Likewise, we can examine almost any other TCP enabled interface ( msdtc.exe is one exception).

 [dave@localhost dcedump]$ ./ifids 192.168.1.108 135 DCE-RPC IFIDS by Dave Aitel. Finds all the interfaces and versions listening on that TCP port Tcp Connected Found 11 entries e1af8308-5d1f-11c9-91a4-08002b14a0fa v3.0 0b0a6584-9e0f-11cf-a3cf-00805f68cb1b v1.1 975201b0-59ca-11d0-a8d5-00a0c90d8051 v1.0 e60c73e6-88f9-11cf-9af1-0020af6e72f4 v2.0 99fcfec4-5260-101b-bbcb-00aa0021347a v0.0 b9e79e60-3d52-11ce-aaa1-00006901293f v0.2 412f241e-c12a-11ce-abff-0020af6e7a17 v0.2 00000136-0000-0000-c000-000000000046 v0.0 c6f3ee72-ce7e-11d1-b71e-00c04fc3111a v1.0 4d9f4ab8-7d1c-11cf-861e-0020af6e7c57 v0.0 000001a0-0000-0000-c000-000000000046 v0.0   Done 

Now, these can be fed directly into SPIKE's msrpcfuzz program to attempt to find overflows in the endpoint mapper or in any other TCP service. If you had the IDL for these services (you can get some of them from open source projects such as Snort), you could guide your analysis of these functions. Otherwise you are reduced to doing automatic or manual binary analysis. One program that may help you is Muddle, by Matt Chapman. You can find this program at www.cse.unsw.edu.au/~matthewc/muddle/; it will automatically decode certain executables to tell you their arguments. Muddle generated the IDL fragment you saw earlier in this chapter, which we took from the file for the RPC locator service.

Microsoft has tunneled the DCE-RPC protocol across almost anything it can get its hands on. From SMB to SOAP, if you can tunnel DCE-RPC across it, you've enabled all Microsoft's tools. In the examples, you can see a DCE-RPC over named pipe interface ( ncacn_np ), a DCE-RPC over Local RPC interface, and a DCE-RPC over TCP interface. Named pipe, TCP, and UDP interfaces are all accessible remotely and should make your mouth water.

Exploitation

There are as many ways to exploit a remote DCOM service as there are to exploit a remote SunRPC service. You can do popen() or system() style attacks, try to access files on the file system, find buffer overflows or similar attacks, try to bypass authentication, or anything else you can think up that a remote server might be vulnerable to. The best tool currently publicly available for playing with RPC services is SPIKE. However, if you want to exploit remote DCE-RPC services, you will have to do a lot of work duplicating this protocol in the language of your choice. CANVAS ( www.immunitysec.com/CANVAS/ ) duplicates DCE-RPC using Python.

At first you may be tempted to use Microsoft's internal APIs to do DCE-RPC or DCOM exploitation work, but in the long run, your inability to directly control the APIs will lead to shoddy exploits. Definitely keep to using your own or an open source protocol implementation if possible.

Tokens and Impersonation

Tokens are exactly what they sound likerepresentations of access rights. In Windows, your access rights to things such as files or processes are not defined by a simple user / group /any permission set the way they are on Linux. Instead they use a flexible, and extremely poorly understood mechanism which relies on tokens. In the smallest sense, a token is simply a 32-bit integer, much like a file handle. The NT kernel maintains an internal structure per process that indicates what each token represents in terms of access rights. For example, when a process wants to spawn another process it must check to see if it can access the file it wants to spawn.

Now, here is where things get complicated, because there are several types of tokens, and two tokens can affect each operation: the primary token, and the current thread token. The process was given the primary token when it started up. The current thread token can be obtained from another process or from the LogonUser() function. The LogonUser() function requires a username and password and returns a new token if it is successful. You can attach any given token to your current thread using SetThreadToken(token_to_attach) and remove it with RevertToSelf() , at which point the thread reverts to the primary token.

For fun, load the Sysinternals ( www.sysinternals.com ) Process Explorer to a process and you'll see several things: The primary token is printed out as ser Name and you may see one or more tokens with varying levels of access listed in the bottom pane. Figure 6.2 shows the various tokens in a process.

click to expand
Figure 6.2: Using Process Explorer to view tokens in a process. Note the different levels of access between the Administrator token and the user (primary token).

Getting a token from another process is simple: The kernel will give you the token of any process that is attached to a named pipe you created if you call ImpersonateNamedPipeClient() . Likewise you can impersonate remote DCE-RPC clients or any client that gives you a username and password.

For example, when a user connects to a Unix ftp server, that server is running as root , so it can use setuid() to change its user ID to whatever user the client authenticates as. With Windows, the user sends a username and password, and then the ftp server calls LogonUser() which returns a new token. It then spawns a new thread and that thread calls SetThread_Token(new_token) . When that thread is finished serving the client, it calls RevertToSelf() and joins the threadpool or calls ExitThread() and disappears.

Think of this procedure as an opportunity for a hackerin Unix when you've exploited an ftp server with a buffer overflow after authenticating, you cannot become root or any other user. In Windows, you will likely find tokens from all the users who have authenticated recently waiting in memory for you to grab them and use them. Of course, in many cases, the ftp server itself will be running as SYSTEM , and you can call RevertToSelf() to gain that privilege.

One common misunderstanding surrounds CreateProcess() . A Unix hacker will often call execve ("/bin/sh") as part of their shellcode, but under Windows, CreateProcess() uses the primary token as the token for the new process and uses the current thread token for all file access. This means that if the current primary token is of a lower access level than the token of the current thread, the new process may not be able to read or delete its own executable.

A good illustration of this quirk is what happens during an IIS attack. IIS's external components run inside processes whose primary tokens are IUSR or IWAM rather than SYSTEM . However, these processes often have threads that run inside them as SYSTEM . When an overflow gives a hacker control of one of these threads and they download a file and CreateProcess() it they find themselves running as IUSR or IWAM , but the file is owned by SYSTEM .

If you ever find yourself in this situation you have two options: you can use DuplicateTokenEx() to generate a new primary token, which you can assign to a CreateProcessAsUser() call, or you can do all your work from within your current thread by loading a DLL directly into memory or by using a simple shellcode that does whatever you need from within the original process.

Exception Handling under Win32

In Linux, exception handlers are typically global; in other words, per-process . You set an exception handler with the signal() system call, which gets called whenever an exception such as a segfault (or in Windows terminology, an AV) occurs In Windows, that global handler (in ntdll.dll ) catches any and all exceptions and then performs a fairly complex routine in order to determine to where it gives control. Because the programming model under Windows NT is thread-focused, the exception-handling model is also thread-focused.

Figure 6.3 may help explain exception handling under Windows NT.

click to expand
Figure 6.3: OllyDbg nicely shows you how exception handling works in Windows NT.

As you can see in the figure, the cmd.exe process has two threads. The second thread's data block (which will be at fs:[0] while it is executing) has a pointer to a linked list (chain) of exception structures. The first element of that structure is the pointer to the next handler. The second element of that structure (Structured Exception Handler [SEH]) is a function pointer. As shown in Figure 6.3, the pointer to the next handler is set to -1, indicating no more handlers. However, if the first handler should choose not to handle a given exception, then the next handler (if there is one) would do it, and so on. If no handler wants to accept the exception, then the default exception handler for the process handles it. Usually this results in the termination of the process.

As a hacker you should now see several ways to take control of this system via heap overflows or similar attacks that let you write a word into memory. You could certainly overwrite the pointer to the SEH chain. Every process in a Win32 application has an operating system supplied SEH. The SEH is responsible for displaying the error box that tells the user that the application has terminated . If you happen to have a debugger running, then the SEH gives you an option to debug the application. Another possibility is to overwrite the function pointer for the handler on the stack, or you could overwrite the default exception handler.

On Windows XP you have another option: Vectored Exception handling. Basically, it's just another linked list that the exception handling code in ntdll.dll checks first. So now you have a global variable that gets called on every exceptionperfect for overwriting.



The Shellcoder's Handbook. Discovering and Exploiting Security
Hacking Ubuntu: Serious Hacks Mods and Customizations (ExtremeTech)
ISBN: N/A
EAN: 2147483647
Year: 2003
Pages: 198
Authors: Neal Krawetz

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