Impersonation isn’t dangerous

I was called to task because in Writing Secure Code for Windows Vista, I asserted that from the standpoint of a service, the impersonation privilege isn’t dangerous. SeImpersonate is one of the newer privileges in Windows, and has only been put there since Windows 2003 (and is in XP SP2). Basically, the attack it is trying to prevent is one where you can get a higher-level process to connect to some resource you have, impersonate them, and escalate privilege. This can happen in a number of ways:

  1. The higher-level process might be tricked into opening some arbitrary file, and while unlike UNIX, not everything is a file, a fair number of things can be opened with CreateFile – for example, \\.\pipe\mypipe is a fairly sure attack. In Office, we have a whole lot of APIs wrapped to do the right thing, so CreateFile becomes MsoCreateFile, which correctly sets the security quality of service bits to prevent impersonation.

  2. COM sources and sinks can be a nasty security hole – in this scenario, the server becomes a client when it executes the callback. If the server is running as local system and hasn’t set the security settings correctly, then the client becomes localsystem, and away we go. Since COM is really only a wrapper over RPC for the most part, the same issues also apply to RPC.

  3. It’s now possible to impersonate socket clients, so the same sort of things apply.

So it’s starting to sound as if impersonation was enemy #2 (just behind buffer overruns) – why would I say it’s benign? Here’s the deal – by default, local system, local service, and network service are allowed to impersonate callers. To get to be running as any of these user accounts, you’ve got to have a service installed by an admin (or a hideously misconfigured service ACL), and if you’re being attacked by the admin, just give up – it’s game over. The second way you get to be one of these services, is by finding some sort of ghastly flaw in the service, taking it over, and causing it to do your bidding. Sounds like a plausible attack, so let’s think about how that might work. If you’ve manged to own up a local system service, game over, and you win. Next, work on taking over the rest of the network (which you of course have permission from the owner to conduct pen testing on, or you risk serious consequences, like a new, unfriendly roommate in a very bad, long-stay facility). So let’s say you’ve managed to take over a service running as the local service account – surely you can trick something running as local system to connect to you, and do rotten things. If you can, then there’s a flaw in the local system service – the local system service should NEVER assume that any less privileged process is doing anything other than running malicious code. Whether the service running as local service has SeImpersonate or not, if the higher level process trusts them, they’re making a mistake.

But what if an unwary admin connects to a service? Sorry – same rules apply. If you’re running as admin, you’re equivalent to local system (since you can install new drivers and services). If you put trust in anything running with less privileges that’s on the system, it’s your mistake – or the person writing the software has made a mistake.

The point here is that the service that has SeImpersonate isn’t what is at risk here. The users connecting to the service are at risk, if they allow themselves to be fully impersonated.

Now let’s look at the flip side – if a service ought to be impersonating, and is not, then we have an opportunity for what’s called a luring attack. In a luring attack, one user tricks another into doing something on their behalf that ought not happen. Impersonation prevents luring attacks, because the action is being performed as the calling user. If you’re doing something on behalf of another user, _not_ impersonating is very likely to be a problem. We can’t say that impersonation is really just scary and ought not be used – then we’d have luring attacks all over the place.

Impersonation is a complex issue, and using it really correctly is something that ought to be covered much more thoroughly than what we did in either WSC2 or WSCV. Here’s the bottom line:

  • If you’re writing a high level service, or a tool typically used by admins, understand how impersonation could be mis-used to attack your user or app. Take proper measures to only allow identification.

  • If you’re writing a lower level service (in terms of privilege – I don’t mean drivers), you’re probably doing something on behalf of other users. If you are, you may need to maintain an audit trail, and you do want to avoid luring attacks. If so, impersonation is probably a good idea.

  • If you decide your service doesn’t need to impersonate anything, drop the privilege. Using least privilege never hurt anything. Even if it is just a speed-bump, and an attacker with full knowledge of everything on the system could hop over to some other process, and gain the privilege, the more complex the attack, the less likely it is to work. And sometimes the attacker doesn’t know everything and will trip the alarm system.

  • If you do use impersonation, remember that you need to maintain correct seperation between the various users. Even hopping from one user to another can be an EoP in effect – you and your boss are probably both plain users with respect to the domain controller, but if you can run code as your boss, all sorts of interesting possibilities spring to mind (including getting sacked when caught). The thing to think about here is something called “least common mechanism” – one of the Saltzer and Schroeder design principles, which basically says that the fewer resources two users have to share, the less chances for one to usurp the privileges of the other.

