Things I learned while poking around with Exchange 2007

August 28th, 2007

(Warning: Long post about Exchange 2007 setup woes ahead.)

Recently, I’ve had the chance (or the misfortune, some might say) of having some time with poking around in an Exchange 2007 configuration. Here’s a brief list of some of the more annoying things I’ve ran into along the way, and how I resolved them (in no particular order):

  1. Offline Address Book (OAB) doesn’t work in RPC-HTTP (Outlook Anywhere) mode unless you have autodiscovery set up. This one took me several weeks to figure out. It was a pretty annoying problem because first I didn’t even know what was the problem – when I configured Outlook 2007 to talk to Exchange in native RPC mode, and then configured it to work over Outlook Anywhere (RPC-HTTP), some indeterminant time after that I would occasionally get complaints from Outlook that send/receive had failed because an item was not found. Now, I enabled full Outlook debug logging, but of course, there was absolutely no mention of this anywhere at all in Outlook’s logs on disk – in fact, there was barely anything useful there at all. Only by searching in Google for a long time did I learn that it might be related to the OAB (or Offline Address Book). After learning this, I narrowed it down to the OAB by determining that doing a send/receive just for purposes of downloading the address book would always break (you can do this from the send/receive menu).

    However, fixing the problem was quite another story. The thing that was most frustrating is that, of course, RPC-HTTP runs over SSL and so you can’t do a packet capture of what was going on. So I tried checking IIS logs, but there weren’t any hits outside of the RPC-HTTP proxy URL (nothing at all apparently related to OAB). Most of the information I had found on Google / Microsoft.com related to things like the OAB distribution URL not being set, the OAB virtual directory not being created in IIS, and a variety of other problems, all of which I could rule out as not applicable to me.

    The whole time, the Exchange address book worked fine over OWA too. And to make things even stranger, I seem to recall that it magically appeared to work if I switched Outlook off of RPC-HTTP and back to direct RPC connectivity.

    It turns out that the real problem here was that I didn’t have autodiscovery working correctly for one of the mail domains in the Exchange environment. I only thought to look at autodiscovery after reading this post about somebody else’s OWA woes. Apparently, Outlook wanted to talk to https://example.com/autodiscover or https://autodiscover.example.com/autodiscover (where “example.com” is the mail domain in use) in order to determine the OAB download URL. This explained the lack of hits in my IIS logs, as the mail server for this domain happened to be on a completely different box from anything else on that domain, so hits on example.com/autodiscover would never show up. Because the mail server wasn’t even on that domain, I decided to just go with autodiscover.example.com. However, this presented a problem, as I would need to acquire another cert and another IP address for that
    domain, just for Outlook to not complain about the OAB periodically. Ugh!

    After doing that (I ended up using a subject alternate name (SAN) certificate), the OAB magically began working in Outlook 2007. Hooray.

  2. Don’t specify a list of domain names (-DomainName) for New-ExchangeCertificate in quotes if you are requesting a SAN certificate. I spent about 10 minutes staring at my command line wondering just what was possibly wrong with it before I learned that the way New-ExchangeCertificate is written, it expects a list of command line arguments and not a command line argument that is a list (subtle distinction, eh?). This one turned out to be my not paying close enough attention to the documentation examples.
  3. Exchange will not accept a certificate with an “E=” in the subject name field for the TLS listeners for IMAP4 / POP3 / SMTP. This one ate up a good chunk of time trying to work through as well. I had filled in an email field out of habit when requesting a cert for Exchange from the domain CA, and everything else in the world besides Exchange had no problem with it. That is to say, IIS liked it, browsers liked it, RDP-SSL liked it, and pretty much everything else I tried with it worked. However, as soon as I gave it to Exchange to use for IMAP4 / POP3 / SMTP, it would barf with an extremely unhelpful (and totally misleading!) event log message:

    A certificate for the hostname “example.com” could not be found. SSL or TLS encryption cannot be made to the IMAP service.

    Which was of course, completely wrong. The certificate was there in the cert store for the computer account, and nothing else had any trouble recognizing it. Even the Exchange console recognized it just fine, but the service just would not take it on start.

    Now, to make things even worse, the Event ID of that event log message happened to be “2007“. Try searching in Google for “Exchange 2007 event id 2007” and you’ll see what a wonderful thing that is for getting useful information on the subject. (Hint: You’ll get pages talking about any Exchange 2007 event log message.)

    Finally, I ended up taking the “sledgehammer approach” and just made a new cert, without an “E=” in the subject name, and it magically worked. Grrrrr…

  4. Something doesn’t work right with the “-MemberOfGroup” filter when used in conjunction with an email address policy (EAP). For some reason, I could never get this to work. The bizzare thing was, the exact same filter string would work great for an address book policy. Furthermore, when dumping the EAP out, if I ran the LDAP query that the filter OPATH got translated into in the AD management console, it returned the expected results. Even more baffling, if I used any other custom filter besides “-MemberOfGroup”, the EAP would work, which didn’t make any sense at all given that the exact same OPATH filter worked fine with an address book policy. I never got to the bottom of this unfortunately, and finally gave up and used a filter off of one of the AD properties for a user instead (which, by the way, worked fine as both a custom or precanned filter).

    I’m guessing that this one has got to be something misconfigured or broken on my end, but for the life of me I have absolutely no idea what. The particular Exchange install was a fresh one on a completely clean Active Directory, all pretty much the most extreme simple case possible (both of which were, as far as I know, done by the books).

  5. Exchange requires that the root certificate authority issuing a non-self-signed certificate be trusted for a certificate to be used for IMAP4 / POP3 / SMTP. Another fun one, unlike everything else in Windows (including RDP-SSL, IIS, etc), Exchange barfs on a certificate for usage as a server if the root CA is not trusted. If you used an external CA to save yourself the headache of setting up a domain CA just for Exchange testing, make sure that Exchange is configured to trust it, or IMAP4 / POP3 / SMTP will all fall over when given a cert issued by that CA. This holds true for both Hub Transport / Client Access / Edge Transport roles in my observation.
  6. The Exchange mangement console and Exchange command shell use a ton of memory. In my experience, the management console (MMC applet) displaced something on the order of 150-200MB* of commit if you watch the memory counters closely before it finished loading. The command shell (PowerShell-based) clocked in at a cool 80MB or so. Wow. Whatever happened to Bill Gate’s quote on memory usage:

    For DOS LM 1.0, the redirector took up 64K of RAM.

    And Bill went ballistic.

    “What do you mean 64K? When we wrote BASIC, it only took up 8K of RAM. What the f*k do you think idiots think you’re doing? Is this thing REALLY 8 F*ing BASIC’s?”

    Yeah, I’m a programmer, and I realize that more complicated software tends to make memory-performance trade offs. Still, it’s a mail server management UI and a command shell. I find it rather amazing that Visual Studio 2005 Team Suite in all of its glory and .NET-ness still manages to clock in at less memory usage (after loading a project) than the MMC console for a mail server. Oh, and to add insult to injury, the MMC GUI can’t even do about half of the administrative tasks out there, despite its humongous memory footprint (to be fair, the GUI is supposed to be enhanced to cover most of the “missing spots” in the SP1 timeframe, from what I can see). Times change, I guess…

    * Note: To be fair (and more precise), dumping the address space and totalling MEM_COMMIT | MEM_PRIVATE regions turned up ~160MB of unshared memory after spawning one instance of the MMC console when the commit charge for the process was ~200MB.

  7. The Exchange 2007 management console has friendly, owner-drawn windows with pretty gradient custom background bitmaps. In other words, the GUI looks nice and polished… until you try and use it over an Internet link instead of a LAN. Then you end up waiting 10 seconds for the “New Mailbox” page to blit its pretty gradient background over to mstsc.exe, block by block. Whoops. To Microsoft’s credit, there is an option (View->Visual Effects->Never) to turn this off. Too bad that it seems to be stuck on maximum graphical awesomeness (otherwise read as excruciatingly slowness over Terminal Server) by default, and that the option is arguably rather well hidden.

