Wednesday, May 13, 2009

Episode #35: Remotely Locking Out User While Preserving Session

Ed kicks it off:

We received a request the other day from Mr. Fordm via the Pauldotcom IRC channel. He was wondering if there was a way to lock out a user engaged in an active session on a machine. This kind of thing comes up from time to time, often during abrupt employee termination. Here's the scenario: User John Doe gets canned. He's sitting at his computer logged on in his cubicle and the IT or security staff is instructed to just get him off the machine immediately. Any delay, and there is a chance he'd launch the missiles against friendly targets or something.

The security guy suggests just remotely shutting the system down. But, no... management wants more. They want to preserve the currently logged on session so they can see if John had started to launch the missiles by typing:

C:\> wmic missiles call launch target=...*

So, how can we lock the user out while preserving the GUI session which might hold some juicy info?

First off, we want to change the user's password. Otherwise, he or she would log right back in once we lock the session. Let's assume the user is logged in via a local account, and change the password by using remote command execution via WMIC. We covered remote command execution in Episode #31, which we'll use to invoke the "net user" command to change the password:

C:\> wmic /node:[IPaddr] /user:[Admin] /password:[password] process call
create "net user [user] [NewPassword]"

You can go further, disabling the account so that no one can login with it until you re-enable it, by running:

C:\> wmic /node:[IPaddr] /user:[Admin] /password:[password] process call
create "net user [user] /active:no"

Remember, if you want to get back into this user's session later, you'll have to re-enable that user by running:

C:\> wmic /node:[IPaddr] /user:[Admin] /password:[password] process call
create "net user [user] /active:yes"

Next, we've got to lock the session. On first blush, you might think to use the following command, wrapped up inside of WMIC for remote execution:

C:\> rundll32.exe user32.dll,LockWorkStation


When executed by a local user currently logged on to a Windows box, this will lock the workstation. Nice... but... executed remotely, using WMIC as shown above, won't do the trick on most versions of Windows. You see, this command against a remote target won't be able to get access to the user's currently logged on console GUI session, so nothing happens.

You might think that we can get a little more intricate by running the logoff command against the user, again wrapped up inside of WMIC:

C:\> logoff


Nope... same problem. Works great locally, but remotely, it can't interact with that console session. And, worse... if it did work, it would eliminate the session with the juicy information we want to preserve when it logs off the user.

So, what to do? There's a great command for doing just this kind of thing: tsdiscon.

You can run it as follows:

C:\> wmic /node:[IPaddr] /user:[Admin] /password:[password] process call
create "tsdiscon"
Alternatively, the tsdiscon command has an option to run remotely:

C:\> tsdiscon console /server:[IPaddr] /v

This works like a champ on XP, locking the user at the console out, while preserving the session.

Note that tsdiscon, when run remotely, will pass through your current user's authentication credentials to the target IPaddr machine. Thus, make sure you are logged in with a user and password combination that are also in the admin group of the target machine, or that have domain admin privileges.

Unfortunately, while this works great on XP, the tsdiscon command doesn't allow you to disconnect the console session for Windows Vista or 2008 Server. I've confirmed this in my lab, and have found references to that limitation in Microsoft documentation. On Vista and 2008, you can use tsdiscon to disconnect RDP/Terminal Services sessions other than the console session (you can get a list of sessions on Vista and 2008 by running "query session" or by running "qwinsta" on XP). Sadly, I haven't found a remote command-line method for closing the console session on Vista or 2008 server while preserving that session. The rwinsta command in XP and Vista resets a session on a Vista or XP box, when used as follows:

C:\> wmic /node:[IPaddr] /user:[Admin] /password:[password] process call
create "rwinsta console"

...but you'll lose all of the current session information and running programs when rwinsta kills the session. Still, that'll let you lock out the user so he can't launch the missiles... but at the cost of losing the cmd.exe session history showing that he tried to launch them. For most purposes, that'll suffice. And, I guess it provides yet another reason to stay on XP (as if you needed any more of them).

If you know of a way to remotely disconnect a console user session on Vista using built-in command-line tools, please do send in a suggestion to suggestions@commandlinekungfu.com, and I'll gladly add it to this article.

