I've written more than once about how interesting restricted tokens are – the earliest article was on Mark Edward's Windows Security web site. Unless it's been taken down recently, the article and source code are still there. In the nearly 8 years since then, I've talked about them in "Writing Secure Code", and Michael Howard has made it into a rudimentary tool to launch apps with less privileges.
A bit of history – restricted tokens were part of a somewhat failed sandboxing effort named SAFER that shipped with Windows 2000. Up until fairly recently, we haven't used them in many places – one of them is to keep the logon screen out of mischief. Restricted tokens are the core of the sandbox we shipped with MOICE, and as part of that effort, we got some lessons from the school of hard knocks, and I've learned a lot about some of the nuances of how you might use this technology. I'm documenting our techniques for a lot of reasons – first, security by obscurity isn't security. I'm quite sure there are hackers (um, security 'researchers' – whatever) out there who know as much as I do about the sandbox in MOICE, which leads me to the second reason – I'd like it if people building on Windows knew how to correctly use this technology to protect their own apps where it makes sense.
A serious caveat – one of the reasons SAFER was considered a failure is that it tried to sandbox general purpose apps on the same desktop as everything else. This isn't going to work. It can make a somewhat reasonable speedbump that will trip up malware not expecting to be run as something other than admin, but I'm interested in a serious security boundary, not a speedbump. What this implies is that an app really needs to be written with the constraints of the sandbox kept in mind.
Let's take a look at the actual API –
Now let's look at each parameter, and I'll show you how we used each one:
I found the SidsToRestrict one of the more confusing aspects of this API, but let's look at what happens if you don't use this parameter. You'll end up with a token that has the token user enabled, and whatever groups you decided to keep. Because the token user is still enabled, the process has enough rights to open a non-restricted process (or edit your profile and all your stuff), which then allows it to escalate privilege back to the original token, and all we've accomplished is a speed bump. The only way to get the original user out of the equation is by using the SidsToRestrict.
If you have SidsToRestrict, access checks all get to be a 2 pass test. The first pass is against the groups that remain enabled (and the token user). The second is against SidsToRestrict. This allows a restricted process read access to a portion of your user profile, since the ACL looks like admins:F, you:F, restricted:R – the first pass allows full control because you have access to your profile, and the 2nd pass grants read because restricted has to be there. The net permissions are the intersection of the 2 sets, which amounts to read. If a group shows up in both lists, obviously access will be granted to that group.
If you now go and create process using this token, you'll get a weird error. The problem is that the token's default DACL that I was explaining yesterday will create a process DACL that this process token doesn't grant access to, and a process that doesn't have access to itself dies a quick and horrible death, spewing an undocumented error – though CreateProcessAsuser will succeed – and it will die early enough in initialization that I was never able to debug into it. Ouch. I think we ought to mention this in the SDK.
So our next task is to define a proper default DACL for the token and call SetTokenInformation to apply a correct DACL. What I do is to make the ACL system, admins, logon ID allow full control. Once this bit of extra work has been done, you can now happily create a process that doesn't have access to much of anything – it can't access your stuff, and there won't be many places on the file system it can write to. Due to leaving users enabled, it can execute installed programs.
The hackers out there are now thinking – but there's a bunch of attack surface still available – clipboard, global atoms, and window message attacks. There is that attack surface out there, but we can deal with that – stay tuned – more blog posts to follow.