Okay, enough bashing on Exchange (and to be fair, the last two items are more things that annoyed me about Exchange than something I’d consider a “problem” in some sense of the word). I like it for all of the cool things it offers, and knowing what I do now, I could probably have gotten a simple Exchange 2007 configuration running in half the time or less than it took the first time around. Furthermore, I’m sure that most of the other large, competing integrated messaging solutions also have their fair share of skeletons in the closet, too.

But that doesn’t change the fact that despite going over the documentation available to me, setting up Exchange was an exercise in banging my head against bizzare problem after bizzare problem, while intermittantly either waiting on pretty dialogs to take forever to transfer over RDP (before I figured out how to “de-prettyify” the GUI), or waiting for the MMC console to finish loading.

Yes, yes, I know, I should be using the PowerShell applet to do all of the work instead of the clunky GUI. Sorry, but I don’t fancy typing out long strings like “First Storage Group”, “CN=First Storage Group,CN=InformationStore”, etc over and over and over again until my fingers bleed. Sometimes, having a GUI is a good thing. Both tools are useful, but sometimes it is more convenient to use the GUI, and sometimes it is more convenient to use the command line. On the plus side, the fact that Exchange 2007 appears to completely support full administration exclusively via a scriptable command line is a big step forward.

Enough of my Exchange meanderings for now. Back to more regular topics next time…

PatchGuard v3 has no relation to “Purple Pill”

August 16th, 2007

One of the things that seems to have been making the rounds lately is some confusion that the recent announcement that Kernel Patch Protection has been updated (“PatchGuard v3”) is a response to Alex Ionescu‘s “Purple Pill”. For those that missed the news recently, Purple Pill is a program that Alex wrote (and briefly posted) that uses a bug in a common ATI driver to gain code execution in kernel mode and from there load a driver image, bypassing the normal Kernel Mode Code Signing (KMCS, otherwise known as Ci – Code Integrity) checks.

Now, KMCS and PatchGuard are designed to achieve different goals. PatchGuard is intended, for better or worse, to prevent third party code from doing things like hooking system calls, hooking interrupts belonging to other code, performing code patches on kernel exports, and the like. Historically, many third party drivers have done this in dangerous and incorrect ways (something that appears to be common among personal security software suites). With the move to 64-bit (which requires a recompile of all kernel mode code), Microsoft took the opportunity to say “enough is enough” and outlaw things like system call hooking, with this ban being enforced by what has come to be known as PatchGuard.

Now, while it is arguable that the assertion that it is really not possible to do correctly many of the things that PatchGuard blocks is really true, it is a sad fact that the vast majority of third party drivers out there which do such dangerous deeds are buggy (and often introduce security holes or crash – bluescreen – bugs in the process). PatchGuard is an obfuscation-protected system for periodically verifying the integrity of various key kernel modules in-memory, as well as the integrity of certain important kernel global variables and processor registers (e.g. a select few MSRs).

On the other hand, KMCS is intended to address a completely different problem. KMCS enforces a policy that every driver that is loaded on Vista x64 (and later Windows versions) must be signed with a valid VeriSign code signing certificate. There is nothing more and nothing less to KMCS than that; the entity of KMCS can be summed up as a mandatory digital signature check on loadable kernel modules. Unlike PatchGuard, KMCS has no way (nor does it try to) enforce any sort of restrictions on what sort of actions any code that passes the signature check can take. That’s not to say that there aren’t potential consequences for signing a driver that does something Microsoft doesn’t like, as they could always blacklist a given driver (or signing key) if they so desired. However, KMCS itself doesn’t have a “magic crystal ball” that would allow it to automatically determine whether a given driver is “good” or “bad”. Rather, as long as the driver has a valid signature and hasn’t been revoked, it is simply permitted to be loaded and executed.

In other words, there is nothing in PatchGuard that has any bearing on Purple Pill. Alex’s program essentially exploits a bug in a (perfectly legitimate) third party driver with a valid signature. Unless his program goes out of its way to do something that would attract the attention of PatchGuard (which would not be necessary to achieve the task of simply mapping code into kernel mode and calling an entrypoint on the loaded image), PatchGuard can be said to be completely unrelated to Purple Pill. And although I wasn’t involved in the creation of Purple Pill, I can tell you that there ought to have been no need to do anything that would have aroused PatchGuard’s ire in order to accomplish what Alex’s program does.

Furthermore, it’s actually a fact that PatchGuard v3 has been available for a lot longer than Purple Pill has (Windows Server 2008 – formerly Windows Server Codename “Longhorn” – has shipped PatchGuard v3 since at least Beta 3), further dispeling the myth that the two are connected in any way. Microsoft simply chose this “Patch Tuesday” to publicly announce PatchGuard v3 and begin pushing it out to users via Windows Update. In this case, the coincidence is just that – a coincidence. You can easily verify this yourself, as the code that I posted for disabling PatchGuard v2 doesn’t in fact work on Windows Server 2008 Beta 3; you’ll get a bugcheck 109 within a few minutes of trying to bypass the driver using the method I provided an implementation for in that paper.

It can sometimes be confusing as to what the relation is between KMCS and PatchGuard, especially because both are at present only implemented on x64 systems, and at least in PatchGuard’s case, PatchGuard on Vista (for some reason) seemed to get more press than PatchGuard on Windows Server 2003 (despite the fact that they are essentially the same). However, with some careful consideration as to what the two technologies do, it’s clear that there isn’t really a direct relation.

