NTop icon indicating copy to clipboard operation
NTop copied to clipboard

Username is showing up as SYSTEM on Remote Desktop Server

Open greatquux opened this issue 4 years ago • 17 comments

For some reason, all usernames (except Administrator where I'm running ntop) show up as SYSTEM when I try to run it on a remote desktop server. The processes are there, just USER is showing as SYSTEM. Any idea why or how to fix?

image

greatquux avatar Oct 29 '20 14:10 greatquux

BTW this is ntop 0.3.4, the latest release binary. I haven't compiled the latest code, but I don't think any of the recent commits seem to relate to this.

greatquux avatar Oct 29 '20 14:10 greatquux

It seems that either GetTokenInformation or OpenProcessToken fails probably due to missing permissions. Could you try running as administrator? For now it only says "SYSTEM" because that's just the default user name I set.

gsass1 avatar Oct 29 '20 18:10 gsass1

I am running it as a local machine (and domain) Administrator, and UAC is completely disabled (EnableLUA set to 0). That's why in my screenshot I included Task Manager also to show I can see the usernames in there.

greatquux avatar Oct 29 '20 19:10 greatquux

Unfortunately I have no TS setup and there seems to be no error code shown nor any kind of logging/tracing going on. Process.UserName is set to SYSTEM (_tcsncpy_s(Process.UserName, UNLEN, _T("SYSTEM"), UNLEN);) by default before attempting to open the process (OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, Entry.th32ProcessID);).

And my knowledge of Active Directory is rather limited. The doc for LookupAccountSid declares that the first parameter (lpSystemName), if NULL, looks on the local system first, then on trusted domain controllers (relative to the local computer), and if the machine is on an untrusted domain, that the parameter should be set.

But then I have not much of an idea for a trusted computer (joined?) on a domain. I feel like NULL is the same as "." and the name of the computer, but that could be tested out with a snapshot build could be done to show the error code (e.g. e00000000) to get a better clue.

List of possible failures:

  • OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION... returned NULL
    • Seems like UsedMemory is unset, either this or GetProcessMemoryInfo failed
    • Disk metrics are unset as well, NULL handle?
  • OpenProcessToken returned FALSE
  • The first GetTokenInformation didn't set its error code to ERROR_INSUFFICIENT_BUFFER
  • The second GetTokenInformation returned FALSE
  • LookupAccountSid failed

One thing I noticed is that the handle is never closed after the OpenProcess call. Microsoft recommends closing it when done.

My best guess is probably that the processes are protected as noted in https://docs.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights#protected-processes due to the handle being NULL (Io counters are unset as well).

Next question would be, is it a local Administrator account or one from the domain, if it's joined a domain?

dd86k avatar Oct 30 '20 04:10 dd86k

The server is joined to a domain, and it's a domain Administrator account (that is a domain admin and local machine admin) that I'm logging into. Task Manager (and also Process Explorer) must be doing something different to get the user names as they show up there. I'll see if I am able to do a build myself and show any error codes.

greatquux avatar Nov 02 '20 15:11 greatquux