The whole topic is fairly complicated, so I might spend some time on it in the future. Another wrinkle worth a future post is how you manage when you can’t impersonate, but you can identify the caller. There’s a bunch of relatively new functionality that makes this problem a lot easier.


Comments (12)

  1. Andrew Royal says:

    Impersonation could be dangerous enough.

    By default Windows has at least one service (RPC) run as ‘Netwrok Service’ that accepts and impersonate LocalSystem clients. That means any other service that run as ‘Netwrok Service’ can easily elevate up to LocalSystem. And if such service (any that run as Network Service) is hacked, then an attacker gets LocalSystem.

    As a result Network Service is neraly the same as LocalSystem, due to:

    1) impersonation

    2) unreasonable defaults, configuration where RPC service would run as LocalSystem is more secure in fact. That is impersonation paradox.

    No need admin connection, you have LocalSystem already who connects and impersonated.

    Network Service is just a most obvious case. There are other conditions when impersonation is dangerous. Impersonation poses a sufficient risk, and the risk must be clearly understood/communicated  not mitigated.

  2. As I was saying, the flaw would be in the local system service for allowing a less privileged server to impersonate it. The ideal way for a service to handle such things would be to first try to impersonate, then drop to identify, and for the higher level service not to allow impersonation.

    Let’s also examine the alternative – if you confine impersonation to local system, then you have all the services that need it running with most privilege, not least.

    Another note – it’s true that having all the services running under the same 3 accounts is a problem. This offers a lot of opportunity for services to work against one another, but there’s some cool new stuff in Vista that helps a lot. More on that later, or you can read it in detail in WSCV.

    I also don’t completely agree with the analysis in the URL above, but there’s not time this morning to go into detail, except that to say again that if a localsystem service allows itself to be impersonated, it has a problem. SQL 2000 had such a problem, and IIRC, it has been fixed for quite a while.

  3. Andrew Royal says:

    OK, but what about particular example of rpcss? It is used by many privileged services and still run as Network Service account. Mircosoft actively suggest to use Network Service as a replacement for LocalSystem. Indeed it makes sense as an additional measure, but the value of this measure is misleading at least in official description. There is a note that Network Service is an ordinal unprivileged user account which is true. However, there is no any note that Network Service is used to run critical service such as rpcss. I assume it would be quite difficult to run such service “unprivileged” due to its nature. And for the rpcss it really weakened overall configuration. An alternative to run rpcss as LocalSystem is more secure because of two reasons:

    – Rpcss services many privileged requests and if an attacker managed to break into it then Network Service will not help anyway. So it doesn’t provide claimed attack surface reduction if a target is rpcss.

    – Those who follow using Network Service advice would not be vulnerable to privilege elevation to LocalSystem.

    Why not to fix this problem for rcpss?

    Regarding the SQL server, no need to agree.  You may try a demo for this provided by the advisory authors. I can confirm it works on w2k3 R2, SQL2000 SP4. The same issue is present for IIS6 as runs ‘Worker Process’ as Network Service by default.

  4. Andrew Royal says:

    David, just to clarify the problem is not in SQL/ISS etc. there is nothing to fix there. The problem is that they use Network Service which can be easily elevated up to LocalSystem. The path:

    Open rpcss process -> Break into impersonation negotiations/get tokens handles/etc. ->token handle of LocalSystem -> impersonate to LocalSyetm

    rpcss must impersonate LocalSystem and impersonation makes it useless to run as unprivileged account. In this case makes it even worth as there are other services run as this unprivileged account.

  5. You’d prefer that rpcss be running with reduced privileges, even if imperfect – it’s listening directly to the network. It is also typically doing enough stuff that trying to have a very controlled exploit that could sit around and wait until a localsystem process connected would likely be unreliable. Something else that I’m honestly not sure about is whether that service is normally doing the impersonation, or hands that off to the processes that actually serve up the RPC endpoint.

    Reviewing the services as shipped with Vista, it looks like network service is used much less often than local service, and it is used for some fairly sensitive services – for example, if you could compromise the terminal services service, you could swipe credentials without any further ado. That’s assuming that the exploit didn’t corrupt it to the point it would fail to run properly. Because of this, and the fact network service can authenticate as that system across the network, don’t write services to run as that account unless you have a need to do so.

    Stuff like this always gets fixed in stages – up until XP, we had local system or plain users, with the accompanying password managment issues. People had to do a ton of work to get things to run as network service or local service, and while this isn’t perfect, it’s a lot better than having everything running as localsystem. It also allows you to get to the next level.

    In Vista, there’s a new thing called a service SID that allows you to ACL resources directly to your service, and keeps other services from being able to access your stuff.

    Something else – it’s not nearly as easy to hop from one service to another in Vista as it was previously – check out the process ACL – the service has a unique logon ID SID, and access is granted to that logon ID and administrators. But what about the service configuration – seems that’s locked down, too. So if you could take over rpcss and do so in a way that kept it stable, you could probably eventually EoP. This is still better than the alternative, which is running as full localsystem. But what you can’t do, that you could do before is to easily hop from one network service process to another. That’s a significant level of hardening.

    The other thing to check out in Vista is the SID type configuration – this is complicated and took a couple pages to explain, so I’ll blog about that soon.

    Does this get us as far as I’d like? No – even with all the great new stuff, it isn’t perfect, only a lot better. I can say we are thinking about ways to improve this in future releases, but obviously can’t go into details, especially since it hasn’t even been coded yet.

    Anyway, thanks for the feedback – you’ve given me some more things to blog about!

  6. Andrew Royal says:

    AFAIC, the exploit is quite simple and reliable in this case, no hooks or waiting for LocalSystem are required. Rpcss always has a number of LocalSystem token handles with impersonation level = SecurityImpersonation (not just SecurityIdentification). You can check it by ProcessExplorer for example. What is required is enumeration of the handles, selecting those with LocalSystem token handles and duplicating them.

    Vista indeed fixes the issue by making unique ID as token’s owner. That is good step forward, just waiting when we get it available in server platform. BTW, would Longhorn Server have some additional measures in this area (I mean least privileges) in comparison to Vista?

    I hope, I have inspired a topic on Vista’s “least privileges” options 🙂

    Thanks, please keep posting!

  7. It’s a pretty fair bet that anything in Vista (in terms of infrastructure) will show up in later releases. What will be added is something I don’t know, and can’t really talk about now if I did know. I wouldn’t expect a whole lot – we usually use the extra time to test the server-only features. The real speculation is around what goes in the next version, since we’re in the process of figuring that out about now.

    Thanks for the good feedback!

    And BTW, that exploit is going to be a little harder to write, since the core Windows components are all ASLR, NX, and SafeSEH. Kind of brings up the question of given all that, what’s still exploitable? I’m sure some things are, but fewer than you’d have without all those countermeasures.

  8. Karthik says:

    You might want to look at to get an overview of the Service SID support in Vista.

  9. Good Point says:

    I was looking passing the correct impersonation bits to CreateFile() and ran into this:

    It says:

    When the named pipe, RPC, or DDE connection is remote, the flags passed to CreateFile to set the impersonation level are ignored. In this case, the impersonation level of the client is determined by the impersonation levels enabled by the server, which is set by a flag on the server’s account in the directory service. For example, if the server is enabled for delegation, the client’s impersonation level will also be set to delegation even if the flags passed to CreateFile specify the identification impersonation level.

    Is this correct?

    [dcl] Sure – at the end of the day, the server is going to create a token for you, and the best assumption is that the server is completely controlled by that server’s admin. You are thus at their mercy with respect to their own server. What the server is allowed to do with your identity on the rest of the network depends on what the domain lets it do.

    BTW, the advice we gave about setting the security QOS, while correct at the time, is a little outdated – on current XP and later, impersonation is a privilege, and only admins and services have it by default. In general, you don’t have to worry about impersonation attacks on systems like that. Getting tricked into opening a pipe is mostly only a problem on Win2k, which is starting to get really old (though I’m sure there are many instances running).

  10. I wrote this piece up for our group as we entered the most recent round of threat models. I’ve cleaned

  11. I wrote this piece up for our group as we entered the most recent round of threat models. I've cleaned