*Current versions of Windows do not expose the missiles alias within wmic. In Windows 9 (code name: "We miss Bill"), though, it will be built-in, along with the callable method "launch". Just wait.

Hal reports from the bunker:

As Ed points out, the first trick is to lock the user's account. I'm going to assume that the system is using local password files, rather than a networked authentication database such as LDAP or Kerberos. These latter systems have their own command-line interfaces which allow you to lock user accounts, but they're outside of the scope of this blog.

So we need to SSH into the user's workstation and gain root privileges via su or sudo. Note that this assumes you have an SSH server running for remote maintenance tasks. A lot of Linux workstation builds don't automatically configure an SSH server by default. You're "Seriously Out of Luck" in these cases, and the best that you can do is try to seize the workstation before the user has a chance to launch their missiles. If you have an intelligent switch fabric, you might want to move the user's workstation onto an isolated VLAN before seizing the workstation. That way, the user might have a chance to trash their own system, but less opportunity to launch missiles at other targets.

Once you're into the system, use "passwd -l" to lock the user's account ("passwd -u" will unlock the account again, btw). Let's use Paul as our example fall guy again:


# passwd -l paul

"passwd -l" can have different effects, depending on what flavor of Unix you're using. On Linux systems, the usual practice is to introduce a "!" character at the front of the user's password hash. This renders the hash invalid so users can't log in, but it's easy to undo the change if you decide you later want to let the user into the system. Some Linux systems go further and set the "account disabled as of ..." field in the /etc/shadow file (it's the second-to-last field for each entry) to a date in the past so that even just resetting the password hash is insufficient to unlock the account.

On older, proprietary Unix systems like Solaris, "passwd -l" usually changes the user's password hash to an invalid string like "*LK*", which unfortunately loses the user's original password hash. However, at least on Solaris systems, the cron daemon will actually refuse to execute jobs for users whose password entry is "*LK*". This means clever users can't set up automated tasks to re-open access to their systems (or launch missiles). When locking accounts on Linux systems, you should also make sure to disable any cron jobs that user may have set up:

# crontab -l -u paul > /root/paul.crontab
# crontab -r -u paul

Here we're making a backup copy of Paul's crontab under /root and then removing all cron jobs. You could later restore Paul's crontab with "crontab -u paul /root/paul.crontab".

If you're worried about the user logging into the workstation remotely after you've turned on the screen locker, then you also need to be careful that the user has no "authorized_keys" files, or even ".[sr]hosts" and hosts.equiv files if you're allowing "HostBasedAuthentication":

# mkdir /root/paul-trustfiles
# mv ~paul/.ssh/authorized_keys ~paul/.[rs]hosts /etc/*hosts.equiv /root/paul-trust

OK, that should be sufficient for keeping that naughty Paul out of the machine. As far as turning on the screen locker, there are a lot of different options on different Unix systems, but let's just stick with the popular (and widely available) "xlock" program. Whatever program you choose to use, the biggest trick to remotely enabling the screen locker is to first acquire the necessary credentials to access the user's X display:

# export DISPLAY=:0.0
# cp ~paul/.Xauthority /root
# su -c paul 'xlock -mode blank -info "This workstation administratively locked"'

On the first line, we set our "DISPLAY" environment variable to match the user's display-- normally ":0.0", you can validate this with the "who" command if you're not sure. On the second line, we grab the user's "magic cookie" file, which allows us access to the X server on the specified "DISPLAY". Finally, we turn on the xlock program with just a blank, black screen. Also, the above example demonstrates that you can also specify an informational message that the user sees when they try to unlock their workstation.

Note that our example starts the xlock program as user "paul", which means the password for the "paul" account-- rendered invalid with the "passwd -l" command earlier-- will be required to unlock the screen. You could actually dispense with the "su paul -c" and start the xlock program as root, thus forcing somebody to enter the root password to unlock the screen. Of course, if Paul happens to know the root password for his workstation, this is not a good idea (you certainly don't want to lock the root account on the system)! However, another possibility would be to actually "su" to some other user account when starting up the screen locker, just to make things more difficult for Paul. But I think you're probably better off using Paul's account, since we know that user has an invalid password. To unlock the screen again, once Paul has been safely escorted out of the building, just kill the xlock process.