Data mining in reverse (or who’s spamming you after all)

August 13th, 2007

Everybody hates spam, the scourge of the Internet. One of the common ways to reduce the amount of spam one gets to use throw-away email accounts or aliases when signing up for compulsory registration services (another scourge of the Internet in my mind, but that’s just my opinion).

Google even makes this easy with Gmail, as you can append a + sign followed by text to create a throw-away alias for your Gmail account (with the disadvantage that spammers could trivially strip + signs and determine your real email address if they were so inclined).

One way to take this a bit further and figure out just who is spamming you, however, is to use unique aliases for every compulsory registration service you sign up for, and then take note of which ones actually start getting spammed. Provided you really don’t reuse those aliases, if one starts getting spammed, it’s a pretty good indicator that the person you gave it to is either compromised or selling your email address out.

For example, I used a unique alias to sign up for some VMware Server serial numbers some time ago (they’re freely available), and recently (to my surprise) it started getting spams (of the type that I would imagine would be emitted by spam bots on compromised home systems). Now, either VMware is selling out my email to spammers of the more shady sort (which I would consider unlikely for a reputable company), or someone with access to VMware’s marketing mailing lists got compromised with some sort of malware at some point and the contents of the mailing list got inadvertently leaked out (oops!). I’d consider the latter more likely in this case, at least unless VMware has some sort of underhanded interest in herbal medication and the like that they’ve been keeping under the table…

I think this is a good time to point out that even reputable companies make mistakes, and it only takes one person’s compromised Outlook to spill the goods on a mailing list. As a result, even with more trusted companies, I am more inclined to use throw-away aliases rather than my main alias, so that I can cut off the throw-away aliases when they start getting spam some time down the road.

You can write a (complete) minidump of a process from TaskMgr in Vista

August 10th, 2007

One of the often overlooked enhancements that was made to Windows with the release of Vista is the capability to write a complete (large) minidump describing the state of a process from Task Manager. To use this functionality, switch to the Processes tab in Task Manager, access the right click (context) menu for a process for which your user account has access to, and select Create Dump File.

Although not as handy as having an “in-box” debugger (ntsd was most regrettably removed from the Vista distribution on the grounds that too many users were getting it and the (typically more up to date) DTW distribution of ntsd confused), Microsoft has thrown the developer crowd at least something of a bone with the dump file support in Task Manager. (It’s at least easier to talk a non-developer through getting a dump via Task Manager than via ntsd, or so one would suppose.)

The create dump file option writes a full minidump out to %temp%\exename.dmp. The dump is large and describes a fairly complete state of the process, so it would be a good idea to compress it before transfer. (I don’t know of any option to generate summary dumps that ships with the OS. However, to be honest, unless space is of an extreme concern, I don’t know why anyone would want to write a summary dump if they are manually gathering a dump – full state information is definitely worth a few minutes wait of transfer time on a typical cable/DSL connection.)

While I’d still prefer having a full debugger shipped with the OS (still powerful, even without symbol support), the new Task Manager support is definitely better than nothing. Though, I still object to the line of thought that it’s better to remove developer tools from the default install because people might accidentally run an older version that shipped with the OS instead of the newer version they installed. Honestly, if a person is enough of a developer to understand how to work ntsd, they had better damn well be able to know which version they are starting (which is really not that much of a feat, considering that the start up banner for ntsd prints out the version number on the first line). If someone is really having that much trouble with launching the wrong version of the debugger, in my expert opinion that is going to be the least of their problems in effectively debugging a problem.

(</silly_rant> – still slightly annoyed at losing out on the debuggers on the default Vista install [yep, I used them!])

How least privilege is that service, anyway (or much ado about impersonation) – part 2

August 8th, 2007

Last time, I described some of the details behind impersonation (including a very brief overview of some of the dangers of using it improperly, and how to use impersonation safely via Security Quality of Service). I also mentioned that it might be possible to go from a compromised LocalService / NetworkService to full LocalSystem access in some circumstances. This article expands upon that concept, and ties together just what impersonation means for low-privileged services and the clients that talk to them.

As I mentioned before, impersonation is not really broken by design as it might appear at first glance, and it is in fact possible to use it correctly via setting up a correct SQOS when making calls out to impersonation-enabled IPC servers. Many things do correctly use impersonation, in fact, just to be clear about that. (Then again, many things also use strcpy correctly (e.g. after an explicit length check). It’s just the ones that don’t which get all the bad press…)

That being said, as with strcpy, it can be easy to misuse impersonation, often to dangerous consequences. As they say, the devil is often in the details. The fact is that there exist a great many things out there which simply don’t use impersonation correctly. Whether this is just due to how they are designed or ignorance of the sensitive nature of allowing an untrusted server to impersonate ones security concept is debatable (though in many cases I would tend towards the latter), but for now, many programs just plain get impersonation wrong.

Microsoft is certainly not ignorant to the matter (for example, David’s post describes how Office wrappers CreateFile to ensure that it never gets tricked into allowing impersonation, because the wrapper by default doesn’t permit remote servers to fully impersonate the caller via the proper use of SQOS). However, the defaults remain permissive at least as far as APIs that connect to impersonation-enabled servers (e.g. named pipes, RPC, LPC), and by default allow full impersonation by the server unless the client program explicitly specifies a different SQOS. Even the best people make mistakes, and Microsoft is certainly no exception to this rule – programmers are, after all, only human.

In retrospect, if the security system were first being designed in today’s day and age instead of back in the days of NT 3.1, I’m sure the designers would have chosen a less security sensitive default, but the reality is that the default can probably never change due to massive application compatibility issues.

Back to the issue of LocalService / NetworkService and svchost, however. Many of these low privileged services have “careless” clients that connect to impersonation-enabled IPC servers with high privileges, even in Windows Server 2008. Moreover, due to the fact that LocalService / NetworkService isolation is from a security standpoint all but paper thin prior to Windows Server 2003 (and better, though only in the non-shared-process, that is, non-svchost case in Vista and Windows server 2008), the sort of additive attack surface problem I described in the previous article comes into play. To give a basic example, try attaching to the svchost that runs the “LocalServiceNetworkRestricted” service group, including Eventlog, Dhcp (the DHCP client), and several other services (in Windows Server 2008) and setting the following breakpoint (be sure to disable HTTP symbol server access beforehand or you’ll deadlock the debugger and have to reboot – another reason why I dislike svchost services in general):

bp RPCRT4!RpcImpersonateClient "kv ; gu ; !token ; g"

Then, wait for an event log message to be written to the system event log (a fairly regular occurance, though if you want you can use msg.exe to send a TS messge from any account if you don’t want to wait, which will result in the message being logged to the system event log immediately courtsey of the hard error logging facility). You’ll see something like this:

