Windows 8, Console API, and Restricted Tokens

I discovered some undesirable behaviour with LUA tokens and the console API. On Windows 8, executing a command with sudont causes an error and the application refuses to start.

The application failed to initialize properly (0xc0000022).

The process sudont uses for starting an application is simple: create an unprivileged token and start the target application with this new token. The error code (NTSTATUS_ACCESS_DENIED) seemed to indicate permissions in the token were incorrect. But sudont works on Windows 7 without any issue.

After some searching, all I could find was this guy's blog post explaining how console applications were starting conhost.exe. But on Windows 7, conhost.exe is a child process of the Client-Server Runtime Subsystem (csrss.exe). This is significant because on Windows 7, conhost.exe runs with full privileges as NT AUTHORITY\SYSTEM.

As explained here, console applications offload UI rendering to conhost.exe for better integration with the surrounding graphical environment. Every running console application has its own instance of conhost.exe, and they likely communicate over named pipes.

When sudont creates a new token, it uses CreateRestrictedToken to strip all privileges and block the admins RID. It also reduces the integrity level to medium or lower. Calling GetTokenInformation confirms the privileges, groups, and integrity level on the token are correct. But the TokenElevation flag is still enabled, and TokenElevationType is set to TokenElevationTypeFull when I would expect it to be TokenElevationTypeLimited.

Leaving the integrity level at high didn't have any effect, nor did leaving all privileges enabled. But the console application did finally start properly if I left the admins RID enabled. This makes sense because the token lies about its actual elevation state.

My working theory is: there's a check somewhere on the token for elevation, and if the flag is set then the user is assumed to have admin rights. Something like this:

TOKEN_ELEVATION te;
DWORD len;
 
GetTokenInformation(
    htok,
    TokenElevation,
    &te,
    sizeof(TOKEN_ELEVATION),
    &len
);
 
if (te.TokenIsElevated) {
  /* herp derp... assume you're an admin even if you're not */
} else {
  /* assume you're a limited user */
}

On Windows 7, conhost.exe runs with full privileges so this check wouldn't be necessary. I tried calling SetTokenInformation but the API doesn't allow those values to be changed after the token is created. CreateRestrictedToken doesn't create a regular limited token, and it seems there's no way to fix it.

The Safer API is an alternative approach that seems to work properly. Instead of modifying an existing elevated token, SaferComputeTokenFromLevel creates a new token with the desired access privileges. Starting a console application on Windows 8 with my version of CreateRestrictedToken works as expected.