It looks like LookupAccountSid is getting error 122, ERROR_INSUFFICIENT_BUFFER. That's the only error code I ever see, I just put a little GetLastError !=0 check after LookupAccountSid and printed it, but I'm not advanced enough in Win32 C programming to get much farther. :( I found a forum thread https://social.msdn.microsoft.com/Forums/en-US/b0c6d946-92a7-4f52-8809-4b8f72010219/lookupaccountsid-fails-with-strange-errors-in-a-thread-that-is-impersonating-a-pipe-client?forum=windowssecurity that seems to indicate you can just reallocate the buffer to make it a bit larger and try again?

greatquux avatar Nov 04 '20 15:11 greatquux

ERROR_INSUFFICIENT_BUFFER directly from LookupAccountSid? That's surprising, since the account username is typically smaller than 256 (UNLEN) characters and the domain name is typically smaller than 260 (MAX_PATH) characters.

My main guess remains that OpenProcess returns NULL because it either has no permission or is simply incapable of processing the query for TS user processes since no other process information is filled out in your screenshot (e.g. used memory, which is processed before username, but only if it has a handle from OpenProcess).

It's possible that Task Manager (and other clones alike) use GetSecurityInfo (https://social.msdn.microsoft.com/Forums/en-US/b0c6d946-92a7-4f52-8809-4b8f72010219/lookupaccountsid-fails-with-strange-errors-in-a-thread-that-is-impersonating-a-pipe-client?forum=windowssecurity)... But then I'm not seeing the function being imported in taskmgr.exe.

Unfortunately my knowledge of Win32 is pretty limited too. I know no foss task managers

dd86k avatar Nov 04 '20 22:11 dd86k

This is how ReactOS' taskmgr does it btw: https://doxygen.reactos.org/d7/dd5/perfdata_8c_source.html

gsass1 avatar Nov 05 '20 08:11 gsass1

Weirdly, I'm getting error 122 from LookupAccountSid even on my Windows 10 computer which works and displays the user names and memory correctly, and also on the remote desktop server which doesn't. But in each case, I do see a non-NULL value for Process.Handle. And all this is taking place right after the call to LookupAccountSid that is if(OpenProcessToken()) block, which is inside if(Process.Handle) so it should have a value.

image

My print statements in that screenshot come right after the call to LookupAccountSid and before the FIXME comment: image

Where do you think I should put a ConPrintf to check on a variable?

EDIT: GitHub doesn't like when I send screenshots via email (Evolution).

greatquux avatar Nov 05 '20 13:11 greatquux

Interesting. Although seeing your snippet: I just remembered that the error code is only set when an error occurs, otherwise I think a successful call doesn't set the errorlevel for the thread (from the first GetTokenInformation). So it's normal you also see 122 on Windows with the provided snippet.

A better thing to print would be the result of LookupAccountSID (if it returns 0 then it failed).

@gsass1 That's interesting. Would you have a quick idea why the other fields (e.g. UsedMem, CPU time) are unset? I could try checking but I doubt I'd have anymore clue.

dd86k avatar Nov 05 '20 20:11 dd86k

I saved the result from LookupAccountSID in a BOOL and it always came out 1, I put an if() around it and will only print it if's not true but I haven't gotten anything out of it. Interestingly, I tried it on another RD server that is not on a domain, just workgroup with local accounts, and even there we see that it can't get CPU, memory, or username values properly. Task Manager is included to show that it has them:

image

greatquux avatar Nov 05 '20 21:11 greatquux

Looking at the ReactOS code: Curious that ReactOS does OpenProcessToken(hProcess, TOKEN_QUERY, &hProcessToken) betwen instead of TOKEN_READ as used in ntop. Want to try with TOKEN_QUERY instead? (Note: TOKEN_READ = TOKEN_QUERY | TOKEN_READ)

Otherwise curious if TokenUserStruct->User.Sid is set to NULL by the later GetTokenInformation.

dd86k avatar Nov 05 '20 22:11 dd86k

Ugh. Still a mystery. Changing to TOKEN_QUERY had no effect. User.Sid is not NULL (and it seems to have valid values when I print it out). If I change the default SYSTEM to something like "default" in most cases it does come back as "default" so it's definitely not writing anything in there.

greatquux avatar Nov 09 '20 16:11 greatquux

Checked around a bit (Well-known SIDs) and there values that have special meaning (e.g. S-1-1-0 for 'null' and S-1-5-32-555 for DOMAIN_ALIAS_RID_REMOTE_DESKTOP_USERS (thanks Microsoft)). Since it's non-null, do you mind printing/checking a few SID values as examples? From there I'll have a better clue.

The ReactOS code is nice but it does not even support logging via Active Directory (see LDAP/ActiveDirectory at Missing ReactOS Functionality) so I doubt their Task Manager would be able to translate anything. Curses.

Sorry if I'm taking too long. Tonight I'll try getting 2008 R2 up and running and try something out.

dd86k avatar Nov 09 '20 22:11 dd86k

I think I have finally figured things out. Once I got SIDs to print properly, and found I needed to use printf to properly redirect output to a file, and remembered my C pointer lessons from 25 years ago, I finally saw that the process loop was skipping over anything it couldn't get the info for on my RD server. The OpenProcess() call right after setting the default ExeName and UserName was failing but not because the Handle was NULL, because it did not have sufficient rights. On my workstation this happens to csrss.exe and the Idle process only, but on the RD server it was for everyone else.
If I run ntop.exe as SYSTEM, I can see all the usernames properly (and presumably the other stats). It looks like you need to give ntop.exe the SeDebugPrivilege privilege (or at least do it with a command line option) so that it can read stats on everything. It took a while to figure it all out, but as always, it was good to learn!

greatquux avatar Nov 10 '20 13:11 greatquux

That's particular. Maybe I should add a command-line option for SeDebugPrivilege? (Or maybe PROCESS_ALL_ACCESS)

dd86k avatar Nov 10 '20 16:11 dd86k

Maybe, but definitely experiment first on your computer to see if you are able to access the information about the csrss.exe process first. Right now I am having trouble even giving myself the SE_DEBUG_NAME privilege (even though I'm a local machine admin with UAC turned off) and I should really get back to "real work". :)

greatquux avatar Nov 10 '20 20:11 greatquux