Call Site
RPCRT4!RpcImpersonateClient
wevtsvc!EvtCheckAccess+0x68
wevtsvc!LegacyAccessCheck+0x15e
wevtsvc!ElfrReportEventW+0x2b2
RPCRT4!Invoke+0x65
[…]

TS Session ID: 0x1
User: S-1-5-18 (LocalSystem)
Groups:
00 S-1-5-32-544 (Administrators)
Attributes – Default Enabled Owner
01 S-1-1-0
Attributes – Mandatory Default Enabled
02 S-1-5-11
Attributes – Mandatory Default Enabled
03 S-1-16-16384 (System Integrity)
Attributes – GroupIntegrity GroupIntegrityEnabled
Primary Group: S-1-5-18
Privs:
00 0x000000002 SeCreateTokenPrivilege Attributes –
[…]
Auth ID: 0:3e7 (SYSTEM_LUID)
Impersonation Level: Impersonation
TokenType: Impersonation

Again, checking winnt.h, it is immediately obvious that the eventlog service (which runs as LocalService) gets RPC requests from LocalSystem at System integrity level, with the caller enabling full impersonation and transferring all of its far-reaching privileges, many of them alone enough to completely compromise the system. Now, this and of itself might not be so bad, if not for the fact that a bunch of other “non-privileged” services share the same effective security context as eventlog (thanks to the magic of svchost), such as the DHCP client, significant parts of the Windows Audio subsystem (in Vista or in Srv08 if you enable audio), and various other services (such as the Security Center / Peer Networking Identity Manager / Peer Name Resolution Protocol services, at least in Vista).

What does all of this really mean? Well, several of those above services are network facing (the DHCP client certainly is), and they all likely expose some sort of user-facing IPC interface as well. Due to the fact that they all share the same security context as eventlog, a compromise in any one of those services could trivially be escalated to LocalSystem by an attacker who is the least bit clever. And that is how a hypothetical vulnerability in a non-privileged network-facing service like the DHCP client might get blown up into a LocalSystem compromise, thanks to svchost.

(Actually, even the DHCP client alone seems to expose its own RPC interface that is periodically connected to by LocalSystem processes who allow impersonation, so in the case of an (again hypothetical) vulnerability DHCP client service, one wouldn’t even need to go to the trouble of attacking eventlog as the current service would be enough.)

The point of this series is not to point fingers at the DHCP / Windows Audio / Eventlog (or other) services, however, but rather to point out that many of the so-called “low privileged” services are not actually as low privileged as one might think in the current implementation. Much of the fault here actually lies with whatever programs connect to these low-privileged services with full impersonation enabled than the actual services themselves, in fact, but the end result is that many of these services are not nearly as privilege-isolated as we would prefer to believe due to the fact that they are called irresponsibly (e.g. somebody forgot to fill out a SECURITY_QUALITY_OF_SERVICE and accepted the defaults, which amounts to completely trusting the other end of the IPC call).

The problem is even more common when it comes to third party software. I would put forth that at least some of this is a documentation / knowledge transfer problem. For example, how many times have you seen SECURITY_QUALITY_OF_SERVICE mentioned in the MSDN documentation? The CreateFile documentation mentions SQOS-related attributes (impersonation restriction flags) in passing, with only a hint of the trouble you’re setting yourself up for by accepting the defaults. This is especially insidious with CreateFile, as unlike other impersonation-enabled APIs, it’s comparatively very easy to sneak a “bad” filename that points to a dangerous, custom impersonation-enabled named pipe server anywhere a user-specified file is opened and then written to (other impersonation attack approaches typically require that an existing, “well-known” address / name for an IPC server to be compromised as opposed to the luxury of being able to set up a completely new IPC server with a unique name that a program might be tricked into connecting to).

The take-home for this series is then to watch out when you’re connecting to a remote service that allows impersonation. Unless absolutely necessary you should specify the minimum level of impersonation (e.g. SecurityIdentification) instead of granting full access (e.g. SecurityImpersonation, the default). And if you must allow the remote service to use full impersonation, be sure that you aren’t creating a “privilege inversion” where you are transferring high privileges to an otherwise low-privileged, network-facing service.

Oh, and just to be clear, this doesn’t mean that eventlog (or the other services mentioned) are full of security holes outside of the box. You’ll note that I explicitly used a hypothetical vulnerability in the DHCP client service for my example attack scenario. The impersonation misuse does, however, mean that much of the work that has been done in terms of isolating services into their own compartmentalized security contexts isn’t exactly the bullet proof wall one would hope for most LocalService / NetworkService processes (and especially those sharing the same address space). Ironically, though, from certain respects it is the callers of these services that share part or all of the blame (depending on whether the service really requires the ability to fully impersonate its clients like that or not).

As a result, at least from an absolute security perspective, I would consider eventlog / DHCP / AudioSrv (and friends) just as “LocalSystem” in Windows Server 2008 as they were back in Windows 2000, because the reality is that if any of those services are compromised, in today’s (and tommorow’s, with respect to Windows Server 2008, at least judging from the Beta 3 timeframe) implementation, the attacker can elevate themselves to LocalSystem if they are sufficiently clever. That’s not to say that all the work that’s been done since Windows 2000 is wasted, but rather that we’re hardly “all of the way there yet”.

How least privilege is that service, anyway (or much ado about impersonation) – part 1

August 6th, 2007

Previously, I discussed some of the pitfalls with LocalService and NetworkService, especially with respect to pre-Windows-Vista platforms (e.g. Windows Server 2003).

As I alluded to in that post, however, a case can be made that there still exists a less than ideal circumstance even in Vista / Srv08 with respect to how the least-privilege service initiative has actually paid out. Much of this is due to the compromise between performance and security that was made with packing many unrelated (or loosely related) services into a single svchost process. On Windows Server 2003 and prior platforms, the problem is somewhat more exacerbated as it is much easier for services running under different processes to directly interfere with eachother while running as LocalService / NetworkService, due to a loose setting of the owner field in the process object DACL for LocalService / NetworkService processes.

As it relates to Vista, and downlevel platforms as well, many of these “low privileged” services that run as LocalService or NetworkService are really highly privileged processes in disguise. For a moment, assume that this is actually a design requirement (which is arguable in many cases) and that some of these “low privileged” services are required to be given dangerous abilities (where “dangerous” is defined in this context as could be leveraged to take control of the system or otherwise elevate privileges). A problem then occurs when services that really don’t need to be given high privileges are mixed in the same security context (either the same process in Vista, or the same account (LocalService / NetworService) in downlevel systems. This is due to the obvious fact that mixing code with multiple privilege levels in a way such that code at a lower privilege level can interfere with code at a higher privilege level represents a clear break in the security model.

Now, some of you are by this point probably thinking “What’s he talking about? LocalService and NetworkService aren’t the same thing as LocalSystem.”, and to a certain extent, that is true, at least when you consider a trivial service on its own. There are many ways in which these “low privileged” services are actually more privileged than might meet the eye at first glance, however. Many of these “low privileged services” happen to work in a way such that although they don’t explicitly run with System/Administrator privileges, if the service “really wanted to”, it could get high privileges with the resources made available to it. Now, in my mind, when you are considering whether two things are equivalent from a privilege level, the proper thing to do is to consider the maximum privilege set a particular program could run with, which might not be the same as the privilege set of the account the process runs as.

To see what I mean, it’s necessary to understand an integral part of the Windows security model called impersonation. Impersonation allows one program to “borrow” the security context of another program, with that program’s consent, for purposes of performing a particular operation on behalf of that program. This is classically described as a higher privileged server that, when receiving a request from a client, obtains the privileges of the client and uses them to carry out the task in question. Thus, impersonation is often seen as a way to ensure that a high privileged server is not “tricked” into doing a dangerous operation on behalf of a client, because it “borrows” the client’s security context for the duration of the operation it is performing on behalf of that client. In other words, for the duration of that operation, the server’s privileges are effectively the same as the client’s, which often results in the server appearing to “drop privileges” temporarily.

Now, impersonation is an important part of practically all of the Windows IPC (inter-process communication) mechanisms, such as named pipes, RPC, LPC, and soforth. Aside from the use of impersonation to authoritatively identify the identity of a caller for purposes of access checks, many services use impersonation to “drop privileges” for an operation to the same level of security as a caller (such that if a “plain user” tries to make a call that does some operation requiring administrative privileges, even if the service was running with those administrative privileges originally, any resources accessed by the server during impersonation are treated as if the “plain user” accessed them. This prevents the “plain user” from successfully performing the dangerous / administrative task without the proper privileges being granted to it).

All this is well and fine, but there’s a catch: impersonation can also effectively elevate the privileges of a thread, not just lower them. Therein lies the rub with svchosts and LocalService / NetworkService accounts, since while these accounts may appear to be unprivileged at first glance, many of them operate RPC or LPC or named pipe servers that that privileged clients connect to on a regular basis. If one of these “low privileged” services is then compromised, although an attacker might not immediately be able to gain control of the system by elevating themself to LocalSystem privileges, with a bit of patience he or she can still reach the same effect. Specifically, the attacker need only take over the server part of the RPC / LPC / other IPC interface, wait for an incoming request, then impersonate the caller. If the caller happens to be highly privileged, then poof! All of a sudden the lowly, unprivileged LocalService / NetworkService just gained administrative access to the box.

Of course, the designers of the NT security system forsaw this problem a mile away, and built in features to the security system to prevent its abuse. Foremost, the caller gets to determine to what extent the server can impersonate it, via the use of a little-known attribute known as the Security Quality of Service (SQOS). Via the SQOS attribute (which can be set on any call that enables a remote server to impersonate one’s security context), a client can specify that a service can query its identity but not use that identity to perform access or privilege checks, as opposed to giving the server free reign over its identity once it connects. Secondly, in the post-Windows 2000 era, Microsoft restricted impersonation to accounts with a newly-created privilege, SeImpersonatePrivilege, which is by default only granted to LocalService / NetworkService / LocalSystem and administrative accounts.

So, impersonation isn’t really broken by design (and indeed David LeBlanc has an excellent article describing how, if used correctly, impersonation isn’t the giant security hole that you might think it is.

That being said, impersonation can still be very dangerous if misused (like many other aspects of the security system).

Coming up: A look at how all of this impersonation nonsense applies to LocalService / NetworkService (and svchost processes) in Windows (past and future versions).

“These aren’t the network connections you’re looking for”

August 4th, 2007

It seems that among other things, my Srv08 Beta 3 box seems to be practicing its Jedi mind tricks whenever I try to reconnect a broken RDP session to it:

These aren't the network connections you're looking for

I guess it really doesn’t want me to be able to get back on after a connectivity interruption. I certainly hope this bug doesn’t make it out to RTM, as seamless RDP session reconnects are one of my favorite RDP features.

After all, a user shouldn’t really have to think about it when they switch between network connections, in my opinion. Programs should really be designed to more seamlessly resume across minor connectivity hiccups than is commonplace nowadays, especially with mobile Internet connectivity becoming mainstream. I kind of like this over conventional SSH/screen, in that at least for RDP, session reconnect doesn’t require any user interaction. I suppose with an autoreconnecting SSH client and some scriptable commands to run screen immediately on connecting one could hack up a similar end user experience, though I doubt it would be as seamless. One thing that SSH/screen have on RDP is better shadowing support, allowing for more than one session to shadow a particular target session at the same time in particular.

There are still some flaws with RDP session reconnect in general (especially relating to the fact that window Z order tends to get something akin to randomized when you reconnect, and sometimes windows mysteriously get shrunk to the minimum size requiring manual resizing to fix), but it’s mostly reliable in my experience (discounting this Srv08 weirdness, that is).

Handy debugger commands: !uniqstack

August 2nd, 2007

Often times while analyzing more complicated crash or hang problems, one needs to take a survey of what the overall state of a particular program is in order to get a better handle on the problem. A typical way to do this is to grab a call stack of all threads, such as via “~*kv“, or similar commands. While this works, there is often a lot of noise in the output.

Part of the problem is that in most non-trivial programs, you’ll tend to find a lot of worker threads that are for the most part “uninteresting” as to the overall state of the program and whatever issue you are trying to diagnose. For example, RPC likes to create several worker threads, several of which might all be blocked waiting for a new work item to pick up. Or a program could use the thread pooling APIs, or even have its own from-scratch thread pool.

All of these extra worker threads sitting around doing nothing are a nuisance, because they clutter up a summary of all thread stacks unnecessarily and can make it more difficult to find the threads that we’re really interested in.

One useful debugger command that can help cut through the mess in cases like this is !uniqstack. It works similar to “~*k“, except that it filters the output to only include call stacks that are not exact duplicates of eachother (in terms of return addresses on the stack). After all thread stacks are listed, the extension displays a count of duplicate call stacks and enumerates each thread that had a duplicate stack.

Since most of these duplicate call stacks are not interesting to us anyway, this can often help gain some better visibility into a program with many worker threads.

Why you shouldn’t touch Change­Window­Message­Filter with a 10-ft pole…

July 31st, 2007

One of the things introduced with Windows Vista is the concept of something called “user interface privilege isolation”, or an attempt to allow multiple processes to coexist on one desktop even if they are running at different privilege levels, without compromising security. This new change to the security architecture with respect to how the user interface operates is also a significant part of how UAC and Internet Explorer Protected Mode can claim be to be reasonably secure, despite displaying user interfaces from differing security contexts on the same desktop.

(For reference, the Windows GUI model was designed back in the 16-bit cooperative multitasking days, where every task (yes, task – there weren’t processes or threads in Windows in those days) completely trusted every other task. Although things have improved somewhat since then, there are a number of assumptions that are so fundamental to how the Windows GUI model works that it is virtually impossible to eliminate all communication between GUI programs via window messages and still have the system function, which leads to many of the difficulties in securing user interface communications in Windows. As a result, the desktop has traditionally been considered the “security barrier” in terms of the Windows UI, such that processes of differing privilege levels should be isolated on their own desktops. Of course, since only one desktop is displayed locally at a time, this is less than convenient from an end user’s perspective.)

Most of the changes that were made in Vista relate to restrictions on just how processes from different integrity levels or user accounts can communicate eachother using the window messaging system. Window messages are typically split up into several different categories:

  1. System marshalled messages, or messages that come before WM_USER. These are messages that for compatibility with 16-bit Windows (and convenience), are automagically marshalled cross process. For example, this is why you can send the LB_ADDSTRING message to a window located in a different process, even though that message takes a pointer to a string which would obviously not be valid in the remote address space. The windowing system understands the semantics of all system marshalled messages and can thus, as the name implies, marshal them cross-process. This also means that in many circumstances, the system can also perform parameter validation in the cross process case, so that for instance a null pointer passed to LB_ADDSTRING won’t cause the remote process to crash. The system marshalled range also includes messages like WM_DESTROY, WM_QUIT, and the like. Most of the “built in” controls like the Edit and ListBox controls are “grandfathered in” to the system marshalled range, for compatibility reasons, though the newer common controls are not specially handled like this.
  2. The private window class message range, from WM_USER to 0x7FFF. These messages are specific to a particular custom window class (note that common controls, such as Rich Edit, are “custom” window classes even though you might think of them as being built in to the operating system; the windowing system ostensibly has no special knowledge of these controls, unlike the “built-in” controls like ListBox or Edit). Because the format and semantics of these messages are specific to a program-supplied window class, the window manager cannot interpret, marshal, or validate these parameters cross-process.
  3. The private application message range, from WM_APP to 0xBFFF. These window messages are specific to a program (and not a window class), though in practice it is very common for programmers to incorrectly interchange WM_USER and WM_APP for custom window classes that are internal to an application. These window messages are again completely opaque to the window manager and are essentially treated the same as private window class messages. Their intended use is to allow things like application customized subclasses of window classes to communicate with eachother (e.g. in the case where the application hooks a window procedure).
  4. The registered message range, from 0xC000 to 0xFFFF. Window messages here are dynamically assigned similarly to how atoms work in Windows. Specifically, a program passes an arbitrary string to the RegisterWindowMessage routine, which hands the application back a value in the registered message range. Any other program calling RegisterWindowMessage in the same session will receive the same window message value. These messages are, like class- and application- defined messages, opaque to the system. They are used when programs need to communicate cross-process with custom window messages, without the use of some sort of central registry where window messages would have to be permanently registered with Microsoft in order to receive a unique, non-conflicting identifier. By using an arbitrary string value (easy to make unique) and dynamically reserving a numeric message identifier at runtime, no registry is required and programs following the “standard” for that window message can still communicate with eachother. Registered window messages are not marshalled or interpreted by the windowing system.

Now, in Windows Vista, only a subset of the system marshalled messages can be sent cross process when the two processes have differnet integrity levels, and this subset of messages is heavily validated by the system, such that if a program receives a message in the system marshalled range, it can ostensibly “trust” it. Since the windowing system cannot validate custom messages (in any of the other three categories), these are all silently dropped by default in this scenario. This is typically a good thing (consider that many common control messages have pointers in their contracts and lay in the WM_USER range, making it very bad for an untrusted program to be able to send them to a privileged application). However, sometimes, one does need to send custom messages cross process. Vista provides a mechanism for this in the Change­Window­Message­Filter function, which is essentially a way to “poke a hole” in the window message “firewall” that exists between cross-integrity-level processes in Vista.

Now, this might seem like a great approach at first – after all, you’ll only use Change­Window­Message­Filter when you’re sure you can completely validate a received message even if it is from an untrusted source, such that there’s no way something could go wrong, right?

Well, the problem is that even if you do this, you are often opening your program up to attack unintentionally. Consider for a moment how custom window messages are typically used; virtually all the common controls in existance have “dangerous” messages in the custom class message range (e.g. WM_USER and friends). Additionally, many programs and third party libraries confuse WM_USER and WM_APP, such that you may have programs communicating cross process via both WM_USER and WM_APP, via “dangerous” messages that are used to make sensitive decisions or include pointer parameters.

This means that in reality, you can’t really use Change­Window­Message­Filter for a specific window message unless you are absolutely sure that nobody else in your process is listening for that message and can’t be exploited if they receive a malformed (or specially crafted) message. Right away, this pretty much excludes all WM_USER messages, and even WM_APP is highly questionably in my opinion due to how frequently components mix up WM_USER and WM_APP.

Well, that’s still not so bad, is it? After all, you can just ensure that any third party modules you use don’t do stupid things with custom window messages if you’ve got source code to them, right? Well, aside from the fact that that is in reality a pretty implausible scenario, the problem is even worse. There is rampant use of Change­Window­Message­Filter in operating system shipped libraries that your program is already using on Vista, which you have absolutely no control over. For instance, if one looks at Shell32.dll with a disassembler in Vista, one might see this in, say, the SHChangeNotifyRegister function:

mov     edx, 1          ; MSGFLT_ADD
mov     ecx, 401h       ; WM_USER + 1
call    cs:__imp_ChangeWindowMessageFilter

In other words, SHChangeNotifyRegister just promised to the window message firewall that everybody in the entire process fully validates the custom class message WM_USER + 1. Yow! The WM_USER range is the worst of any to be doing that on, especially a low WM_USER value because practically everything that uses a custom window class will use a WM_USER message for something, and the changes of a collision with the set of all custom window classes goes extremely much up that close to the start of the custom window class range. Now, just for kicks, let’s take a look at the SDK headers and see if any of the built in common controls have any interesting messages that match WM_USER + 1, or 0x401:

AclUI.h(142):#define PSPCB_SI_INITDIALOG (WM_USER + 1)
CommCtrl.h(1458):#define TB_ENABLEBUTTON (WM_USER + 1)
CommCtrl.h(2562):#define TTM_ACTIVATE (WM_USER + 1)
CommCtrl.h(6230):#define CBEM_INSERTITEMA (WM_USER + 1)
CommDlg.h(765):#define WM_CHOOSEFONT_GETLOGFONT (WM_USER + 1)
[…]

Let’s look at the documentation for some of those window messages in MSDN. Hmm, there’s CBEM_INSERTITEM[A|W], which takes, in lParam, “A pointer to a COMBOBOXEXITEM structure…”. Oh, and there’s also WM_CHOOSEFONT_GETLOGFONT, which uses lParam for a “pointer to a LOGFONT structure…”. Hmm, let me see. Anybody who calls SHChangeNotify is promising that they are not using any ComboBoxEx controls, any choosefont controls, or any of the other many hits for dangerous WM_USER + 1 messages that I didn’t list for space reasons. Oh, and that’s just the built-in controls – what happens if there’s a third party control thrown in there? What if we’re running in Internet Explorer and there’s a custom ActiveX control showing its own user interface there, blissfully unaware that some other code in the process called SHChangeNotify. That means that anybody, anywhere, who calls SHChangeNotify on Vista (or uses a library or function that calls SHChangeNotify internally, which is probably not going to be documented at all being an implementation detail) just reinvented the shatter attack with their program (congratulations!), probably without even realizing it – how would they, if they didn’t take the time to disassemble the API instead of trusting that it just works?

Now, I might be coming off a bit harsh on Microsoft here, but that’s kind of my point. Microsoft puts a lot of effort into security, and they’re the ones who designed and implemented the new the new security improvements on Vista. Sadly, this is hardly an isolated incident in Vista – with a little bit of looking, it’s very easy to find numerous other examples of system libraries that are loaded in used and called all over the place making this sort of error, which represents in my opinion a fundamental lack of understanding of how user interface security works.

Now, if the company that designed and implemented Change­Window­Message­Filter is using it wrong, how many third party developers out there that just want to get their program working under Vista in the quickest way possible with the minimum amount of effort and money spent will do the right thing? MSDN doesn’t even document this entire class of problems with Change­Window­Message­Filter that I can tell, so to be honest I think I can be fairly confident and say “virtually nobody”. It takes someone with a fairly good understanding of how the window messaging system impacts security to recognize and grasp this problem, and that only includes people who are even thinking about security in the first place when they see a function like Change­Window­Message­Filter, which I’m betting are already the vast minority.

This function is one that is pretty much all but impossible to use correctly aside from just maybe messages in the registered message range, which are already expected to be cross process and and not fully trusted (and I’m still skeptical that people will even on average get it right even in that case).

There are almost certainly other loopholes in the UIPI architecture that have yet to be discovered, if for no other reason than that it is a security bolt-on to an architecture that was designed for a single shared address space in a cooperative multitasking system. I wouldn’t say that this is so much the fault of the UIPI folks, but rather a fact that it’s just going to be ridiculously hard to make it completely safe to run programs with multiple privilege levels on the same desktop. And, remember, the “good guys” have to get it right 100% of the time from a security perspective, while the “bad guys” only need to find that one case out of 1000 that got missed in order to break the system.

So, do yourself a favor and stick to the desktop as a security barrier; the 16-bit Windows-derived window messaging system was just not designed to support programs at different privilege levels on the same desktop.

Be careful about using the built-in “low privilege” service accounts…

July 25th, 2007

One of the security enhancements in the Windows XP and Windows Server 2003 timeframe was to move a number of the built-in services that ship with the OS to run as a more restricted user account than LocalSystem. Specifically, two new built-in accounts akin to LocalSystem were introduced exclusively for use with services: The local service and network service accounts. These are essentially slightly more powerful than plain user accounts, but not powerful enough such that a compromise will mean the entire system is a write-off.

The intention here was to reduce the attack surface of the system as a whole, such that if a service that is running as LocalService or NetworkService is compromised, then it cannot be used to take over the system as a whole.

(For those curious, the difference between LocalService and NetworkService is only evident in domain scenarios. If the computer is joined to a domain, LocalService authenticates as a guest on the network, while NetworkService (like LocalSystem) authenticates as the computer account.)

Now, reducing the amount of code running as LocalSystem is a great thing pretty much all around, but there are some sticking points with the way the two built-in service accounts work that aren’t really covered in the documentation. Specifically, that there are a whole lot of other services that run as either LocalService or NetworkService nowadays, and by virtue of the fact that they all run as the same security context they can be compromised as one unit. In other words, if you compromise one LocalService process, you can attack all other LocalService processes, because they are running under the same security context.

Think about that for a minute. That effectively means that the attack surface of any LocalService process can in some sense be considered the sum of the attack surface of all LocalService processes on the same computer. Moreover, that means that as you offload more and more services to run as LocalService, the problem gets worse. (Although, it’s still better than the situation when everybody ran as LocalSystem, certainly.)

Windows Vista improves on this a little bit; in Vista, LocalService and NetworkService processes do have a little bit of protection from eachother, in that each service instance is assigned a unique SID that is marked as the owner for the process object (even though the process is running as LocalService or NetworkService). Furthermore, the default DACL for processes running as LocalService or NetworkService only grants access to administrators and the service-unique SID. This means that in Vista, one compromised LocalService process can’t simply use OpenProcess and WriteProcessMemory (or the like) to take complete control over another service process in Vista.

You can easily see this in action in the kernel debugger. Here’s what things look like in Vista:

kd> !process fffffa80022e0c10
PROCESS fffffa80022e0c10
[...]
    Token   fffff88001e3e060
[...]
kd> !token fffff88001e3e060
_TOKEN fffff88001e3e060
TS Session ID: 0
User: S-1-5-19
Groups:
[...]
 10 S-1-5-5-0-107490
    Attributes - Mandatory Default Enabled Owner LogonId 
[...]
kd> !object fffffa80022e0c10
Object: fffffa80022e0c10  Type: (fffffa8000654840) Process
    ObjectHeader: fffffa80022e0be0 (old version)
    HandleCount: 5  PointerCount: 96
kd> dt nt!_OBJECT_HEADER fffffa80022e0be0
[...]
   +0x028 SecurityDescriptor : 0xfffff880`01e14c26 
kd> !sd 0xfffff880`01e14c20
->Revision: 0x1
[...]
->Dacl    : ->Ace[0]: ->AceType: ACCESS_ALLOWED_ACE_TYPE
->Dacl    : ->Ace[0]: ->AceFlags: 0x0
->Dacl    : ->Ace[0]: ->AceSize: 0x1c
->Dacl    : ->Ace[0]: ->Mask : 0x001fffff
->Dacl    : ->Ace[0]: ->SID: S-1-5-5-0-107490

->Dacl    : ->Ace[1]: ->AceType: ACCESS_ALLOWED_ACE_TYPE
->Dacl    : ->Ace[1]: ->AceFlags: 0x0
->Dacl    : ->Ace[1]: ->AceSize: 0x18
->Dacl    : ->Ace[1]: ->Mask : 0x00001400
->Dacl    : ->Ace[1]: ->SID: S-1-5-32-544

Looking at winnt.h, we can see that S-1-5-5-X-Y corresponds to a logon session SID. In Vista, each LocalService/NetworkService service process gets its own logon session SID.

By making the process owned by a different user than it is running as, and not allowing access to the user that the service is running as (but instead the logon session), the service is provided some measure of protection against processes in the same user context. This may not provide complete protection, though, as in general, any securable objects such as files or registry keys that contain an ACE matching against LocalService or NetworkService will be at the mercy of all such processes. To Microsoft’s credit, however, the default DACL in the token for such LocalService/NetworkService services doesn’t grant GenericAll to the user account for the service, but rather the service SID (another concept that is unique to Vista and future systems).

Furthermore, it seems like many of the ACLs that previously referred to LocalService/NetworkService are being transitioned to use service SIDs instead, which may again over time make LocalService/NetworkService once again viable, after all the third party software in the world that makes security decisions on those two SIDs is updated (hmm…), and the rest of the ACLs that refer to the old generalized SIDs that have fallen through the cracks are updated (check out AccessEnum from SysInternals to see where those ACLs have slipped through the cracks in Vista – there are at least a couple of places in WinSxS that mention LocalService or NetworkService for write access in my machine, and that isn’t even considering the registry or the more ephemeral kernel object namespace yet).

In Windows Server 2003, things are pretty bleak with respect to isolation between LocalService/NetworkService services. Service processes have direct access to eachother, as shown by their default security descriptors. The default security descriptor doesn’t allow direct access, but does allow one to rewrite it to grant oneself access as the owner field matches LocalService:

lkd> !process fffffadff39895c0 1
PROCESS fffffadff3990c20
[...]
    Token                             fffffa800132b9e0
[...]
lkd> !token fffffa800132b9e0
_TOKEN fffffa800132b9e0
TS Session ID: 0
User: S-1-5-19
Groups:
[...]
 07 S-1-5-5-0-44685
    Attributes - Mandatory Default Enabled LogonId
[...] 
lkd> !object fffffadff3990c20
Object: fffffadff3990c20  Type: (fffffadff4310a00) Process
    ObjectHeader: fffffadff3990bf0 (old version)
    HandleCount: 3  PointerCount: 21
lkd> dt nt!_OBJECT_HEADER fffffadff3990bf0
[...]
   +0x028 SecurityDescriptor : 0xfffffa80`011441ab 
[...]
lkd> !sd 0xfffffa80`011441a0
->Revision: 0x1
->Sbz1    : 0x0
->Control : 0x8004
            SE_DACL_PRESENT
            SE_SELF_RELATIVE
->Owner   : S-1-5-19
[...]
->Dacl    : ->Ace[0]: ->AceType: ACCESS_ALLOWED_ACE_TYPE
->Dacl    : ->Ace[0]: ->AceFlags: 0x0
->Dacl    : ->Ace[0]: ->AceSize: 0x1c
->Dacl    : ->Ace[0]: ->Mask : 0x001f0fff
->Dacl    : ->Ace[0]: ->SID: S-1-5-5-0-44685

->Dacl    : ->Ace[1]: ->AceType: ACCESS_ALLOWED_ACE_TYPE
->Dacl    : ->Ace[1]: ->AceFlags: 0x0
->Dacl    : ->Ace[1]: ->AceSize: 0x14
->Dacl    : ->Ace[1]: ->Mask : 0x00100201
->Dacl    : ->Ace[1]: ->SID: S-1-5-18

Again looking at winnt.h, we clearly see that S-1-5-19 is LocalService. So, there is absolutely no protection at all from one compromised LocalService process attacking another, at least in Windows Server 2003.

Note that if you are marked as the owner of an object, you can rewrite the DACL freely by requesting a handle with WRITE_DAC access and then modifying the DACL field with a function like SetKernelObjectSecurity. From there, all you need to do is re-request a handle with the desired access, after modifying the security descriptor to grant yourself said access. This is easy to verify experimentally by writing a test service that runs as LocalService and requesting WRITE_DAC in an OpenProcess call for another LocalService service process.

To make matters worse, nowadays most services run in shared svchost processes, which means if one process in that svchost is compromised, the whole process is a write off.

I would recommend seriously considering using dedicated unique user accounts for your services in certain scenarios as a result of this unpleasant mess. In the case where you have a security sensitive service that doesn’t need high privileges (i.e. it doesn’t require LocalSystem), it is often the wrong thing to do to just stuff it in with the rest of the LocalService or NetworkService services due to the vastly increased attack surface over running as a completely isolated user account, even if setting up a unique user account is a pain to do programmatically.

Note that although Vista attempts to mitigate this problem by ensuring that LocalService/NetworkService services cannot directly interfere with eachother in the most obvious sense of opening eachother’s processes and writing code into eachother’s address spaces, this is really only a small measure of protection due to the problem that one LocalService process’s data files are at the mercy of every other LocalService process out there. I think that it would be extremely unwise to stake your system security on there being no way to compromise one LocalService process from another in Vista, even with its mitigations; it may be slightly more difficult, but I’d hardly write it off as impossible.

Given all of this, I would steer clear of NetworkService and LocalService for sensitive but unprivileged processes (and yes, I would consider such a thing a real scenario, as you don’t need to be a computer administrator to store valuable data on a computer; you just need there to not be an untrusted (or compromised) computer administrator on the box).

One thing I am actually kind of curious about is what the SWI rationale is for even allowing the svchost paradigm by default, given how it tends to (negatively, from the perspective of system security) multiply the attack surface of all svchost’d processes. Using svchosts completely blows away the security improvements Vista makes to LocalService / NetworkService, as far as I can tell. Even though there are some services that are partitioned off in their own svchosts, there’s still one giant svchost group in Vista that has something on the order of like ~20 services in it (ugh!). Not to mention that svchosts make debugging a nightmare, but that’s a topic for another posting.

Update: Andrew Rogers pointed out that I originally posted the security descriptor for a LocalSystem process in the Windows Server 2003 example, instead of for a LocalService process. Whoops! It actually turns out that contrary to what I originally wrote, the DACL on LocalService processes on Windows Server 2003 doesn’t explicitly allow access to LocalService, but LocalService is still named as the object owner, so it is trivial to gain that access anyway, as previously mentioned (at least for Windows Server 2003).